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/project-upgrades-table.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 { Map } from "immutable";
7
import { webapp_client } from "../../webapp-client";
8
import { rclass, redux, rtypes, Component } from "../../app-framework";
9
import { ErrorDisplay, UpgradeAdjustor, r_join } from "../../components";
10
import { PROJECT_UPGRADES } from "@cocalc/util/schema";
11
import { ProjectTitle } from "../../projects/project-title";
12
import { Button, Row, Col, Panel } from "../../antd-bootstrap";
13
import { ResetProjectsConfirmation } from "./reset-projects";
14
import { plural, is_string, len, round1 } from "@cocalc/util/misc";
15
16
interface reduxProps {
17
get_total_upgrades: Function;
18
help_email: string;
19
project_map: Map<string, any>;
20
get_total_upgrades_you_have_applied: Function;
21
get_upgrades_you_applied_to_project: Function;
22
get_total_project_quotas: Function;
23
get_upgrades_to_project: Function;
24
get_projects_upgraded_by: Function;
25
}
26
27
interface State {
28
show_adjustor: Map<string, boolean>; // project_id : bool
29
expand_remove_all_upgrades: boolean;
30
remove_all_upgrades_error?: string;
31
}
32
33
class ProjectUpgradesTable extends Component<reduxProps, State> {
34
static reduxProps() {
35
return {
36
account: {
37
get_total_upgrades: rtypes.func,
38
},
39
customize: {
40
help_email: rtypes.string,
41
},
42
projects: {
43
project_map: rtypes.immutable.Map,
44
get_total_upgrades_you_have_applied: rtypes.func,
45
get_upgrades_you_applied_to_project: rtypes.func,
46
get_total_project_quotas: rtypes.func,
47
get_upgrades_to_project: rtypes.func,
48
get_projects_upgraded_by: rtypes.func,
49
},
50
};
51
}
52
53
constructor(props, state) {
54
super(props, state);
55
this.state = {
56
show_adjustor: Map({}),
57
expand_remove_all_upgrades: false,
58
remove_all_upgrades_error: undefined,
59
};
60
}
61
62
open_project_settings(e, project_id: string) {
63
redux.getActions("projects").open_project({
64
project_id,
65
target: "settings",
66
switch_to: !(e.which === 2 || e.ctrlKey || e.metaKey),
67
});
68
e.preventDefault();
69
}
70
71
submit_upgrade_quotas({ project_id, new_quotas }) {
72
redux
73
.getActions("projects")
74
.apply_upgrades_to_project(project_id, new_quotas);
75
this.toggle_adjustor(project_id);
76
}
77
78
generate_on_click_adjust(project_id: string) {
79
return (e) => {
80
e.preventDefault();
81
return this.toggle_adjustor(project_id);
82
};
83
}
84
85
toggle_adjustor(project_id: string) {
86
const status = this.state.show_adjustor.get(project_id);
87
const show_adjustor = this.state.show_adjustor.set(project_id, !status);
88
this.setState({ show_adjustor });
89
}
90
91
private render_upgrades_to_project(project_id: string, upgrades) {
92
const v: JSX.Element[] = [];
93
for (let param in upgrades) {
94
const val = upgrades[param];
95
if (!val) {
96
continue;
97
}
98
const info = PROJECT_UPGRADES.params[param];
99
if (info == null) {
100
console.warn(
101
`Invalid upgrades database entry for project_id='${project_id}' -- if this problem persists, email ${this.props.help_email} with the project_id: ${param}`
102
);
103
continue;
104
}
105
const n = round1(val != null ? info.display_factor * val : 0);
106
v.push(
107
<span key={param}>
108
{info.display}: {n} {plural(n, info.display_unit)}
109
</span>
110
);
111
}
112
return r_join(v);
113
}
114
115
private render_upgrade_adjustor(project_id: string) {
116
return (
117
<UpgradeAdjustor
118
key={`adjustor-${project_id}`}
119
total_project_quotas={this.props.get_total_project_quotas(project_id)}
120
upgrades_you_can_use={this.props.get_total_upgrades()}
121
upgrades_you_applied_to_all_projects={this.props.get_total_upgrades_you_have_applied()}
122
upgrades_you_applied_to_this_project={this.props.get_upgrades_you_applied_to_project(
123
project_id
124
)}
125
quota_params={PROJECT_UPGRADES.params}
126
submit_upgrade_quotas={(new_quotas) =>
127
this.submit_upgrade_quotas({ new_quotas, project_id })
128
}
129
cancel_upgrading={() => this.toggle_adjustor(project_id)}
130
style={{ margin: "25px 0px 0px 0px" }}
131
omit_header={true}
132
/>
133
);
134
}
135
136
private render_upgraded_project(project_id: string, upgrades, darker) {
137
return (
138
<Row
139
key={project_id}
140
style={darker ? { backgroundColor: "#eee" } : undefined}
141
>
142
<Col sm={4}>
143
<ProjectTitle
144
project_id={project_id}
145
handle_click={(e) => this.open_project_settings(e, project_id)}
146
/>
147
</Col>
148
<Col sm={8}>
149
<a onClick={this.generate_on_click_adjust(project_id)} role="button">
150
{this.render_upgrades_to_project(project_id, upgrades)}
151
</a>
152
</Col>
153
{this.state.show_adjustor.get(project_id)
154
? this.render_upgrade_adjustor(project_id)
155
: undefined}
156
</Row>
157
);
158
}
159
160
private render_upgraded_projects_rows(upgraded_projects): JSX.Element[] {
161
let i = -1;
162
const result: JSX.Element[] = [];
163
for (let project_id in upgraded_projects) {
164
const upgrades = upgraded_projects[project_id];
165
i += 1;
166
result.push(
167
this.render_upgraded_project(project_id, upgrades, i % 2 === 0)
168
);
169
}
170
return result;
171
}
172
173
async confirm_reset(_e) {
174
try {
175
await webapp_client.project_client.remove_all_upgrades();
176
} catch (err) {
177
this.setState({
178
expand_remove_all_upgrades: false,
179
remove_all_upgrades_error: err?.toString(),
180
});
181
}
182
}
183
184
private render_remove_all_upgrades_error() {
185
let err: any = this.state.remove_all_upgrades_error;
186
if (!is_string(err)) {
187
err = JSON.stringify(err);
188
}
189
return (
190
<Row>
191
<Col sm={12}>
192
<ErrorDisplay
193
title={"Error removing all upgrades"}
194
error={err}
195
onClose={() =>
196
this.setState({ remove_all_upgrades_error: undefined })
197
}
198
/>
199
</Col>
200
</Row>
201
);
202
}
203
204
private render_remove_all_upgrades_conf() {
205
return (
206
<Row>
207
<Col sm={12}>
208
<ResetProjectsConfirmation
209
on_confirm={this.confirm_reset.bind(this)}
210
on_cancel={() =>
211
this.setState({ expand_remove_all_upgrades: false })
212
}
213
/>
214
</Col>
215
</Row>
216
);
217
}
218
219
private render_header() {
220
return (
221
<div>
222
<Row>
223
<Col sm={12} style={{ display: "flex" }}>
224
<div style={{ flex: "1" }}>
225
Upgrades you have applied to projects
226
</div>
227
<Button
228
bsStyle={"warning"}
229
onClick={() =>
230
this.setState({ expand_remove_all_upgrades: true })
231
}
232
disabled={this.state.expand_remove_all_upgrades}
233
>
234
Remove All Upgrades You Applied to Projects...
235
</Button>
236
</Col>
237
</Row>
238
{this.state.remove_all_upgrades_error
239
? this.render_remove_all_upgrades_error()
240
: undefined}
241
{this.state.expand_remove_all_upgrades
242
? this.render_remove_all_upgrades_conf()
243
: undefined}
244
</div>
245
);
246
}
247
248
render() {
249
const upgraded_projects = this.props.get_projects_upgraded_by();
250
if (!len(upgraded_projects)) {
251
return null;
252
}
253
return (
254
<Panel header={this.render_header()}>
255
<Row key="header">
256
<Col sm={4}>
257
<strong>Project</strong>
258
</Col>
259
<Col sm={8}>
260
<strong>
261
Upgrades you have applied to this project (click to edit)
262
</strong>
263
</Col>
264
</Row>
265
{this.render_upgraded_projects_rows(upgraded_projects)}
266
</Panel>
267
);
268
}
269
}
270
271
const tmp = rclass(ProjectUpgradesTable);
272
export { tmp as ProjectUpgradesTable };
273
274