Skip to content

Commit d51e914

Browse files
committed
add collab limit configuration option for free projects; move license config up in project config, since licenses are much more popular than PayG; make heading size consistent; get rid of legacy support link in middle of upgrade page
1 parent 1f84c73 commit d51e914

File tree

9 files changed

+91
-70
lines changed

9 files changed

+91
-70
lines changed

src/packages/frontend/collaborators/add-collaborators.tsx

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
Add collaborators to a project
88
*/
99

10-
import { Alert, Input, Select } from "antd";
10+
import { Alert, Button, Card, Input, Select } from "antd";
1111
import {
1212
React,
1313
redux,
@@ -18,7 +18,7 @@ import {
1818
useTypedRedux,
1919
useState,
2020
} from "../app-framework";
21-
import { Button, ButtonToolbar, Well } from "../antd-bootstrap";
21+
import { Well } from "../antd-bootstrap";
2222
import { A, Icon, Loading, ErrorDisplay, Gap } from "../components";
2323
import { webapp_client } from "../webapp-client";
2424
import { SITE_NAME } from "@cocalc/util/theme";
@@ -39,6 +39,9 @@ import { alert_message } from "../alerts";
3939
import { useStudentProjectFunctionality } from "@cocalc/frontend/course";
4040
import Sandbox from "./sandbox";
4141
import track from "@cocalc/frontend/user-tracking";
42+
import { SiteLicenseInput } from "@cocalc/frontend/site-licenses/input";
43+
import { applyLicense } from "@cocalc/frontend/project/settings/site-license";
44+
import { BuyLicenseForProject } from "@cocalc/frontend/site-licenses/purchase/buy-license-for-project";
4245

4346
interface RegisteredUser {
4447
sort?: string;
@@ -85,6 +88,10 @@ export const AddCollaborators: React.FC<Props> = ({
8588
where,
8689
mode = "project",
8790
}) => {
91+
const unlicensedLimit = useTypedRedux(
92+
"customize",
93+
"unlicensed_project_collaborator_limit",
94+
);
8895
const isFlyout = mode === "flyout";
8996
const student = useStudentProjectFunctionality(project_id);
9097
const user_map = useTypedRedux("users", "user_map");
@@ -119,6 +126,13 @@ export const AddCollaborators: React.FC<Props> = ({
119126
const [email_body_editing, set_email_body_editing] = useState<boolean>(false);
120127
const [invite_result, set_invite_result] = useState<string>("");
121128

129+
const hasLicense = (project?.get("site_license")?.size ?? 0) > 0;
130+
const limitExceeded =
131+
!!unlicensedLimit &&
132+
!hasLicense &&
133+
(project?.get("users").size ?? 1) + selected_entries.length >
134+
unlicensedLimit;
135+
122136
const isMountedRef = useIsMountedRef();
123137

124138
const project_actions = useActions("projects");
@@ -454,24 +468,15 @@ export const AddCollaborators: React.FC<Props> = ({
454468
/>
455469
<div
456470
style={{
457-
border: "1px solid lightgrey",
458-
padding: "10px",
459-
borderRadius: "5px",
471+
padding: "20px 0",
460472
backgroundColor: "white",
461473
marginBottom: "15px",
462474
}}
463475
>
464476
{render_email_body_error()}
465477
{render_email_textarea()}
466478
</div>
467-
<ButtonToolbar>
468-
<Button
469-
bsStyle="primary"
470-
onClick={send_email_invite}
471-
disabled={!!email_body_editing}
472-
>
473-
Send Invitation
474-
</Button>
479+
<div style={{ display: "flex" }}>
475480
<Button
476481
onClick={() => {
477482
set_email_to("");
@@ -481,7 +486,15 @@ export const AddCollaborators: React.FC<Props> = ({
481486
>
482487
Cancel
483488
</Button>
484-
</ButtonToolbar>
489+
<Gap />
490+
<Button
491+
type="primary"
492+
onClick={send_email_invite}
493+
disabled={!!email_body_editing}
494+
>
495+
Send Invitation
496+
</Button>
497+
</div>
485498
</Well>
486499
</div>
487500
);
@@ -640,14 +653,14 @@ export const AddCollaborators: React.FC<Props> = ({
640653
disabled = false;
641654
}
642655
}
643-
if (email_body_error) {
656+
if (email_body_error || limitExceeded) {
644657
disabled = true;
645658
}
646659
return (
647-
<div>
660+
<div style={{ display: "flex" }}>
648661
<Button onClick={reset}>Cancel</Button>
649662
<Gap />
650-
<Button disabled={disabled} onClick={add_selected} bsStyle="primary">
663+
<Button disabled={disabled} onClick={add_selected} type="primary">
651664
<Icon name="user-plus" /> {label}
652665
</Button>
653666
</div>
@@ -678,6 +691,30 @@ export const AddCollaborators: React.FC<Props> = ({
678691
<div
679692
style={isFlyout ? { paddingLeft: "5px", paddingRight: "5px" } : undefined}
680693
>
694+
{limitExceeded && (
695+
<Card
696+
size="small"
697+
title={
698+
<h4>
699+
<div style={{ float: "right" }}>
700+
<BuyLicenseForProject project_id={project_id} />
701+
</div>
702+
<Icon name="key" /> Select License
703+
</h4>
704+
}
705+
style={{ margin: "10px 0" }}
706+
>
707+
<SiteLicenseInput
708+
requireValid
709+
confirmLabel={"Add this license"}
710+
onChange={(license_id) => {
711+
applyLicense({ project_id, license_id });
712+
}}
713+
requireLicense
714+
requireMessage={`A license is required to have more than ${unlicensedLimit} collaborators on this project.`}
715+
/>
716+
</Card>
717+
)}
681718
{err && <ErrorDisplay error={err} onClose={() => set_err("")} />}
682719
{state == "searching" && <Loading />}
683720
{render_search()}

src/packages/frontend/compute/compute-servers-alert.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ export default function ComputeServersAlert({ project_id }) {
1717
type="success"
1818
showIcon
1919
icon={<Icon name="servers" />}
20-
message={<>Dedicated Compute Servers</>}
20+
message={<>Compute Servers</>}
2121
description={
2222
<>
23-
You can also run Jupyter notebooks, terminals, and commercial software
24-
on dedicated VM's where you have root permissions. These are charged
25-
by the second and have up to{" "}
23+
You can also easily use a dedicated server where you have full admin
24+
root permissions and nearly unlimited resources. These are charged by
25+
the second and have up to{" "}
2626
<strong>
27-
11,776GB of RAM, 416 vCPUs, 65TB of disk space, and GPUs.{" "}
27+
416 vCPUs, 65TB of disk space, 11TB of RAM and high end GPUs
28+
including 8x NVIDIA H100s.{" "}
2829
</strong>
2930
<br />
3031
Click the{" "}

src/packages/frontend/customize.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ export interface CustomizeState {
128128
nonfree_countries?: List<string>;
129129
limit_free_project_uptime: number; // minutes
130130
require_license_to_create_project?: boolean;
131+
unlicensed_project_collaborator_limit?: number;
131132
onprem_quota_heading: string;
132133
organization_email: string;
133134
organization_name: string;

src/packages/frontend/project/call-to-support.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export function CallToSupport({ onClose }: { onClose? }) {
8787
closable={onClose != null}
8888
onClose={onClose}
8989
banner
90-
type="info"
90+
type="warning"
9191
showIcon={false}
9292
message={VERSIONS.blaec}
9393
/>

src/packages/frontend/project/settings/quota-editor/admin-quotas.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ export default function AdminQuotas({ project_id, style }: Props) {
119119
style={style}
120120
title={
121121
<>
122-
<Icon name="user-plus" /> Admin Quota Editor
122+
<h4>
123+
<Icon name="user-plus" /> Admin Quota Editor
124+
</h4>
123125
<span style={{ margin: "0 15px", float: "right" }}>
124126
{editing && (
125127
<>

src/packages/frontend/project/settings/upgrade-usage.tsx

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,11 @@ import {
1919
Title,
2020
UpgradeAdjustor,
2121
} from "@cocalc/frontend/components";
22-
import { HelpEmailLink } from "@cocalc/frontend/customize";
23-
import { ShowSupportLink } from "@cocalc/frontend/support";
2422
import { ProjectsActions } from "@cocalc/frontend/todo-types";
2523
import { ROOT } from "@cocalc/util/consts/dedicated";
2624
import { KUCALC_DISABLED } from "@cocalc/util/db-schema/site-defaults";
2725
import { is_zero_map, plural, round2, to_human_list } from "@cocalc/util/misc";
2826
import { PROJECT_UPGRADES } from "@cocalc/util/schema";
29-
import { COLORS } from "@cocalc/util/theme";
3027
import {
3128
DedicatedDisk,
3229
DedicatedResources,
@@ -43,7 +40,6 @@ import PayAsYouGoQuotaEditor from "./quota-editor/pay-as-you-go";
4340
import { RunQuota } from "./run-quota";
4441
import { SiteLicense } from "./site-license";
4542
import { Project } from "./types";
46-
import { URLBox } from "./url-box";
4743

4844
const UPGRADE_BUTTON_STYLE: CSS = {
4945
paddingBottom: "15px",
@@ -75,7 +71,6 @@ export const UpgradeUsage: React.FC<Props> = React.memo(
7571
dedicated_resources,
7672
mode,
7773
}: Readonly<Props>) => {
78-
const isFlyout = mode === "flyout";
7974
const actions: ProjectsActions = useActions("projects");
8075
const project_actions = useActions({ project_id });
8176
const account_groups: List<string> =
@@ -358,40 +353,6 @@ export const UpgradeUsage: React.FC<Props> = React.memo(
358353
);
359354
}
360355

361-
function render_support(): Rendered {
362-
if (!is_commercial) return; // don't render if not commercial
363-
return (
364-
<>
365-
<hr />
366-
<Paragraph type="secondary">
367-
If you have any questions about upgrading a project, create a{" "}
368-
<ShowSupportLink />
369-
{isFlyout ? (
370-
<>
371-
, or email <HelpEmailLink /> and mention the project id:{" "}
372-
<Paragraph
373-
style={{
374-
display: "inline",
375-
color: COLORS.GRAY,
376-
fontWeight: "bold",
377-
}}
378-
copyable={{ text: project_id }}
379-
>
380-
{project_id}
381-
</Paragraph>
382-
.
383-
</>
384-
) : (
385-
<>
386-
, or email <HelpEmailLink /> and include the following URL:
387-
<URLBox />
388-
</>
389-
)}
390-
</Paragraph>
391-
</>
392-
);
393-
}
394-
395356
function render_site_license(): Rendered {
396357
// site licenses are also used in on-prem setups to tweak project quotas
397358
if (!in_kucalc) return;
@@ -421,11 +382,10 @@ export const UpgradeUsage: React.FC<Props> = React.memo(
421382
</Paragraph>
422383
{render_run_quota()}
423384
{render_upgrades_button()}
385+
{render_site_license()}
424386
{renderQuotaEditor()}
425387
{render_dedicated_disks()}
426388
{render_gpu()}
427-
{render_site_license()}
428-
{render_support()}
429389
</div>
430390
);
431391
},

src/packages/frontend/projects/create-project.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import track from "@cocalc/frontend/user-tracking";
3939

4040
const TOGGLE_STYLE: CSS = { margin: "10px 0" } as const;
4141
const TOGGLE_BUTTON_STYLE: CSS = { padding: "0" } as const;
42-
const CARD_STYLE: CSS = { marginTop: "10px", marginBottom: "10px" } as const;
42+
const CARD_STYLE: CSS = { margin: "10px 0" } as const;
4343

4444
interface Props {
4545
start_in_edit_mode?: boolean;
@@ -285,12 +285,12 @@ export const NewProjectCreator: React.FC<Props> = ({
285285
<Card
286286
size="small"
287287
title={
288-
<>
288+
<h4>
289289
<div style={{ float: "right" }}>
290290
<BuyLicenseForProject />
291291
</div>
292292
<Icon name="key" /> Select License
293-
</>
293+
</h4>
294294
}
295295
style={CARD_STYLE}
296296
>
@@ -299,6 +299,7 @@ export const NewProjectCreator: React.FC<Props> = ({
299299
confirmLabel={"Add this license"}
300300
onChange={addSiteLicense}
301301
requireLicense
302+
requireMessage={`A license is required to create additional projects.`}
302303
/>
303304
</Card>
304305
);

src/packages/frontend/site-licenses/select-license.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface Props {
3737
extraButtons?: ReactNode;
3838
requireValid?: boolean;
3939
requireLicense?: boolean;
40+
requireMessage?: ReactNode;
4041
}
4142

4243
export default function SelectLicense(props: Props) {
@@ -53,6 +54,7 @@ export default function SelectLicense(props: Props) {
5354
extraButtons,
5455
requireValid,
5556
requireLicense,
57+
requireMessage = "A license is required.",
5658
} = props;
5759
const isBlurredRef = useRef<boolean>(true);
5860
const [licenseId, setLicenseId] = useState<string>(defaultLicenseId ?? "");
@@ -214,9 +216,9 @@ export default function SelectLicense(props: Props) {
214216
{requireLicense && !licenseId ? (
215217
<Alert
216218
style={{ marginTop: "10px" }}
217-
type="info"
219+
type="warning"
218220
showIcon
219-
message={"A license is required."}
221+
message={requireMessage}
220222
description={
221223
<div>
222224
{showCall ? (

0 commit comments

Comments
 (0)