Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/course/common/copy-step-status.tsx
10976 views
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, Space, Spin } from "antd";
7
import type { SizeType } from "antd/lib/config-provider/SizeContext";
8
import type { TooltipPlacement } from "antd/lib/tooltip";
9
import { FormattedMessage, useIntl } from "react-intl";
10
11
import { Icon, Tip } from "@cocalc/frontend/components";
12
import ShowError from "@cocalc/frontend/components/error";
13
import { COPY_TIMEOUT_MS } from "@cocalc/frontend/course/consts";
14
import { labels } from "@cocalc/frontend/i18n";
15
import { webapp_client } from "@cocalc/frontend/webapp-client";
16
import { BigTime } from "./big-time";
17
import type { LastCopyInfo } from "../store";
18
19
interface CopyStepStatusProps {
20
stepLabel: string;
21
activityLabel: string;
22
data: LastCopyInfo | undefined;
23
enableCopy: boolean;
24
tips: {
25
copy: string;
26
open: string;
27
};
28
handlers: {
29
open: () => void;
30
copy: () => void;
31
stop: () => void;
32
};
33
recopy: boolean;
34
setRecopy: (value: boolean) => void;
35
omitErrors?: boolean;
36
placement: TooltipPlacement;
37
size: SizeType;
38
copyingLabel: string;
39
openTitle?: string;
40
openAriaLabel?: string;
41
showWhatHappensLink?: boolean;
42
errorContext?: string;
43
}
44
45
export function CopyStepStatus({
46
stepLabel,
47
activityLabel,
48
data,
49
enableCopy,
50
tips,
51
handlers,
52
recopy,
53
setRecopy,
54
omitErrors = false,
55
placement,
56
size,
57
copyingLabel,
58
openTitle = "Open assignment",
59
openAriaLabel = "Open assignment folder",
60
showWhatHappensLink = false,
61
errorContext = "assignment",
62
}: CopyStepStatusProps): React.JSX.Element {
63
const intl = useIntl();
64
const info = data ?? {};
65
const v: React.JSX.Element[] = [];
66
67
function render_last_time(time: string | number | Date) {
68
return (
69
<Space key="time" wrap>
70
<BigTime date={time} />
71
</Space>
72
);
73
}
74
75
function render_error(error) {
76
if (typeof error !== "string") {
77
error = `${error}`;
78
}
79
if (error.includes("[object Object]")) {
80
// already too late to know the actual error -- it got mangled/reported incorrectly
81
error = "";
82
}
83
// We search for two different error messages, since different errors happen in
84
// KuCalc versus other places cocalc runs. It depends on what is doing the copy.
85
if (
86
error.indexOf("No such file or directory") !== -1 ||
87
error.indexOf("ENOENT") != -1
88
) {
89
error = `The student might have renamed or deleted the directory that contained their ${errorContext}. Open their project and see what happened. If they renamed it, you could rename it back, then collect the ${errorContext} again -- \n${error}`;
90
} else {
91
error = `Try to ${stepLabel.toLowerCase()} again -- \n${error}`;
92
}
93
return (
94
<ShowError
95
key="error"
96
error={error}
97
style={{ padding: "4px 4px", overflowWrap: "anywhere" }}
98
/>
99
);
100
}
101
102
function render_open() {
103
return (
104
<Tip key="open" title={openTitle} tip={tips.open} placement={placement}>
105
<Button
106
onClick={handlers.open}
107
size={size}
108
icon={<Icon name="folder-open" />}
109
aria-label={openAriaLabel}
110
/>
111
</Tip>
112
);
113
}
114
115
function render_copy() {
116
return (
117
<Tip key="copy" title={stepLabel} tip={tips.copy} placement={placement}>
118
<Button
119
onClick={handlers.copy}
120
size={size}
121
icon={<Icon name="caret-right" />}
122
aria-label={`${stepLabel} this student`}
123
/>
124
</Tip>
125
);
126
}
127
128
function render_copying() {
129
return [
130
<Button key="stop" danger onClick={handlers.stop} size={size}>
131
{intl.formatMessage(labels.cancel)}
132
</Button>,
133
<Button key="copy" disabled={true} size={size}>
134
<Spin /> {copyingLabel ?? stepLabel}
135
</Button>,
136
];
137
}
138
139
function render_recopy_confirm() {
140
if (recopy) {
141
const v: React.JSX.Element[] = [];
142
v.push(
143
<Tip
144
key="copy_cancel"
145
title={intl.formatMessage(labels.cancel)}
146
tip={intl.formatMessage(labels.cancel)}
147
>
148
<Button size={size} onClick={() => setRecopy(false)}>
149
{intl.formatMessage(labels.cancel)}
150
</Button>
151
</Tip>,
152
);
153
v.push(
154
<Tip
155
key="recopy_confirm"
156
title={stepLabel}
157
placement={placement}
158
tip={tips.copy}
159
>
160
<Button
161
danger
162
size={size}
163
onClick={() => {
164
setRecopy(false);
165
handlers.copy();
166
}}
167
>
168
<FormattedMessage
169
id="course.student-assignment-info.recopy_confirm.label"
170
defaultMessage={`Yes, {activity} again`}
171
description={"Confirm an activity, like 'assign', 'collect', ..."}
172
values={{ activity: activityLabel.toLowerCase() }}
173
/>
174
</Button>
175
</Tip>,
176
);
177
if (showWhatHappensLink) {
178
v.push(
179
<div key="what-happens">
180
<a
181
target="_blank"
182
rel="noopener noreferrer"
183
href="https://doc.cocalc.com/teaching-tips_and_tricks.html#how-exactly-are-assignments-copied-to-students"
184
>
185
{intl.formatMessage({
186
id: "course.student-assignment-info.recopy.what_happens",
187
defaultMessage: "What happens when I assign again?",
188
description:
189
"Asking the question, what happens if all files are transferred to all students in an online course once again.",
190
})}
191
</a>
192
</div>,
193
);
194
}
195
return v;
196
}
197
return [
198
<Tip
199
key="copy"
200
title={stepLabel}
201
placement={placement}
202
tip={tips.copy}
203
>
204
<Button
205
size={size}
206
icon={<Icon name="redo" />}
207
onClick={() => setRecopy(true)}
208
aria-label={`Redo ${stepLabel.toLowerCase()} for this student`}
209
/>
210
</Tip>,
211
];
212
}
213
214
if (enableCopy) {
215
const now = webapp_client.server_time();
216
const in_progress = info.start != null && now - info.start < COPY_TIMEOUT_MS;
217
if (in_progress) {
218
v.push(...render_copying());
219
v.push(render_open());
220
} else if (info.time) {
221
v.push(render_open());
222
v.push(...render_recopy_confirm());
223
} else {
224
v.push(render_copy());
225
}
226
}
227
228
if (info.time) {
229
v.push(render_last_time(info.time));
230
}
231
if (info.error && !omitErrors) {
232
v.push(render_error(info.error));
233
}
234
235
return <Space wrap>{v}</Space>;
236
}
237
238