Path: blob/master/src/packages/frontend/course/common/course-unit-controls.tsx
10799 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Alert, Button, Col, Popconfirm, Row, Space } from "antd";6import { MouseEvent, ReactNode } from "react";7import { useIntl } from "react-intl";89import { DateTimePicker, Icon, Tip } from "@cocalc/frontend/components";10import { trunc_middle } from "@cocalc/util/misc";1112import { CourseActions } from "../actions";13import { ConfigurePeerGrading } from "../assignments/configure-peer";14import { ComputeServerButton } from "../compute";15import type { AssignmentRecord, HandoutRecord, Unit } from "../store";16import { useButtonSize } from "../util";17import {18deleteLabel,19dueDateMessages,20exportCollectedMessages,21fileActivityMessages,22deleteConfirmMessages,23openFolderMessages,24peerGradingMessages,25undeleteMessages,26} from "./course-unit-strings";27import type { UnitLabel } from "./course-unit-strings";28import { isAssignmentUnit } from "./course-unit-types";2930interface CourseUnitControlsCommonProps {31actions: CourseActions;32onOpenUnitPath: (e?: MouseEvent<HTMLElement>) => void;33}3435interface CourseUnitControlsAssignmentProps36extends CourseUnitControlsCommonProps {37unit: AssignmentRecord;38expandPeerConfig?: boolean;39showPeerDisabledAlert: boolean;40setShowPeerDisabledAlert: (value: boolean) => void;41}4243interface CourseUnitControlsHandoutProps extends CourseUnitControlsCommonProps {44unit: HandoutRecord;45}4647type CourseUnitControlsProps =48| CourseUnitControlsAssignmentProps49| CourseUnitControlsHandoutProps;5051export function CourseUnitControls(props: CourseUnitControlsProps) {52const intl = useIntl();53const size = useButtonSize();5455const { actions, onOpenUnitPath } = props;56let renderDue: () => ReactNode = () => null;57let renderPeerButton: () => ReactNode = () => null;58let renderPeerExtras: () => ReactNode = () => null;59let renderExportAssignment: () => ReactNode = () => null;60let unitLabel: UnitLabel;61let unitId: string;62let unitPath: string;63let unitDeleted: boolean;64let deleteUnit: () => void;65let undeleteUnit: () => void;6667function renderOpenButton() {68const { label, title, tip } = openFolderMessages(intl, unitLabel);69return (70<Tip title={title} tip={tip}>71<Button onClick={onOpenUnitPath} icon={<Icon name="folder-open" />}>72{label}73</Button>74</Tip>75);76}7778function renderExportFileUseTimes() {79const { label, title, tip } = fileActivityMessages(intl, unitLabel);80return (81<Tip title={title} tip={tip}>82<Button83onClick={() => actions.export.file_use_times(unitId)}84icon={<Icon name="clock" />}85>86{label}87</Button>88</Tip>89);90}9192function renderDeleteButton() {93if (unitDeleted) {94const { label, title, tip } = undeleteMessages(intl, unitLabel);95return (96<Tip placement="left" title={title} tip={tip}>97<Button onClick={undeleteUnit} icon={<Icon name="undo" />}>98{label}99</Button>100</Tip>101);102} else {103const { title, body } = deleteConfirmMessages(104intl,105unitLabel,106trunc_middle(unitPath, 24),107);108return (109<Popconfirm110onConfirm={deleteUnit}111title={112<div style={{ maxWidth: "400px" }}>113<b>{title}</b>114<br />115{body}116</div>117}118>119<Button icon={<Icon name="trash" />}>{deleteLabel(intl)}</Button>120</Popconfirm>121);122}123}124125if (isAssignmentUnit(props.unit)) {126const {127unit,128expandPeerConfig,129showPeerDisabledAlert,130setShowPeerDisabledAlert,131} = props as CourseUnitControlsAssignmentProps;132const peerMsg = peerGradingMessages(intl);133unitLabel = "assignment";134unitId = unit.get("assignment_id") ?? "";135unitPath = unit.get("path");136unitDeleted = unit.get("deleted");137deleteUnit = () => actions.assignments.delete_assignment(unitId);138undeleteUnit = () => actions.assignments.undelete_assignment(unitId);139140renderDue = () => {141const { label, title, tip } = dueDateMessages(intl);142return (143<Tip title={title} tip={tip}>144<span>145{label}{" "}146<DateTimePicker147value={unit.get("due_date")}148onChange={(value) => {149const due: Date | string | null | undefined =150value && typeof (value as any).toDate === "function"151? (value as any).toDate()152: value;153actions.assignments.set_due_date(unitId, due);154}}155/>156</span>157</Tip>158);159};160161renderPeerButton = () => {162const nbgraderUsed = !!unit.get("nbgrader");163const button = (164<Button165disabled={expandPeerConfig || nbgraderUsed}166onClick={() => actions.toggle_item_expansion("peer_config", unitId)}167icon={168<Icon169name={170unit.getIn(["peer_grade", "enabled"])171? "check-square-o"172: "square-o"173}174/>175}176>177{peerMsg.label}178</Button>179);180if (nbgraderUsed) {181return (182<Tip title={peerMsg.disabledTooltip}>183<span>{button}</span>184</Tip>185);186} else {187return button;188}189};190191renderExportAssignment = () => {192const { label, title, tip } = exportCollectedMessages(intl);193return (194<Tip title={title} tip={tip}>195<Button196onClick={() => actions.assignments.export_collected(unitId)}197icon={<Icon name="cloud-download" />}198>199{label}200</Button>201</Tip>202);203};204205renderPeerExtras = () => (206<>207{showPeerDisabledAlert ? (208<div style={{ marginTop: 8 }}>209<Alert210type="warning"211showIcon212closable213onClose={() => setShowPeerDisabledAlert(false)}214message={peerMsg.disabledAlert}215/>216</div>217) : null}218{expandPeerConfig ? (219<ConfigurePeerGrading actions={actions} assignment={unit} />220) : null}221</>222);223} else {224const { unit } = props as CourseUnitControlsHandoutProps;225unitLabel = "handout";226unitId = unit.get("handout_id");227unitPath = unit.get("path");228unitDeleted = unit.get("deleted");229deleteUnit = () => actions.handouts.delete_handout(unitId);230undeleteUnit = () => actions.handouts.undelete_handout(unitId);231}232return (233<Space234key="controls-stack"235direction="vertical"236size={size === "small" ? "small" : "middle"}237style={{ width: "100%" }}238>239<Row gutter={[8, 4]} align="top" justify="space-between">240<Col md={16}>241<Space wrap>242{renderOpenButton()}243{renderDue()}244{renderPeerButton()}245<ComputeServerButton246key="compute"247actions={actions}248unit={props.unit as unknown as Unit}249/>250</Space>251</Col>252<Col md={8} style={{ marginLeft: "auto" }}>253<Space wrap style={{ width: "100%", justifyContent: "flex-end" }}>254{renderExportFileUseTimes()}255{renderExportAssignment()}256{renderDeleteButton()}257</Space>258</Col>259</Row>260{renderPeerExtras()}261</Space>262);263}264265266