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/upgrades/upgrades-page.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 { Col, Panel, Row } from "@cocalc/frontend/antd-bootstrap";
7
import {
8
Component,
9
rclass,
10
redux,
11
rtypes,
12
} from "@cocalc/frontend/app-framework";
13
import { A, Icon, Loading, Gap } from "@cocalc/frontend/components";
14
import { appBasePath } from "@cocalc/frontend/customize/app-base-path";
15
import { plural, round1 } from "@cocalc/util/misc";
16
import { PROJECT_UPGRADES } from "@cocalc/util/schema";
17
import { Progress } from "antd";
18
import { Map } from "immutable";
19
import { join } from "path";
20
import { Footer, PolicyPricingPageUrl, SiteName } from "../../customize";
21
import "./project-upgrades-table";
22
import { ProjectUpgradesTable } from "./project-upgrades-table";
23
export { tmp as UpgradesPage };
24
declare var DEBUG: boolean;
25
26
interface reduxProps {
27
stripe_customer?: Map<string, any>;
28
project_map?: Map<string, any>;
29
all_projects_have_been_loaded?: boolean;
30
}
31
32
class UpgradesPage extends Component<reduxProps> {
33
static reduxProps() {
34
return {
35
projects: {
36
project_map: rtypes.immutable.Map,
37
all_projects_have_been_loaded: rtypes.bool,
38
},
39
account: {
40
stripe_customer: rtypes.immutable.Map,
41
},
42
};
43
}
44
45
private render_no_upgrades(): JSX.Element {
46
return (
47
<div>
48
<h3>Upgrades are no longer available</h3>
49
Please visit <A href={join(appBasePath, "store")}>
50
the new store
51
</A>, explore <A href={join(appBasePath, "pricing")}>our products</A>, or{" "}
52
<A href={join(appBasePath, "billing/subscriptions")}>
53
view your legacy upgrade subscriptions
54
</A>
55
.
56
<Footer />
57
</div>
58
);
59
}
60
61
private render_have_upgrades(): JSX.Element {
62
return (
63
<div style={{ margin: "10px 0" }}>
64
<h3>
65
Thank you for supporting <SiteName />
66
</h3>
67
<div style={{ color: "#666" }}>
68
<p>
69
You have some now deprecated "quota upgrades". They are listed
70
below, along with how you have applied them to projects. You can
71
adjust your project upgrade contribution from the settings page in
72
any project.
73
</p>
74
<p>
75
Going forward, we offer many{" "}
76
<A href={PolicyPricingPageUrl}> pricing and subscription options</A>
77
, which you can subscribe to in the{" "}
78
<A href={join(appBasePath, "store")}>Store</A>.
79
</p>
80
</div>
81
<Gap />
82
</div>
83
);
84
}
85
86
private render_upgrade(param, amount, used, darker): JSX.Element {
87
const info = PROJECT_UPGRADES.params[param];
88
const n = round1(amount != null ? info.display_factor * amount : 0);
89
let u = round1(used != null ? info.display_factor * used : 0);
90
if (u > n) {
91
u = n;
92
}
93
const percent_used = Math.round((u / n) * 100);
94
return (
95
<Row key={param} style={darker ? { backgroundColor: "#eee" } : undefined}>
96
<Col sm={2}>{info.display}</Col>
97
<Col sm={3}>
98
<Row>
99
<Col sm={5}>
100
{u != null ? (
101
<span>
102
{u} {plural(u, info.display_unit)}
103
</span>
104
) : undefined}
105
</Col>
106
<Col sm={7}>
107
<Progress percent={percent_used} />
108
</Col>
109
</Row>
110
</Col>
111
<Col sm={2}>
112
{n != null ? (
113
<span>
114
{n} {plural(n, info.display_unit)}
115
</span>
116
) : undefined}
117
</Col>
118
<Col sm={5} style={{ color: "#666" }}>
119
{info.desc}
120
</Col>
121
</Row>
122
);
123
}
124
125
private render_upgrade_rows(upgrades, used): JSX.Element[] {
126
let i = 1;
127
const result: JSX.Element[] = [];
128
for (let prop of PROJECT_UPGRADES.field_order) {
129
const amount = upgrades[prop];
130
i += 1;
131
result.push(this.render_upgrade(prop, amount, used[prop], i % 2 === 0));
132
}
133
return result;
134
}
135
136
private render_upgrades(): JSX.Element {
137
const upgrades = redux.getStore("account").get_total_upgrades();
138
const used = redux
139
.getStore("projects")
140
.get_total_upgrades_you_have_applied();
141
if (upgrades == null || used == null) {
142
return this.render_no_upgrades();
143
}
144
145
// Ensure that all projects loaded -- this can change used above, which is fine,
146
// and would re-render this component. The issue is that it's conceivable you have
147
// a project nobody has touched for a month, which has upgrades applied to it.
148
redux.getActions("projects").load_all_projects();
149
150
return (
151
<Panel
152
header={
153
<span>
154
<Icon name="tachometer-alt" /> Upgrades from your subscriptions and
155
course packages
156
</span>
157
}
158
>
159
<Row key="header">
160
<Col sm={2}>
161
<strong>Quota</strong>
162
</Col>
163
<Col sm={3}>
164
<strong>Used</strong>
165
</Col>
166
<Col sm={2}>
167
<strong>Purchased</strong>
168
</Col>
169
<Col sm={5}>
170
<strong>Description</strong>
171
</Col>
172
</Row>
173
{this.render_upgrade_rows(upgrades, used)}
174
</Panel>
175
);
176
}
177
178
public render(): JSX.Element {
179
if (this.props.project_map == null) {
180
return <Loading theme={"medium"} />;
181
}
182
if (!this.props.all_projects_have_been_loaded) {
183
// See https://github.com/sagemathinc/cocalc/issues/3802
184
redux.getActions("projects").load_all_projects();
185
return <Loading theme={"medium"} />;
186
}
187
if (
188
!DEBUG &&
189
!this.props.stripe_customer?.getIn(["subscriptions", "total_count"])
190
) {
191
return this.render_no_upgrades();
192
} else {
193
return (
194
<div>
195
{this.render_have_upgrades()}
196
{this.render_upgrades()}
197
<ProjectUpgradesTable />
198
</div>
199
);
200
}
201
}
202
}
203
204
const tmp = rclass(UpgradesPage);
205
206