Skip to content

tweak new project/software env selection #8349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions src/packages/frontend/project/settings/compute-image-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Row,
Space,
Spin,
Switch,
} from "antd";
import { SizeType } from "antd/es/config-provider/SizeContext";
import { fromJS } from "immutable";
Expand All @@ -45,6 +46,7 @@ import { CancelText } from "@cocalc/frontend/i18n/components";
import { capitalize, unreachable } from "@cocalc/util/misc";
import { COLORS } from "@cocalc/util/theme";
import { SOFTWARE_ENVIRONMENT_ICON } from "./software-consts";
import { ComputeImages } from "../../custom-software/init";

type MenuItem = Required<MenuProps>["items"][number];

Expand All @@ -68,7 +70,7 @@ const img_sorter = (a, b): number => {

interface ComputeImageSelectorProps {
current_image: string;
layout: "horizontal" | "compact" | "dialog";
layout: "horizontal" | "compact" | "dialog" | "dropdown";
onSelect: (img: string) => void;
disabled?: boolean;
size?: SizeType;
Expand All @@ -91,6 +93,7 @@ export function ComputeImageSelector({
const disabled = propsDisabled ?? false;
const size = propsSize ?? "small";
const label = propsLabel ?? capitalize(intl.formatMessage(labels.select));
const [dropdownCustom, setDropdownCustom] = useState(false);

// initialize with the given default
const [nextImg, setNextImg] = useState<string>(current_image);
Expand All @@ -107,6 +110,11 @@ export function ComputeImageSelector({
"software",
);

const images: ComputeImages | undefined = useTypedRedux(
"compute_images",
"images",
);

if (software_envs === undefined) {
return <Loading />;
}
Expand Down Expand Up @@ -160,9 +168,11 @@ export function ComputeImageSelector({
}

function render_menu_group(group: string): MenuItem {
const children = render_menu_children(group);
if (children.length === 0) return null;
return {
key: group,
children: render_menu_children(group),
children,
label: group,
type: "group",
};
Expand All @@ -174,7 +184,12 @@ export function ComputeImageSelector({

function getMenu() {
return {
onClick: (e) => (layout === "dialog" ? setNextImg : onSelect)(e.key),
onClick: (e) => {
setNextImg(e.key);
if (layout !== "dialog") {
onSelect(e.key);
}
},
style: { maxHeight: "50vh", overflow: "auto" },
items: menu_items(),
};
Expand All @@ -190,6 +205,22 @@ export function ComputeImageSelector({
);
}

function render_dropdown_custom() {
if (!isCoCalcCom) return null;
return (
<Space style={{ marginLeft: "20px" }}>
<Switch
value={dropdownCustom}
onChange={setDropdownCustom}
title={"Switch to select a custom software environment"}
checkedChildren={"Custom"}
unCheckedChildren={"Standard"}
/>
<HelpIcon title="Software Environment">help</HelpIcon>
</Space>
);
}

function render_doubt() {
return (
<span style={{ color: COLORS.GRAY, fontSize: "11pt" }}>
Expand Down Expand Up @@ -364,7 +395,6 @@ export function ComputeImageSelector({
case "compact":
return render_selector();
case "horizontal":
// used in projects → create new project
return (
<Row gutter={[10, 10]}>
<Col xs={24}>
Expand All @@ -378,6 +408,23 @@ export function ComputeImageSelector({
</Col>
</Row>
);
// used in projects → create new project
case "dropdown":
return (
<Row gutter={[10, 10]}>
<Col xs={24}>
<Icon name={SOFTWARE_ENVIRONMENT_ICON} />
<Gap />
{render_selector()}
<Gap />
{render_dropdown_custom()}
</Col>
<Col xs={24}>
<Paragraph>{render_info(nextImg)}</Paragraph>
<pre>{JSON.stringify(images?.toJS(), null, 2)}</pre>
</Col>
</Row>
);
// successor of "vertical", where there is a dialog with a clear indication to click a button
case "dialog":
return (
Expand Down
152 changes: 77 additions & 75 deletions src/packages/frontend/projects/create-project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Create a new project
import { Button, Card, Col, Form, Input, Row, Space } from "antd";
import { delay } from "awaiting";
import { useIntl } from "react-intl";

import { Alert, Well } from "@cocalc/frontend/antd-bootstrap";
import {
CSS,
Expand All @@ -29,9 +30,11 @@ import {
SoftwareEnvironmentState,
} from "@cocalc/frontend/custom-software/selector";
import { labels } from "@cocalc/frontend/i18n";
import { ComputeImageSelector } from "@cocalc/frontend/project/settings/compute-image-selector";
import { SiteLicenseInput } from "@cocalc/frontend/site-licenses/input";
import { BuyLicenseForProject } from "@cocalc/frontend/site-licenses/purchase/buy-license-for-project";
import track from "@cocalc/frontend/user-tracking";
import { DEFAULT_COMPUTE_IMAGE } from "@cocalc/util/db-schema";
import {
KUCALC_COCALC_COM,
KUCALC_ON_PREMISES,
Expand Down Expand Up @@ -61,7 +64,6 @@ export const NewProjectCreator: React.FC<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 [title_prefill, set_title_prefill] = useState<boolean>(false);
const [license_id, set_license_id] = useState<string>("");
const [custom_software, set_custom_software] =
Expand All @@ -82,6 +84,15 @@ export const NewProjectCreator: React.FC<Props> = ({
[customize_kucalc],
);

const customize_software = useTypedRedux("customize", "software");
const [default_software_img, software_images] = useMemo(
() => [
customize_software.get("default"),
customize_software.get("environments"),
],
[customize_software],
);

const [form] = Form.useForm();

useEffect(() => {
Expand Down Expand Up @@ -112,7 +123,6 @@ export const NewProjectCreator: React.FC<Props> = ({
set_title_text(default_value ?? "");
set_error("");
set_custom_software({});
set_show_advanced(false);
set_show_add_license(requireLicense);
set_title_prefill(true);
set_license_id("");
Expand Down Expand Up @@ -267,15 +277,33 @@ export const NewProjectCreator: React.FC<Props> = ({
set_custom_software(obj);
}

function render_advanced() {
if (!show_advanced) return;
function render_customize_software_env() {
return (
<Card size="small" title="Software environment" style={CARD_STYLE}>
<SoftwareEnvironment
onChange={custom_software_on_change}
showTitle={false}
/>
</Card>
<>
<Form.Item label="Software environment">
<ComputeImageSelector
current_image={default_software_img ?? DEFAULT_COMPUTE_IMAGE}
layout={"dropdown"}
onSelect={(img) => {
const display = software_images.get(img)?.get("title");
custom_software_on_change({
image_selected: img,
title_text: display,
image_type: "standard",
});
}}
changing={false}
label={"set"}
/>
</Form.Item>

<Card size="small" title="Software environment" style={CARD_STYLE}>
<SoftwareEnvironment
onChange={custom_software_on_change}
showTitle={false}
/>
</Card>
</>
);
}

Expand All @@ -284,63 +312,44 @@ export const NewProjectCreator: React.FC<Props> = ({
}

function render_add_license() {
if (!show_add_license) return;
return (
<Card
size="small"
title={
<h4>
<div style={{ float: "right" }}>
<BuyLicenseForProject />
</div>
<Icon name="key" /> Select License
</h4>
}
style={CARD_STYLE}
>
<SiteLicenseInput
requireValid
confirmLabel={"Add this license"}
onChange={addSiteLicense}
requireLicense
requireMessage={`A license is required to create additional projects.`}
/>
</Card>
);
}

function render_advanced_toggle(): JSX.Element | undefined {
// we only support custom images on cocalc.com and onprem
if (!show) return;
if (show_advanced) return;
return (
<div style={TOGGLE_STYLE}>
<Button
onClick={() => set_show_advanced(true)}
type="link"
style={TOGGLE_BUTTON_STYLE}
>
<Icon name="plus" /> Customize the software environment...
</Button>
</div>
);
}

function render_add_license_toggle(): JSX.Element | undefined {
if (!show) return;
if (show_add_license) return;
return (
<div style={TOGGLE_STYLE}>
<Button
disabled={requireLicense}
onClick={() => set_show_add_license(true)}
type="link"
style={TOGGLE_BUTTON_STYLE}
if (!show_add_license) {
return (
<div style={TOGGLE_STYLE}>
<Button
disabled={requireLicense}
onClick={() => set_show_add_license(true)}
type="link"
style={TOGGLE_BUTTON_STYLE}
>
<Icon name="plus" /> Add a license key...
</Button>
</div>
);
} else {
return (
<Card
size="small"
title={
<h4>
<div style={{ float: "right" }}>
<BuyLicenseForProject />
</div>
<Icon name="key" /> Select License
</h4>
}
style={CARD_STYLE}
>
<Icon name="plus" /> Add a license key...
</Button>
</div>
);
<SiteLicenseInput
requireValid
confirmLabel={"Add this license"}
onChange={addSiteLicense}
requireLicense
requireMessage={`A license is required to create additional projects.`}
/>
</Card>
);
}
}

function render_license() {
Expand Down Expand Up @@ -381,6 +390,7 @@ export const NewProjectCreator: React.FC<Props> = ({
message: helpTxt,
},
]}
help={"You can change the title at any time."}
>
<Input
ref={new_project_title_ref}
Expand All @@ -392,9 +402,6 @@ export const NewProjectCreator: React.FC<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" }}>
Expand All @@ -412,18 +419,13 @@ export const NewProjectCreator: React.FC<Props> = ({
</div>
</Col>
</Row>
{render_add_license_toggle()}
{render_customize_software_env()}
{render_add_license()}
{render_license()}
{render_advanced_toggle()}
{render_advanced()}
<Row>
<Col sm={24} style={{ marginTop: "10px" }}>
<Space>
<Button
disabled={state === "saving"}
onClick={cancel_editing}
>
<Button disabled={state === "saving"} onClick={cancel_editing}>
{intl.formatMessage(labels.cancel)}
</Button>
<Button
Expand Down
2 changes: 1 addition & 1 deletion src/packages/frontend/site-licenses/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* License: MS-RSL – see LICENSE.md for details
*/

// Inputing a site license, e.g., for a project, course, etc.
// Inputting a site license, e.g., for a project, course, etc.

import {
redux,
Expand Down
2 changes: 1 addition & 1 deletion src/packages/frontend/site-licenses/select-license.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
Select a license or enter a license code.

Component takes as input data that describes a licens.
Component takes as input data that describes a license.

IMPORTANT: this component must work in *both* from nextjs and static.
*/
Expand Down
Loading