Skip to content

Commit b4e0a9c

Browse files
committed
course: add assignments menu item
1 parent 0cf3d61 commit b4e0a9c

File tree

7 files changed

+107
-68
lines changed

7 files changed

+107
-68
lines changed

src/packages/frontend/course/assignments/assignments-panel.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { cmp_array } from "@cocalc/util/misc";
1919
import { Alert, Col, Row } from "antd";
2020
import { Map, Set } from "immutable";
2121
import { CourseActions } from "../actions";
22-
import { FoldersToolbar } from "../common";
22+
import { AddItems, FoldersToolbar } from "../common/folders-tool-bar";
2323
import {
2424
AssignmentRecord,
2525
IsGradingMap,
@@ -306,3 +306,24 @@ export const AssignmentsPanel: React.FC<Props> = React.memo((props: Props) => {
306306
</div>
307307
);
308308
});
309+
310+
// used for adding assignments outside of the above component.
311+
export function AddAssignments({ name, actions }) {
312+
const assignments = useRedux(name, "assignments");
313+
return (
314+
<AddItems
315+
itemName="assignment"
316+
items={assignments}
317+
addItems={actions.assignments.addAssignment}
318+
selectorStyle={{
319+
position: null,
320+
width: "100%",
321+
boxShadow: null,
322+
zIndex: null,
323+
backgroundColor: null,
324+
}}
325+
defaultOpen
326+
closable={false}
327+
/>
328+
);
329+
}

src/packages/frontend/course/common/folders-tool-bar.tsx

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,48 @@ export const FoldersToolbar: React.FC<FoldersToolbarProps> = (
4545
plural_item_name = "item",
4646
} = props;
4747

48-
function submit_selected(path_list) {
49-
if (path_list != null) {
50-
// If nothing is selected and the user clicks the button to "Add handout (etc)" then
51-
// path_list is undefined, hence don't do
52-
// (NOTE: I'm also going to make it so that button is disabled, which fits our
53-
// UI guidelines, so there's two reasons that path_list is defined here.)
54-
add_folders(path_list);
55-
}
56-
}
48+
return (
49+
<Space>
50+
<SearchInput
51+
placeholder={`Find ${plural_item_name}...`}
52+
default_value={propsSearch}
53+
on_change={search_change}
54+
style={SEARCH_STYLE}
55+
/>
56+
{num_omitted ? (
57+
<h5
58+
style={{
59+
textAlign: "center",
60+
color: COLORS.GRAY_L,
61+
marginTop: "5px",
62+
}}
63+
>
64+
(Omitting {num_omitted}{" "}
65+
{num_omitted > 1 ? plural_item_name : item_name})
66+
</h5>
67+
) : undefined}
68+
<AddItems addItems={add_folders} itemName={item_name} items={items} />
69+
</Space>
70+
);
71+
};
5772

