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/course/configuration/actions-panel.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 { Button, Card, Col, Row, Space } from "antd";
7
import { FormattedMessage, useIntl } from "react-intl";
8
9
import { useActions, useStore } from "@cocalc/frontend/app-framework";
10
import { Icon, Paragraph } from "@cocalc/frontend/components";
11
import { course } from "@cocalc/frontend/i18n";
12
import type { ProjectMap } from "@cocalc/frontend/todo-types";
13
import { RESEND_INVITE_INTERVAL_DAYS } from "@cocalc/util/consts/invites";
14
import { CourseActions } from "../actions";
15
import { CourseStore } from "../store";
16
import { DeleteAllStudentProjects } from "./delete-all-student-projects";
17
import { DeleteAllStudents } from "./delete-all-students";
18
import EmptyTrash from "./empty-trash";
19
import { StudentProjectsStartStopPanel } from "./start-stop-panel";
20
import { TerminalCommandPanel } from "./terminal-command";
21
22
interface Props {
23
name: string;
24
project_map: ProjectMap;
25
configuring_projects?: boolean;
26
reinviting_students?: boolean;
27
}
28
29
export function ActionsPanel({
30
name,
31
project_map,
32
configuring_projects,
33
reinviting_students,
34
}: Props) {
35
const actions = useActions<CourseActions>({ name });
36
37
return (
38
<div className="smc-vfill" style={{ overflowY: "scroll" }}>
39
<Row>
40
<Col md={12} style={{ padding: "15px 15px 15px 0" }}>
41
<StartAllProjects name={name} project_map={project_map} />
42
<br />
43
<ReconfigureAllProjects
44
configuring_projects={configuring_projects}
45
actions={actions}
46
/>
47
<br />
48
<TerminalCommandPanel name={name} />
49
<br />
50
<ExportGrades actions={actions} />
51
</Col>
52
<Col md={12} style={{ padding: "15px" }}>
53
<ResendInvites
54
actions={actions}
55
reinviting_students={reinviting_students}
56
/>
57
<br />
58
<CopyMissingHandoutsAndAssignments actions={actions} />
59
<br />
60
<EmptyTrash />
61
<br />
62
<DeleteAllStudentProjects actions={actions} />
63
<br />
64
<DeleteAllStudents actions={actions} />
65
</Col>
66
</Row>
67
</div>
68
);
69
}
70
71
export function StartAllProjects({ name, project_map }) {
72
const store = useStore<CourseStore>({ name });
73
const r = store.num_running_projects(project_map);
74
const n = store.num_students();
75
return (
76
<StudentProjectsStartStopPanel
77
name={name}
78
num_running_projects={r}
79
num_students={n}
80
/>
81
);
82
}
83
84
export function ExportGrades({ actions, close }: { actions; close? }) {
85
const intl = useIntl();
86
87
async function save_grades_to_csv() {
88
await actions.export.to_csv();
89
close?.();
90
}
91
92
async function save_grades_to_py() {
93
await actions.export.to_py();
94
close?.();
95
}
96
97
async function save_grades_to_json() {
98
await actions.export.to_json();
99
close?.();
100
}
101
102
return (
103
<Card
104
title={
105
<>
106
<Icon name="table" /> {intl.formatMessage(course.export_grades)}
107
</>
108
}
109
>
110
<Paragraph style={{ marginBottom: "10px" }}>
111
<FormattedMessage
112
id="course.actions-panel.export-grades.title"
113
defaultMessage="Save grades to..."
114
/>
115
</Paragraph>
116
<Space>
117
<Button onClick={save_grades_to_csv}>
118
<Icon name="csv" /> CSV file...
119
</Button>
120
<Button onClick={save_grades_to_json}>
121
<Icon name="file-code" /> JSON file...
122
</Button>
123
<Button onClick={save_grades_to_py}>
124
<Icon name="file-code" /> Python file...
125
</Button>
126
</Space>
127
<hr />
128
<Paragraph type="secondary">
129
<FormattedMessage
130
id="course.actions-panel.export-grades.info"
131
defaultMessage={`Export all the grades you have recorded for students in your course
132
to a CSV or Python file.
133
{br}
134
In Microsoft Excel, you can <A>import the CSV file</A>.`}
135
values={{
136
A: (c) => (
137
<a
138
target="_blank"
139
href="https://support.microsoft.com/en-us/office/import-or-export-text-txt-or-csv-files-5250ac4c-663c-47ce-937b-339e391393ba?ui=en-us&rs=en-us&ad=us"
140
>
141
{c}
142
</a>
143
),
144
br: <br />,
145
}}
146
/>
147
</Paragraph>
148
</Card>
149
);
150
}
151
152
export function ReconfigureAllProjects({
153
actions,
154
configuring_projects,
155
}: {
156
actions;
157
configuring_projects?: boolean;
158
}) {
159
const intl = useIntl();
160
161
return (
162
<Card
163
title={
164
<>
165
<Icon name="envelope" />{" "}
166
{intl.formatMessage(course.reconfigure_all_projects)}
167
</>
168
}
169
>
170
<FormattedMessage
171
id="course.actions-panel.reconfigure-all-projects.info"
172
defaultMessage={`Ensure all projects have the correct students and TA's,
173
titles and descriptions set, etc.
174
This will also resend any outstanding email invitations.`}
175
/>
176
<hr />
177
<Button
178
disabled={configuring_projects}
179
onClick={() => {
180
actions.configuration.configure_all_projects();
181
}}
182
>
183
{configuring_projects ? <Icon name="cocalc-ring" spin /> : undefined}{" "}
184
{intl.formatMessage(course.reconfigure_all_projects)}
185
</Button>
186
</Card>
187
);
188
}
189
190
export function ResendInvites({
191
actions,
192
reinviting_students,
193
}: {
194
actions;
195
reinviting_students?;
196
}) {
197
const intl = useIntl();
198
199
return (
200
<Card
201
title={
202
<>
203
<Icon name="envelope" /> {intl.formatMessage(course.resend_invites)}
204
</>
205
}
206
>
207
<FormattedMessage
208
id="course.actions-panel.resend-invite.info"
209
defaultMessage={`Send another email to every student who didn't sign up yet.
210
This sends a maximum of one email every {days}
211
{days, plural, one {day} other {days}}.`}
212
values={{ days: RESEND_INVITE_INTERVAL_DAYS }}
213
/>
214
<hr />
215
<Button
216
disabled={reinviting_students}
217
onClick={() => {
218
actions.student_projects.reinvite_oustanding_students();
219
}}
220
>
221
{reinviting_students ? <Icon name="cocalc-ring" spin /> : undefined}{" "}
222
<FormattedMessage
223
id="course.actions-panel.resend-invite.button"
224
defaultMessage={"Reinvite students"}
225
description={"Resending email invitiatons to students in a course."}
226
/>
227
</Button>
228
</Card>
229
);
230
}
231
232
export function CopyMissingHandoutsAndAssignments({ actions }) {
233
const intl = useIntl();
234
return (
235
<Card
236
title={
237
<>
238
<Icon name="share-square" />{" "}
239
{intl.formatMessage(course.copy_missing_handouts_assignments)}
240
</>
241
}
242
>
243
<FormattedMessage
244
id="course.actions-panel.copy-missing-handouts-assignments"
245
defaultMessage={`If you <b>add new students</b> to your course,
246
you can click this button to ensure they have all the assignments and handouts
247
that you have already assigned to other students in the course.`}
248
/>
249
<hr />
250
<Button
251
onClick={() => {
252
actions.configuration.push_missing_handouts_and_assignments();
253
}}
254
>
255
<Icon name="share-square" />{" "}
256
{intl.formatMessage(course.copy_missing_handouts_assignments)}
257
</Button>
258
</Card>
259
);
260
}
261
262