Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into delete-project-data
Browse files Browse the repository at this point in the history
  • Loading branch information
haraldschilly committed Jul 12, 2024
2 parents 39d0065 + 72b6ecf commit 2429b15
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 50 deletions.
4 changes: 2 additions & 2 deletions src/packages/frontend/compute/onprem-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export default function OnPremCloudConfiguration({
if (!editable || !project_id) {
return (
<div>
On Prem {configuration.arch == "arm64" ? "ARM64" : "x86_64"} Linux VM
OnPrem {configuration.arch == "arm64" ? "ARM64" : "x86_64"} Linux VM
{configuration.gpu ? " that has an NVIDIA GPU" : ""}.
</div>
);
Expand All @@ -91,7 +91,7 @@ export default function OnPremCloudConfiguration({
<div style={{ marginBottom: "30px" }}>
<div style={{ color: "#666", marginBottom: "15px" }}>
<div style={{ color: "#666", marginBottom: "5px" }}>
<b>On Prem Compute Server</b>
<b>OnPrem Compute Server</b>
</div>
You can connect your own <b>Ubuntu 22.04 Virtual Machine</b> to this
CoCalc project and seamlessly run Jupyter notebooks and terminals on it.
Expand Down
1 change: 1 addition & 0 deletions src/packages/frontend/customize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export interface CustomizeState {
max_upgrades: TypedMap<Partial<Upgrades>>;
nonfree_countries?: List<string>;
limit_free_project_uptime: number; // minutes
require_license_to_create_project?: boolean;
onprem_quota_heading: string;
organization_email: string;
organization_name: string;
Expand Down
3 changes: 3 additions & 0 deletions src/packages/frontend/projects/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ export class ProjectsActions extends Actions<ProjectsState> {
image?: string; // if given, sets the compute image (the ID string)
start?: boolean; // immediately start on create
noPool?: boolean; // never use the pool
license?: string;
}): Promise<string> {
const image = await redux.getStore("customize").getDefaultComputeImage();

Expand All @@ -356,12 +357,14 @@ export class ProjectsActions extends Actions<ProjectsState> {
image?: string;
start: boolean;
noPool?: boolean;
license?: string;
} = defaults(opts, {
title: "No Title",
description: "No Description",
image,
start: false,
noPool: undefined,
license: undefined,
});
if (!opts2.image) {
// make falseish same as not specified.
Expand Down
96 changes: 64 additions & 32 deletions src/packages/frontend/projects/create-project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Create a new project

import { Button, Card, Col, Form, Input, Row } from "antd";
import { delay } from "awaiting";

import { BuyLicenseForProject } from "@cocalc/frontend/site-licenses/purchase/buy-license-for-project";
import { Alert, Well } from "@cocalc/frontend/antd-bootstrap";
import {
CSS,
Expand Down Expand Up @@ -48,9 +48,10 @@ interface Props {

type EditState = "edit" | "view" | "saving";

export const NewProjectCreator: React.FC<Props> = (props: Props) => {
const { start_in_edit_mode, default_value } = props;

export const NewProjectCreator: React.FC<Props> = ({
start_in_edit_mode,
default_value,
}: Props) => {
const managed_licenses = useTypedRedux("billing", "managed_licenses");

// view --> edit --> saving --> view
Expand All @@ -60,7 +61,6 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
const [title_text, set_title_text] = useState<string>(default_value ?? "");
const [error, set_error] = useState<string>("");
const [show_advanced, set_show_advanced] = useState<boolean>(false);
const [show_add_license, set_show_add_license] = useState<boolean>(false);
const [title_prefill, set_title_prefill] = useState<boolean>(false);
const [license_id, set_license_id] = useState<string>("");
const [warnBoost, setWarnBoost] = useState<boolean>(false);
Expand All @@ -72,6 +72,12 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {

const is_anonymous = useTypedRedux("account", "is_anonymous");
const customize_kucalc = useTypedRedux("customize", "kucalc");
const requireLicense = !!useTypedRedux(
"customize",
"require_license_to_create_project",
);
const [show_add_license, set_show_add_license] =
useState<boolean>(requireLicense);

// onprem and cocalc.com use licenses to adjust quota configs – but only cocalc.com has custom software images
const show = useMemo(
Expand Down Expand Up @@ -110,7 +116,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
set_error("");
set_custom_software({});
set_show_advanced(false);
set_show_add_license(false);
set_show_add_license(requireLicense);
set_title_prefill(true);
set_license_id("");
}
Expand All @@ -131,6 +137,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
title: title_text,
image: await derive_project_img_name(custom_software),
start: true, // used to not start, due to apply_default_upgrades, but upgrades are deprecated
license: license_id,
};
try {
project_id = await actions.create_project(opts);
Expand All @@ -140,9 +147,6 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
set_error(`Error creating project -- ${err}`);
return;
}
if (isValidUUID(license_id)) {
await actions.add_site_license_to_project(project_id, license_id);
}
track("create-project", {
how: "projects-page",
project_id,
Expand Down Expand Up @@ -224,10 +228,13 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
);
}

function create_disabled() {
function isDisabled() {
if (requireLicense && !license_id) {
return true;
}
return (
// no name of new project
title_text === "" ||
!title_text?.trim() ||
// currently saving (?)
state === "saving" ||
// user wants a non-default image, but hasn't selected one yet
Expand Down Expand Up @@ -283,8 +290,20 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
function render_add_license() {
if (!show_add_license) return;
return (
<Card size="small" title="Select license" style={CARD_STYLE}>
<Card
size="small"
title={
<>
<div style={{ float: "right" }}>
<BuyLicenseForProject size="small" />
</div>
<Icon name="key" /> Select License
</>
}
style={CARD_STYLE}
>
<SiteLicenseInput
requireValid
confirmLabel={"Add this license"}
onChange={addSiteLicense}
/>
Expand Down Expand Up @@ -322,6 +341,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
return (
<div style={TOGGLE_STYLE}>
<Button
disabled={requireLicense}
onClick={() => set_show_add_license(true)}
type="link"
style={TOGGLE_BUTTON_STYLE}
Expand All @@ -345,7 +365,7 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
>
add/remove licenses
</A>{" "}
in the project settings later on.
in project settings later.
</div>
);
}
Expand Down Expand Up @@ -381,36 +401,48 @@ export const NewProjectCreator: React.FC<Props> = (props: Props) => {
/>
</Form.Item>
</Form>
<div style={{ color: COLORS.GRAY, float: "right" }}>
You can change the title at any time.
</div>
</Col>
<Col sm={12}>
<div style={{ color: COLORS.GRAY, marginLeft: "30px" }}>
A <A href="https://doc.cocalc.com/project.html">project</A> is an
isolated private computational workspace that you can share with
others. You can easily change the project's title at any time in
project settings.
A <A href="https://doc.cocalc.com/project.html">project</A> is a
private computational workspace that you can use with
collaborators that you explicitly invite. You can attach powerful{" "}
<A href="https://doc.cocalc.com/compute_server.html">
GPUs, CPUs
</A>{" "}
and{" "}
<A href="https://doc.cocalc.com/cloud_file_system.html">
storage
</A>{" "}
to a project.
</div>
</Col>
</Row>
{render_advanced_toggle()}
{render_advanced()}
{render_add_license_toggle()}
{render_add_license()}
{render_license()}
{render_advanced_toggle()}
{render_advanced()}
<Row>
<Col sm={24} style={{ marginTop: "10px" }}>
<Button.Group>
<Button disabled={state === "saving"} onClick={cancel_editing}>
Cancel
</Button>
<Button
disabled={create_disabled()}
onClick={() => create_project()}
type="primary"
>
Create Project
{create_disabled() ? " (enter a title above!)" : ""}
</Button>
</Button.Group>
<Button
disabled={state === "saving"}
onClick={cancel_editing}
style={{ marginRight: "8px" }}
>
Cancel
</Button>
<Button
disabled={isDisabled()}
onClick={() => create_project()}
type="primary"
>
Create Project
{requireLicense && !license_id && <> (select license above)</>}
</Button>
</Col>
</Row>
<Row>
Expand Down
3 changes: 3 additions & 0 deletions src/packages/frontend/site-licenses/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface Props {
style?: CSS;
extra?: React.ReactNode;
extraButtons?: React.ReactNode;
requireValid?: boolean;
}

export const SiteLicenseInput: React.FC<Props> = (props: Props) => {
Expand All @@ -46,6 +47,7 @@ export const SiteLicenseInput: React.FC<Props> = (props: Props) => {
confirmLabel = "Apply License",
extra,
extraButtons,
requireValid,
} = props;

const managedLicenses = useManagedLicenses();
Expand All @@ -63,6 +65,7 @@ export const SiteLicenseInput: React.FC<Props> = (props: Props) => {
style={style}
extra={extra}
extraButtons={extraButtons}
requireValid={requireValid}
/>
);
};
40 changes: 24 additions & 16 deletions src/packages/frontend/site-licenses/select-license.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ interface Props {
style?: CSS;
extra?: ReactNode; // plain-text node is ok
extraButtons?: ReactNode;
requireValid?: boolean;
}

export default function SelectLicense(props: Props) {
Expand All @@ -49,6 +50,7 @@ export default function SelectLicense(props: Props) {
style,
extra,
extraButtons,
requireValid,
} = props;
const isBlurredRef = useRef<boolean>(true);
const [licenseId, setLicenseId] = useState<string>(defaultLicenseId ?? "");
Expand Down Expand Up @@ -154,24 +156,30 @@ export default function SelectLicense(props: Props) {
style={{ width: "100%", ...style }}
>
<div>
{(showAll || licenseIds.length < len(managedLicenses)) && (
<Checkbox
style={{
flex: "1 0 0",
margin: "5px 0",
color: COLORS.GRAY_M,
whiteSpace: "nowrap",
}}
checked={showAll}
onChange={() => setShowAll(!showAll)}
>
Show expired
</Checkbox>
)}{" "}
{!requireValid &&
(showAll || licenseIds.length < len(managedLicenses)) && (
<Checkbox
style={{
flex: "1 0 0",
margin: "5px 0",
color: COLORS.GRAY_M,
whiteSpace: "nowrap",
}}
checked={showAll}
onChange={() => setShowAll(!showAll)}
>
Show expired
</Checkbox>
)}{" "}
<Select
style={{ width: "100%", flex: "1 1 0", marginRight: "10px" }}
style={{
width: "100%",
height: licenseId ? "100px" : undefined,
flex: "1 1 0",
marginRight: "10px",
}}
placeholder={
"Enter license code " +
`Enter${requireValid ? " valid " : " "}license code ` +
(options.length > 0
? `or select from the ${options.length} licenses you manage`
: "")
Expand Down
9 changes: 9 additions & 0 deletions src/packages/util/db-schema/site-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export type SiteSettingsKeys =
| "max_trial_projects"
| "nonfree_countries"
| "limit_free_project_uptime"
| "require_license_to_create_project"
| "google_analytics"
| "kucalc"
| "dns"
Expand Down Expand Up @@ -576,6 +577,14 @@ export const site_settings_conf: SiteSettings = {
show: only_cocalc_com,
to_display: (val) => `${val} minutes`,
},
require_license_to_create_project: {
name: "Require License to Create Project",
desc: "If yes the 'New Project' creation form on the projects page requires the user to enter a valid license. This has no other impact and only impacts the frontend UI. Users can circumvent this via the API or a course.",
default: "no",
valid: only_booleans,
show: only_cocalc_com,
to_val: to_bool,
},
datastore: {
name: "Datastore",
desc: `Show the '${DATASTORE_TITLE}' panel in the project settings`,
Expand Down

0 comments on commit 2429b15

Please sign in to comment.