58-
// Omit any -collect directory (unless explicitly searched for).
59-
// Omit any currently assigned directory or subdirectories.
73+
export function AddItems({
74+
addItems,
75+
itemName,
76+
items,
77+
defaultOpen,
78+
selectorStyle,
79+
closable = true,
80+
}: {
81+
addItems;
82+
itemName: string;
83+
items;
84+
defaultOpen?;
85+
selectorStyle?;
86+
closable?;
87+
}) {
88+
// Omits any -collect directory (unless explicitly searched for).
89+
// Omits any currently assigned directory or subdirectories.
6090
const pathsToOmit = useMemo(() => {
6191
const omit: Set<string> = new Set([]);
6292
items
@@ -75,7 +105,9 @@ export const FoldersToolbar: React.FC<FoldersToolbarProps> = (
75105
const isExcluded = useCallback(
76106
(path) => {
77107
if (!path) return true;
78-
if (path.includes("-collect")) return true;
108+
if (path.includes("-collect")) {
109+
return true;
110+
}
79111
if (pathsToOmit.has(path)) {
80112
return true;
81113
}
@@ -91,30 +123,17 @@ export const FoldersToolbar: React.FC<FoldersToolbarProps> = (
91123
);
92124

93125
return (
94-
<Space>
95-
<SearchInput
96-
placeholder={`Find ${plural_item_name}...`}
97-
default_value={propsSearch}
98-
on_change={search_change}
99-
style={SEARCH_STYLE}
100-
/>
101-
{num_omitted ? (
102-
<h5
103-
style={{
104-
textAlign: "center",
105-
color: COLORS.GRAY_L,
106-
marginTop: "5px",
107-
}}
108-
>
109-
(Omitting {num_omitted}{" "}
110-
{num_omitted > 1 ? plural_item_name : item_name})
111-
</h5>
112-
) : undefined}
113-
<MultipleAddSearch
114-
isExcluded={isExcluded}
115-
addSelected={submit_selected}
116-
itemName={item_name}
117-
/>
118-
</Space>
126+
<MultipleAddSearch
127+
isExcluded={isExcluded}
128+
addSelected={(paths) => {
129+
if (paths != null) {
130+
addItems(paths);
131+
}
132+
}}
133+
itemName={itemName}
134+
defaultOpen={defaultOpen}
135+
selectorStyle={selectorStyle}
136+
closable={closable}
137+
/>
119138
);
120-
};
139+
}

src/packages/frontend/course/common/multiple-add-search.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ interface MultipleAddSearchProps {
1414
itemName: string;
1515
err?: string;
1616
isExcluded: (path: string) => boolean;
17+
defaultOpen?;
18+
selectorStyle?;
19+
closable: boolean;
1720
}
1821

1922
// Multiple result selector
@@ -23,8 +26,11 @@ export function MultipleAddSearch({
2326
addSelected,
2427
itemName = "result",
2528
isExcluded,
29+
defaultOpen,
30+
selectorStyle,
31+
closable,
2632
}: MultipleAddSearchProps) {
27-
const [selecting, setSelecting] = useState<boolean>(false);
33+
const [selecting, setSelecting] = useState<boolean>(defaultOpen);
2834
const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set([]));
2935

3036
function clear() {
@@ -44,12 +50,14 @@ export function MultipleAddSearch({
4450
{selecting && (
4551
<DirectorySelector
4652
multi
53+
closable={closable}
4754
style={{
4855
width: "500px",
4956
margin: "10px 0",
5057
position: "absolute",
5158
zIndex: 1,
5259
boxShadow: "8px 8px 4px #888",
60+
...selectorStyle,
5361
}}
5462
title={`Select one or more ${itemName} directories`}
5563
onMultiSelect={setSelectedItems}

src/packages/frontend/course/modals.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from "@cocalc/frontend/course/configuration/configuration-panel";
2929
import { Parallel } from "@cocalc/frontend/course/configuration/parallel";
3030
import { Nbgrader } from "@cocalc/frontend/course/configuration/nbgrader";
31+
import { AddAssignments } from "@cocalc/frontend/course/assignments/assignments-panel";
3132

3233
interface Props {
3334
frameActions;
@@ -87,6 +88,8 @@ function getModal(modal: string) {
8788
switch (modal) {
8889
case "add-students":
8990
return { Body: AddStudents, title: "Add Students", icon: "users" };
91+
case "add-assignments":
92+
return { Body: AddAssignments, title: "Add Assignemtns", icon: "share-square" };
9093

9194
case "start-all-projects":
9295
return {

src/packages/frontend/frame-editors/course-editor/editor.ts

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const COURSE_MENUS = {
4040
label: menu.edit,
4141
pos: 1,
4242
entries: {
43-
editStudents: ["course-add-student"],
43+
editStudents: ["course-add-students", "course-add-assignments"],
4444
courseUpgrades: ["course-upgrades"],
4545
configCourse: [
4646
"course-title-and-description",
@@ -86,18 +86,26 @@ const COURSE_MENUS = {
8686
};
8787

8888
const COMMANDS = {
89-
"course-add-student": {
89+
"course-add-students": {
9090
icon: "users",
9191
label: "Add Students",
9292
button: "+Student",
9393
title: "Add one or more students to this course.",
9494
onClick: ({ props }) => {
95-
// const { id, actions } = props;
96-
// actions.set_frame_type(id, "course_students");
9795
const { actions } = props;
9896
actions.setModal("add-students");
9997
},
10098
},
99+
"course-add-assignments": {
100+
icon: "share-square",
101+
label: "Add Assignments",
102+
button: "+Assignment",
103+
title: "Add one or more assignments to this course.",
104+
onClick: ({ props }) => {
105+
const { actions } = props;
106+
actions.setModal("add-assignments");
107+
},
108+
},
101109
"course-title-and-description": {
102110
icon: "header",
103111
label: "Course Title and Description",
@@ -201,8 +209,6 @@ const COMMANDS = {
201209
title:
202210
"You can start all projects associated with this course so they are immediately ready for your students to use.",
203211
onClick: ({ props }) => {
204-
// const { id, actions } = props;
205-
// actions.set_frame_type(id, "course_actions");
206212
const { actions } = props;
207213
actions.setModal("start-all-projects");
208214
},
@@ -214,8 +220,6 @@ const COMMANDS = {
214220
title:
215221
"Run a bash terminal command in the home directory of all student projects. Up to 30 commands run in parallel, with a timeout of 1 minutes.",
216222
onClick: ({ props }) => {
217-
// const { id, actions } = props;
218-
// actions.set_frame_type(id, "course_actions");
219223
const { actions } = props;
220224
actions.setModal("terminal-command");
221225
},
@@ -226,8 +230,6 @@ const COMMANDS = {
226230
button: "Reconfigure",
227231
title: "Update all projects with correct students, descriptions, etc.",
228232
onClick: ({ props }) => {
229-
// const { id, actions } = props;
230-
// actions.set_frame_type(id, "course_actions");
231233
const { actions } = props;
232234
actions.setModal("reconfigure-all-projects");
233235
},
@@ -239,8 +241,6 @@ const COMMANDS = {
239241
title:
240242
"Export all the grades you have recorded for students in your course to a csv or Python file.",
241243
onClick: ({ props }) => {
242-
// const { id, actions } = props;
243-
// actions.set_frame_type(id, "course_actions");
244244
const { actions } = props;
245245
actions.setModal("export-grades");
246246
},
@@ -252,8 +252,6 @@ const COMMANDS = {
252252
title:
253253
"Send another email to every student who didn't sign up yet. This sends a maximum of one email every 1 day.",
254254
onClick: ({ props }) => {
255-
// const { id, actions } = props;
256-
// actions.set_frame_type(id, "course_actions");
257255
const { actions } = props;
258256
actions.setModal("resend-invites");
259257
},
@@ -265,8 +263,6 @@ const COMMANDS = {
265263
title:
266264
"If you add new students to your course, you can ensure they have all the assignments and handouts that you have already assigned to other students in the course.",
267265
onClick: ({ props }) => {
268-
// const { id, actions } = props;
269-
// actions.set_frame_type(id, "course_actions");
270266
const { actions } = props;
271267
actions.setModal("copy-missing-handouts-and-assignments");
272268
},
@@ -278,8 +274,6 @@ const COMMANDS = {
278274
title:
279275
"Empty trash by purging deleted students, assignments, and handouts.",
280276
onClick: ({ props }) => {
281-
// const { id, actions } = props;
282-
// actions.set_frame_type(id, "course_actions");
283277
const { actions } = props;
284278
actions.setModal("empty-trash");
285279
},
@@ -291,8 +285,6 @@ const COMMANDS = {
291285
title:
292286
"If for some reason you would like to delete all the student projects created for this course, you may do so by clicking above.",
293287
onClick: ({ props }) => {
294-
// const { id, actions } = props;
295-
// actions.set_frame_type(id, "course_actions");
296288
const { actions } = props;
297289
actions.setModal("delete-student-projects");
298290
},
@@ -304,8 +296,6 @@ const COMMANDS = {
304296
title:
305297
"Student projects will not be deleted. If you make a mistake, students can still be undeleted from the Student tab or using TimeTravel.",
306298
onClick: ({ props }) => {
307-
// const { id, actions } = props;
308-
// actions.set_frame_type(id, "course_actions");
309299
const { actions } = props;
310300
actions.setModal("delete-students");
311301
},
@@ -317,8 +307,6 @@ const COMMANDS = {
317307
title:
318308
"Student projects will not be deleted. If you make a mistake, students can still be undeleted from the Student tab or using TimeTravel.",
319309
onClick: ({ props }) => {
320-
// const { id, actions } = props;
321-
// actions.set_frame_type(id, "course_shared_project");
322310
const { actions } = props;
323311
actions.setModal("delete-shared-project");
324312
},
@@ -330,8 +318,6 @@ const COMMANDS = {
330318
title:
331319
"Create a single common shared project, which everybody -- students and all collaborators on this project (your TAs and other instructors) -- have write access to.",
332320
onClick: ({ props }) => {
333-
// const { id, actions } = props;
334-
// actions.set_frame_type(id, "course_shared_project");
335321
const { actions } = props;
336322
actions.setModal("create-shared-project");
337323
},

src/packages/frontend/project/directory-selector.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ interface Props {
4949
showHidden?: boolean;
5050
title?: ReactNode;
5151
multi?: boolean; // if true enables multiple select
52+
closable?: boolean;
5253
}
5354

5455
export default function DirectorySelector({
@@ -64,6 +65,7 @@ export default function DirectorySelector({
6465
showHidden: defaultShowHidden,
6566
title,
6667
multi,
68+
closable = true,
6769
}: Props) {
6870
const frameContext = useFrameContext(); // optionally used to define project_id and startingPath, when in a frame
6971
if (project_id == null) project_id = frameContext.project_id;
@@ -196,7 +198,7 @@ export default function DirectorySelector({
196198
<Card
197199
title={
198200
<>
199-
{onClose != null && (
201+
{closable && onClose != null && (
200202
<Icon
201203
name="times"
202204
style={{ float: "right", cursor: "pointer", marginTop: "5px" }}

src/packages/static/src/crash.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export default function Crash() {
6565
Sorry to interrupt your work. An error happened in CoCalc's web
6666
application.
6767
<br />
68-
Don't worry, all your files are securely stored on its servers!
68+
Don't worry, your CoCalc files are securely stored on our servers!
6969
</div>
7070

7171
<hr />

0 commit comments

Comments
 (0)