Path: blob/master/src/packages/frontend/course/common/copy-step-status.tsx
10976 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, Space, Spin } from "antd";6import type { SizeType } from "antd/lib/config-provider/SizeContext";7import type { TooltipPlacement } from "antd/lib/tooltip";8import { FormattedMessage, useIntl } from "react-intl";910import { Icon, Tip } from "@cocalc/frontend/components";11import ShowError from "@cocalc/frontend/components/error";12import { COPY_TIMEOUT_MS } from "@cocalc/frontend/course/consts";13import { labels } from "@cocalc/frontend/i18n";14import { webapp_client } from "@cocalc/frontend/webapp-client";15import { BigTime } from "./big-time";16import type { LastCopyInfo } from "../store";1718interface CopyStepStatusProps {19stepLabel: string;20activityLabel: string;21data: LastCopyInfo | undefined;22enableCopy: boolean;23tips: {24copy: string;25open: string;26};27handlers: {28open: () => void;29copy: () => void;30stop: () => void;31};32recopy: boolean;33setRecopy: (value: boolean) => void;34omitErrors?: boolean;35placement: TooltipPlacement;36size: SizeType;37copyingLabel: string;38openTitle?: string;39openAriaLabel?: string;40showWhatHappensLink?: boolean;41errorContext?: string;42}4344export function CopyStepStatus({45stepLabel,46activityLabel,47data,48enableCopy,49tips,50handlers,51recopy,52setRecopy,53omitErrors = false,54placement,55size,56copyingLabel,57openTitle = "Open assignment",58openAriaLabel = "Open assignment folder",59showWhatHappensLink = false,60errorContext = "assignment",61}: CopyStepStatusProps): React.JSX.Element {62const intl = useIntl();63const info = data ?? {};64const v: React.JSX.Element[] = [];6566function render_last_time(time: string | number | Date) {67return (68<Space key="time" wrap>69<BigTime date={time} />70</Space>71);72}7374function render_error(error) {75if (typeof error !== "string") {76error = `${error}`;77}78if (error.includes("[object Object]")) {79// already too late to know the actual error -- it got mangled/reported incorrectly80error = "";81}82// We search for two different error messages, since different errors happen in83// KuCalc versus other places cocalc runs. It depends on what is doing the copy.84if (85error.indexOf("No such file or directory") !== -1 ||86error.indexOf("ENOENT") != -187) {88error = `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}`;89} else {90error = `Try to ${stepLabel.toLowerCase()} again -- \n${error}`;91}92return (93<ShowError94key="error"95error={error}96style={{ padding: "4px 4px", overflowWrap: "anywhere" }}97/>98);99}100101function render_open() {102return (103<Tip key="open" title={openTitle} tip={tips.open} placement={placement}>104<Button105onClick={handlers.open}106size={size}107icon={<Icon name="folder-open" />}108aria-label={openAriaLabel}109/>110</Tip>111);112}113114function render_copy() {115return (116<Tip key="copy" title={stepLabel} tip={tips.copy} placement={placement}>117<Button118onClick={handlers.copy}119size={size}120icon={<Icon name="caret-right" />}121aria-label={`${stepLabel} this student`}122/>123</Tip>124);125}126127function render_copying() {128return [129<Button key="stop" danger onClick={handlers.stop} size={size}>130{intl.formatMessage(labels.cancel)}131</Button>,132<Button key="copy" disabled={true} size={size}>133<Spin /> {copyingLabel ?? stepLabel}134</Button>,135];136}137138function render_recopy_confirm() {139if (recopy) {140const v: React.JSX.Element[] = [];141v.push(142<Tip143key="copy_cancel"144title={intl.formatMessage(labels.cancel)}145tip={intl.formatMessage(labels.cancel)}146>147<Button size={size} onClick={() => setRecopy(false)}>148{intl.formatMessage(labels.cancel)}149</Button>150</Tip>,151);152v.push(153<Tip154key="recopy_confirm"155title={stepLabel}156placement={placement}157tip={tips.copy}158>159<Button160danger161size={size}162onClick={() => {163setRecopy(false);164handlers.copy();165}}166>167<FormattedMessage168id="course.student-assignment-info.recopy_confirm.label"169defaultMessage={`Yes, {activity} again`}170description={"Confirm an activity, like 'assign', 'collect', ..."}171values={{ activity: activityLabel.toLowerCase() }}172/>173</Button>174</Tip>,175);176if (showWhatHappensLink) {177v.push(178<div key="what-happens">179<a180target="_blank"181rel="noopener noreferrer"182href="https://doc.cocalc.com/teaching-tips_and_tricks.html#how-exactly-are-assignments-copied-to-students"183>184{intl.formatMessage({185id: "course.student-assignment-info.recopy.what_happens",186defaultMessage: "What happens when I assign again?",187description:188"Asking the question, what happens if all files are transferred to all students in an online course once again.",189})}190</a>191</div>,192);193}194return v;195}196return [197<Tip198key="copy"199title={stepLabel}200placement={placement}201tip={tips.copy}202>203<Button204size={size}205icon={<Icon name="redo" />}206onClick={() => setRecopy(true)}207aria-label={`Redo ${stepLabel.toLowerCase()} for this student`}208/>209</Tip>,210];211}212213if (enableCopy) {214const now = webapp_client.server_time();215const in_progress = info.start != null && now - info.start < COPY_TIMEOUT_MS;216if (in_progress) {217v.push(...render_copying());218v.push(render_open());219} else if (info.time) {220v.push(render_open());221v.push(...render_recopy_confirm());222} else {223v.push(render_copy());224}225}226227if (info.time) {228v.push(render_last_time(info.time));229}230if (info.error && !omitErrors) {231v.push(render_error(info.error));232}233234return <Space wrap>{v}</Space>;235}236237238