CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/account/licenses/managed-licenses.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Alert, Checkbox, Spin } from "antd";
7
import { load_target } from "@cocalc/frontend/history";
8
import {
9
CSS,
10
React,
11
useActions,
12
useEffect,
13
useIsMountedRef,
14
useMemo,
15
useState,
16
useTypedRedux,
17
} from "@cocalc/frontend/app-framework";
18
import { ErrorDisplay, Title } from "@cocalc/frontend/components";
19
import { SiteLicensePublicInfoTable } from "@cocalc/frontend/site-licenses/site-license-public-info";
20
import type { SiteLicenses } from "@cocalc/frontend/site-licenses/types";
21
import { plural } from "@cocalc/util/misc";
22
23
export const LICENSES_STYLE: CSS = {
24
margin: "30px 0",
25
padding: "0",
26
} as const;
27
28
export const ManagedLicenses: React.FC = () => {
29
const [error, setError] = useState<string | undefined>();
30
const [loading, setLoading] = useState<boolean>(true);
31
const [show_all, set_show_all] = useState<boolean>(false);
32
const actions = useActions("billing");
33
const is_mounted_ref = useIsMountedRef();
34
35
const active_licenses = useTypedRedux("billing", "managed_license_ids"); // currently or recently valid
36
const all_licenses = useTypedRedux("billing", "all_managed_license_ids");
37
const licenses = useMemo(
38
() => (show_all ? all_licenses : active_licenses),
39
[active_licenses, all_licenses, show_all],
40
);
41
42
async function reload() {
43
setLoading(true);
44
try {
45
await actions.update_managed_licenses();
46
} catch (err) {
47
if (!is_mounted_ref.current) return;
48
setError(err.toString());
49
} finally {
50
if (!is_mounted_ref.current) return;
51
setLoading(false);
52
}
53
}
54
55
// When we first mount the component or when error gets cleared,
56
// we try to load the managed licenses:
57
useEffect(() => {
58
if (error) return; // do nothing when error is set.
59
reload();
60
}, [error]);
61
62
function render_error() {
63
if (!error) return;
64
return (
65
<ErrorDisplay banner error={error} onClose={() => setError(undefined)} />
66
);
67
}
68
69
function render_managed() {
70
if (error) return;
71
if (licenses == null) {
72
return <Spin />;
73
}
74
if (licenses.size == 0) {
75
return <div>You are not the manager of any licenses yet.</div>;
76
}
77
78
const site_licenses: SiteLicenses = licenses.toJS().reduce((acc, v) => {
79
acc[v] = null; // we have no info about them yet
80
return acc;
81
}, {});
82
83
return (
84
<div style={LICENSES_STYLE}>
85
<SiteLicensePublicInfoTable site_licenses={site_licenses} />
86
</div>
87
);
88
}
89
90
function render_count() {
91
if (licenses != null && licenses.size > 0) {
92
return <>({licenses.size})</>;
93
}
94
}
95
96
function render_show_all() {
97
if (
98
licenses == null ||
99
all_licenses == null ||
100
active_licenses == null ||
101
all_licenses.size == active_licenses.size
102
) {
103
// don't show if not loaded or not useful
104
return;
105
}
106
const n = all_licenses.size - licenses.size;
107
return (
108
<Checkbox
109
style={{ marginRight: "15px", fontWeight: 450 }}
110
checked={show_all}
111
onChange={() => set_show_all(!show_all)}
112
>
113
{n == 0
114
? "Showing all"
115
: `Show all (${n} older expired ${plural(n, "license")} omitted)`}
116
</Checkbox>
117
);
118
}
119
120
return (
121
<>
122
<Title level={3}>
123
Licenses You Manage {render_count()}
124
<div style={{ float: "right" }}>{render_show_all()}</div>
125
{loading && <Spin />}
126
</Title>
127
<CancelSubscriptionBanner />
128
{render_error()}
129
{render_managed()}
130
</>
131
);
132
};
133
134
// TODO: obviously this should only be shown if the user *has* a subscription!
135
function CancelSubscriptionBanner() {
136
return (
137
<Alert
138
banner
139
type="info"
140
message={
141
<>
142
To cancel a subscription,{" "}
143
<a
144
onClick={() => {
145
load_target("settings/subscriptions");
146
}}
147
>
148
visit the Subscription tab above
149
</a>
150
. To edit a license <i>that you purchased</i> expand the license
151
below, then click on the "Edit License..." button. To apply a license
152
to a project, select the project under Projects below.
153
</>
154
}
155
/>
156
);
157
}
158
159