@capytale/meta-player 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/App.tsx +1 -1
- package/src/MetaPlayer.tsx +29 -2
- package/src/features/activityData/activityDataSlice.ts +47 -12
- package/src/features/activityJS/ActivityJSProvider.tsx +15 -14
- package/src/features/activityJS/Saver.tsx +34 -24
- package/src/features/layout/layoutSlice.ts +16 -2
- package/src/features/navbar/CapytaleMenu.tsx +93 -79
- package/src/features/pedago/InstructionsEditor.tsx +50 -33
- package/src/features/pedago/PdfEditor.tsx +91 -0
- package/src/features/pedago/PedagoCommands.tsx +325 -0
- package/src/features/pedago/SharedNotesEditor.tsx +145 -0
- package/src/features/pedago/index.tsx +19 -74
- package/src/features/pedago/style.module.scss +120 -15
- package/src/index.css +6 -2
- package/src/features/pedago/AnswerSheetEditor.tsx +0 -72
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capytale/meta-player",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite",
|
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@capytale/activity.js": "^3.1.
|
|
22
|
+
"@capytale/activity.js": "^3.1.6",
|
|
23
23
|
"@capytale/capytale-anti-triche": "^0.2.1",
|
|
24
24
|
"@capytale/capytale-rich-text-editor": "^0.4.2",
|
|
25
25
|
"@reduxjs/toolkit": "^2.0.1",
|
|
26
26
|
"@uidotdev/usehooks": "^2.4.1",
|
|
27
27
|
"primeicons": "^7.0.0",
|
|
28
28
|
"primereact": "^10.8.3",
|
|
29
|
+
"react-dropzone": "^14.2.9",
|
|
29
30
|
"react-html-props": "^2.0.9",
|
|
30
31
|
"react-redux": "^9.1.0",
|
|
31
32
|
"screenfull": "^6.0.2",
|
package/src/App.tsx
CHANGED
|
@@ -102,7 +102,7 @@ const App: FC<AppProps> = (props) => {
|
|
|
102
102
|
<Pedago key="pedago" />
|
|
103
103
|
</SplitterPanel>
|
|
104
104
|
<SplitterPanel minSize={40} size={70}>
|
|
105
|
-
<div
|
|
105
|
+
<div className="meta-player-content-cover"></div>
|
|
106
106
|
<div id="meta-player-content">{props.children}</div>
|
|
107
107
|
</SplitterPanel>
|
|
108
108
|
</Splitter>
|
package/src/MetaPlayer.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { FC, PropsWithChildren } from "react";
|
|
1
|
+
import React, { FC, PropsWithChildren, useMemo } from "react";
|
|
2
2
|
|
|
3
3
|
import { Provider } from "react-redux";
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ import { APIOptions, PrimeReactProvider } from "primereact/api";
|
|
|
6
6
|
import "primeicons/primeicons.css";
|
|
7
7
|
|
|
8
8
|
import App from "./App";
|
|
9
|
-
import {
|
|
9
|
+
import { makeStore } from "./app/store";
|
|
10
10
|
import "./index.css";
|
|
11
11
|
import ThemeSwitcher from "./features/theming/ThemeSwitcher";
|
|
12
12
|
import {
|
|
@@ -31,14 +31,23 @@ import {
|
|
|
31
31
|
selectReturnUrl,
|
|
32
32
|
} from "./features/activityData/activityDataSlice";
|
|
33
33
|
|
|
34
|
+
import { initialState as layoutInitialState } from "./features/layout/layoutSlice";
|
|
35
|
+
|
|
34
36
|
type AntiCheatOptions = {
|
|
35
37
|
preserveDom: boolean;
|
|
36
38
|
hasIframes: boolean;
|
|
37
39
|
};
|
|
38
40
|
|
|
41
|
+
type UIOptions = {
|
|
42
|
+
closePedagoByDefault: boolean;
|
|
43
|
+
noWorkflow: boolean;
|
|
44
|
+
noSaveForStudents: boolean;
|
|
45
|
+
};
|
|
46
|
+
|
|
39
47
|
type MetaPlayerProps = PropsWithChildren<{
|
|
40
48
|
activityJSOptions?: LoadOptions;
|
|
41
49
|
antiCheatOptions?: Partial<AntiCheatOptions>;
|
|
50
|
+
uiOptions?: Partial<UIOptions>;
|
|
42
51
|
}>;
|
|
43
52
|
|
|
44
53
|
const MetaPlayer: FC<MetaPlayerProps> = (props) => {
|
|
@@ -50,6 +59,24 @@ const MetaPlayer: FC<MetaPlayerProps> = (props) => {
|
|
|
50
59
|
hasIframes: false,
|
|
51
60
|
...props.antiCheatOptions,
|
|
52
61
|
};
|
|
62
|
+
const uiOptions: UIOptions = {
|
|
63
|
+
closePedagoByDefault: false,
|
|
64
|
+
noWorkflow: false,
|
|
65
|
+
noSaveForStudents: false,
|
|
66
|
+
...props.uiOptions,
|
|
67
|
+
};
|
|
68
|
+
const store = useMemo(
|
|
69
|
+
() =>
|
|
70
|
+
makeStore({
|
|
71
|
+
layout: {
|
|
72
|
+
...layoutInitialState,
|
|
73
|
+
isPedagoVisible: !uiOptions.closePedagoByDefault,
|
|
74
|
+
showWorkflow: !uiOptions.noWorkflow,
|
|
75
|
+
showSaveForStudents: !uiOptions.noSaveForStudents,
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
[],
|
|
79
|
+
);
|
|
53
80
|
return (
|
|
54
81
|
<PrimeReactProvider value={primeSettings}>
|
|
55
82
|
<Provider store={store}>
|
|
@@ -26,6 +26,8 @@ export type Icon = {
|
|
|
26
26
|
style?: any;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
export type EditorType = "rich" | "none";
|
|
30
|
+
|
|
29
31
|
interface ActivityJSData {
|
|
30
32
|
title: string;
|
|
31
33
|
mode: ActivityMode;
|
|
@@ -38,7 +40,10 @@ interface ActivityJSData {
|
|
|
38
40
|
accessTimerange: TimeRange | null;
|
|
39
41
|
studentInfo: StudentInfo | null;
|
|
40
42
|
instructions: Instructions | null;
|
|
41
|
-
|
|
43
|
+
instructionsType: EditorType;
|
|
44
|
+
pdfInstructions: Blob | null;
|
|
45
|
+
sharedNotesContent: InitialEditorStateType | null;
|
|
46
|
+
sharedNotesType: EditorType;
|
|
42
47
|
codeLink: string;
|
|
43
48
|
icon: Icon | null;
|
|
44
49
|
friendlyType: string;
|
|
@@ -55,7 +60,7 @@ type SaveState = "idle" | "should-save" | "saving";
|
|
|
55
60
|
|
|
56
61
|
interface UIState {
|
|
57
62
|
canSaveInstructions: boolean;
|
|
58
|
-
|
|
63
|
+
canSaveSharedNotes: boolean;
|
|
59
64
|
saveState: SaveState;
|
|
60
65
|
isPlayerDirty: boolean;
|
|
61
66
|
isMPDirty: boolean;
|
|
@@ -83,7 +88,10 @@ const initialState: ActivityDataState = {
|
|
|
83
88
|
class: "",
|
|
84
89
|
},
|
|
85
90
|
instructions: null,
|
|
86
|
-
|
|
91
|
+
instructionsType: "rich",
|
|
92
|
+
pdfInstructions: null,
|
|
93
|
+
sharedNotesContent: null,
|
|
94
|
+
sharedNotesType: "none",
|
|
87
95
|
codeLink: "",
|
|
88
96
|
icon: null,
|
|
89
97
|
friendlyType: "",
|
|
@@ -94,7 +102,7 @@ const initialState: ActivityDataState = {
|
|
|
94
102
|
...defaultMetaPlayerOptions,
|
|
95
103
|
|
|
96
104
|
canSaveInstructions: true,
|
|
97
|
-
|
|
105
|
+
canSaveSharedNotes: true,
|
|
98
106
|
saveState: "idle",
|
|
99
107
|
isPlayerDirty: false,
|
|
100
108
|
isMPDirty: false,
|
|
@@ -124,6 +132,9 @@ export const activityDataSlice = createAppSlice({
|
|
|
124
132
|
state.accessTimerange = action.payload.accessTimerange;
|
|
125
133
|
state.studentInfo = action.payload.studentInfo;
|
|
126
134
|
state.instructions = action.payload.instructions;
|
|
135
|
+
state.instructionsType = action.payload.instructionsType;
|
|
136
|
+
state.sharedNotesContent = action.payload.sharedNotesContent;
|
|
137
|
+
state.sharedNotesType = action.payload.sharedNotesType;
|
|
127
138
|
state.codeLink = action.payload.codeLink;
|
|
128
139
|
state.icon = action.payload.icon;
|
|
129
140
|
state.friendlyType = action.payload.friendlyType;
|
|
@@ -148,9 +159,9 @@ export const activityDataSlice = createAppSlice({
|
|
|
148
159
|
state.canSaveInstructions = action.payload;
|
|
149
160
|
},
|
|
150
161
|
),
|
|
151
|
-
|
|
162
|
+
setCanSaveSharedNotes: create.reducer(
|
|
152
163
|
(state, action: PayloadAction<boolean>) => {
|
|
153
|
-
state.
|
|
164
|
+
state.canSaveSharedNotes = action.payload;
|
|
154
165
|
},
|
|
155
166
|
),
|
|
156
167
|
setLexicalInstructionsState: create.reducer(
|
|
@@ -162,9 +173,24 @@ export const activityDataSlice = createAppSlice({
|
|
|
162
173
|
};
|
|
163
174
|
},
|
|
164
175
|
),
|
|
165
|
-
|
|
176
|
+
setLexicalSharedNotesState: create.reducer(
|
|
166
177
|
(state, action: PayloadAction<InitialEditorStateType>) => {
|
|
167
|
-
state.
|
|
178
|
+
state.sharedNotesContent = action.payload;
|
|
179
|
+
},
|
|
180
|
+
),
|
|
181
|
+
setPdfInstructions: create.reducer(
|
|
182
|
+
(state, action: PayloadAction<Blob | null>) => {
|
|
183
|
+
state.pdfInstructions = action.payload;
|
|
184
|
+
},
|
|
185
|
+
),
|
|
186
|
+
setInstructionsType: create.reducer(
|
|
187
|
+
(state, action: PayloadAction<EditorType>) => {
|
|
188
|
+
state.instructionsType = action.payload;
|
|
189
|
+
},
|
|
190
|
+
),
|
|
191
|
+
setSharedNotesType: create.reducer(
|
|
192
|
+
(state, action: PayloadAction<EditorType>) => {
|
|
193
|
+
state.sharedNotesType = action.payload;
|
|
168
194
|
},
|
|
169
195
|
),
|
|
170
196
|
setSaveState: create.reducer((state, action: PayloadAction<SaveState>) => {
|
|
@@ -206,7 +232,10 @@ export const activityDataSlice = createAppSlice({
|
|
|
206
232
|
codeLink: data.codeLink,
|
|
207
233
|
}),
|
|
208
234
|
selectInstructions: (data) => data.instructions,
|
|
209
|
-
|
|
235
|
+
selectInstructionsType: (data) => data.instructionsType,
|
|
236
|
+
selectPdfInstructions: (data) => data.pdfInstructions,
|
|
237
|
+
selectSharedNotesContent: (data) => data.sharedNotesContent,
|
|
238
|
+
selectSharedNotesType: (data) => data.sharedNotesType,
|
|
210
239
|
selectComments: (data) => data.comments,
|
|
211
240
|
selectGrading: (data) => data.grading,
|
|
212
241
|
selectHasInstructions: (data) => data.hasInstructions,
|
|
@@ -253,9 +282,12 @@ export const {
|
|
|
253
282
|
setActivityJSData,
|
|
254
283
|
setPlayerSettings,
|
|
255
284
|
setCanSaveInstructions,
|
|
256
|
-
|
|
285
|
+
setCanSaveSharedNotes,
|
|
257
286
|
setLexicalInstructionsState,
|
|
258
|
-
|
|
287
|
+
setLexicalSharedNotesState,
|
|
288
|
+
setPdfInstructions,
|
|
289
|
+
setInstructionsType,
|
|
290
|
+
setSharedNotesType,
|
|
259
291
|
setSaveState,
|
|
260
292
|
setGrading,
|
|
261
293
|
setComments,
|
|
@@ -271,7 +303,10 @@ export const {
|
|
|
271
303
|
selectActivityInfo,
|
|
272
304
|
selectSharingInfo,
|
|
273
305
|
selectInstructions,
|
|
274
|
-
|
|
306
|
+
selectInstructionsType,
|
|
307
|
+
selectPdfInstructions,
|
|
308
|
+
selectSharedNotesContent,
|
|
309
|
+
selectSharedNotesType,
|
|
275
310
|
selectComments,
|
|
276
311
|
selectGrading,
|
|
277
312
|
selectHasInstructions,
|
|
@@ -44,18 +44,9 @@ export const ActivityJSProvider: FC<ActivityJSProviderProps> = (props) => {
|
|
|
44
44
|
window.capy = data;
|
|
45
45
|
|
|
46
46
|
const ab = data.activityBunch;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
console.log(ab.activityNode.answer_sheet.value);
|
|
51
|
-
*/
|
|
52
|
-
/*
|
|
53
|
-
const answerSheetContent =
|
|
54
|
-
ab.assignmentNode?.answer_sheet.value ||
|
|
55
|
-
ab.activityNode.answer_sheet.value ||
|
|
56
|
-
null;
|
|
57
|
-
*/
|
|
58
|
-
const answerSheetContent = null;
|
|
47
|
+
|
|
48
|
+
const sharedNotesContent = ab.shared_notes.value;
|
|
49
|
+
|
|
59
50
|
dispatch(
|
|
60
51
|
setActivityJSData({
|
|
61
52
|
title: ab.title.value || "",
|
|
@@ -82,10 +73,18 @@ export const ActivityJSProvider: FC<ActivityJSProviderProps> = (props) => {
|
|
|
82
73
|
}
|
|
83
74
|
: {
|
|
84
75
|
value: null,
|
|
85
|
-
htmlValue: ab.instructions.value.html,
|
|
76
|
+
htmlValue: ab.instructions.value.html || "",
|
|
86
77
|
format: "html",
|
|
87
78
|
},
|
|
88
|
-
|
|
79
|
+
instructionsType:
|
|
80
|
+
ab.instructions.value.lexical ||
|
|
81
|
+
ab.instructions.value.html ||
|
|
82
|
+
(data.mode === "create" && ab.activityNode.isNew)
|
|
83
|
+
? "rich"
|
|
84
|
+
: "none",
|
|
85
|
+
pdfInstructions: null,
|
|
86
|
+
sharedNotesContent: sharedNotesContent,
|
|
87
|
+
sharedNotesType: sharedNotesContent == null ? "none" : "rich",
|
|
89
88
|
codeLink: data.codeLink || "",
|
|
90
89
|
icon: data.icon || null,
|
|
91
90
|
friendlyType: data.friendlyType,
|
|
@@ -175,10 +174,12 @@ export const useActivityJsEssentials = () => {
|
|
|
175
174
|
};
|
|
176
175
|
const hasAssignment = !!ab.assignmentNode;
|
|
177
176
|
const title = ab.title.value;
|
|
177
|
+
const nid = ab.mainNode.nid;
|
|
178
178
|
return {
|
|
179
179
|
mode,
|
|
180
180
|
title,
|
|
181
181
|
hasAssignment,
|
|
182
|
+
nid,
|
|
182
183
|
getActivityContent,
|
|
183
184
|
getAssignmentContent,
|
|
184
185
|
getActivityBinaryData,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FC, useEffect, useRef } from "react";
|
|
2
2
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
selectSharedNotesContent,
|
|
5
5
|
selectComments,
|
|
6
6
|
selectGrading,
|
|
7
7
|
selectInstructions,
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
setIsPlayerDirty,
|
|
11
11
|
setSaveState,
|
|
12
12
|
setWorkflow,
|
|
13
|
+
selectSharedNotesType,
|
|
14
|
+
selectInstructionsType,
|
|
13
15
|
} from "../activityData/activityDataSlice";
|
|
14
16
|
import { useActivityJS } from "./ActivityJSProvider";
|
|
15
17
|
import { Toast } from "primereact/toast";
|
|
@@ -19,7 +21,9 @@ const Saver: FC<{}> = () => {
|
|
|
19
21
|
const dispatch = useAppDispatch();
|
|
20
22
|
const activityJs = useActivityJS();
|
|
21
23
|
const instructions = useAppSelector(selectInstructions);
|
|
22
|
-
const
|
|
24
|
+
const instructionsType = useAppSelector(selectInstructionsType);
|
|
25
|
+
const sharedNotesContent = useAppSelector(selectSharedNotesContent);
|
|
26
|
+
const sharedNotesType = useAppSelector(selectSharedNotesType);
|
|
23
27
|
const comments = useAppSelector(selectComments);
|
|
24
28
|
const grading = useAppSelector(selectGrading);
|
|
25
29
|
const beforeSave = useAppSelector(selectBeforeSave);
|
|
@@ -52,37 +56,43 @@ const Saver: FC<{}> = () => {
|
|
|
52
56
|
}
|
|
53
57
|
const ab = activityJs.activitySession.activityBunch;
|
|
54
58
|
if (activityJs.activitySession.mode === "create") {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
? instructions.value
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
:
|
|
72
|
-
|
|
59
|
+
if (instructionsType === "rich") {
|
|
60
|
+
ab.instructions.value = {
|
|
61
|
+
lexical:
|
|
62
|
+
instructions?.format === "lexical"
|
|
63
|
+
? typeof instructions.value === "string"
|
|
64
|
+
? instructions.value
|
|
65
|
+
: JSON.stringify(instructions.value)
|
|
66
|
+
: null,
|
|
67
|
+
html:
|
|
68
|
+
instructions?.format === "lexical"
|
|
69
|
+
? instructions.htmlValue
|
|
70
|
+
: (instructions?.value as string),
|
|
71
|
+
};
|
|
72
|
+
} else {
|
|
73
|
+
ab.instructions.value = {
|
|
74
|
+
lexical: null,
|
|
75
|
+
html: null,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
if (activityJs.activitySession.mode === "review") {
|
|
75
80
|
ab.assignmentNode!.comments.value = comments || "";
|
|
76
81
|
ab.assignmentNode!.grading.value = grading || "";
|
|
77
82
|
}
|
|
78
83
|
if (
|
|
84
|
+
activityJs.activitySession.mode === "create" ||
|
|
79
85
|
activityJs.activitySession.mode === "review" ||
|
|
80
86
|
activityJs.activitySession.mode === "assignment"
|
|
81
87
|
) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
if (sharedNotesContent && sharedNotesType === "rich") {
|
|
89
|
+
ab.shared_notes.value =
|
|
90
|
+
typeof sharedNotesContent === "string"
|
|
91
|
+
? sharedNotesContent
|
|
92
|
+
: JSON.stringify(sharedNotesContent);
|
|
93
|
+
} else {
|
|
94
|
+
ab.shared_notes.value = null;
|
|
95
|
+
}
|
|
86
96
|
}
|
|
87
97
|
try {
|
|
88
98
|
const saveData = await ab.save();
|
|
@@ -3,20 +3,24 @@ import { createAppSlice } from "../../app/createAppSlice";
|
|
|
3
3
|
|
|
4
4
|
export type Orientation = "horizontal" | "vertical";
|
|
5
5
|
|
|
6
|
-
type PedagoTab = "instructions" | "
|
|
6
|
+
export type PedagoTab = "instructions" | "sharedNotes" | "pdf";
|
|
7
7
|
|
|
8
8
|
export interface LayoutState {
|
|
9
9
|
orientation: Orientation;
|
|
10
10
|
isPedagoVisible: boolean;
|
|
11
11
|
isGradingVisible: boolean;
|
|
12
12
|
pedagoTab: PedagoTab;
|
|
13
|
+
showWorkflow: boolean;
|
|
14
|
+
showSaveForStudents: boolean;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
const initialState: LayoutState = {
|
|
17
|
+
export const initialState: LayoutState = {
|
|
16
18
|
orientation: "horizontal",
|
|
17
19
|
isPedagoVisible: true,
|
|
18
20
|
isGradingVisible: true,
|
|
19
21
|
pedagoTab: "instructions",
|
|
22
|
+
showWorkflow: true,
|
|
23
|
+
showSaveForStudents: true,
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
// If you are not using async thunks you can use the standalone `createSlice`.
|
|
@@ -41,6 +45,11 @@ export const layoutSlice = createAppSlice({
|
|
|
41
45
|
toggleIsPedagoVisible: create.reducer((state) => {
|
|
42
46
|
state.isPedagoVisible = !state.isPedagoVisible;
|
|
43
47
|
}),
|
|
48
|
+
setIsGradingVisible: create.reducer(
|
|
49
|
+
(state, action: PayloadAction<boolean>) => {
|
|
50
|
+
state.isGradingVisible = action.payload;
|
|
51
|
+
},
|
|
52
|
+
),
|
|
44
53
|
toggleIsGradingVisible: create.reducer((state) => {
|
|
45
54
|
state.isGradingVisible = !state.isGradingVisible;
|
|
46
55
|
}),
|
|
@@ -55,6 +64,8 @@ export const layoutSlice = createAppSlice({
|
|
|
55
64
|
selectIsPedagoVisible: (layout) => layout.isPedagoVisible,
|
|
56
65
|
selectIsGradingVisible: (layout) => layout.isGradingVisible,
|
|
57
66
|
selectPedagoTab: (layout) => layout.pedagoTab,
|
|
67
|
+
selectShowWorkflow: (layout) => layout.showWorkflow,
|
|
68
|
+
selectShowSaveForStudents: (layout) => layout.showSaveForStudents,
|
|
58
69
|
},
|
|
59
70
|
});
|
|
60
71
|
|
|
@@ -63,6 +74,7 @@ export const {
|
|
|
63
74
|
toggleLayout,
|
|
64
75
|
setLayout,
|
|
65
76
|
toggleIsPedagoVisible,
|
|
77
|
+
setIsGradingVisible,
|
|
66
78
|
toggleIsGradingVisible,
|
|
67
79
|
setPedagoTab,
|
|
68
80
|
} = layoutSlice.actions;
|
|
@@ -73,4 +85,6 @@ export const {
|
|
|
73
85
|
selectIsPedagoVisible,
|
|
74
86
|
selectIsGradingVisible,
|
|
75
87
|
selectPedagoTab,
|
|
88
|
+
selectShowWorkflow,
|
|
89
|
+
selectShowSaveForStudents,
|
|
76
90
|
} = layoutSlice.selectors;
|
|
@@ -25,6 +25,10 @@ import capytaleUI from "@capytale/activity.js/backend/capytale/ui";
|
|
|
25
25
|
import ButtonDoubleIcon from "../../utils/ButtonDoubleIcon";
|
|
26
26
|
import CardSelector from "../../utils/CardSelector";
|
|
27
27
|
import { useActivityJS } from "../activityJS/ActivityJSProvider";
|
|
28
|
+
import {
|
|
29
|
+
selectShowSaveForStudents,
|
|
30
|
+
selectShowWorkflow,
|
|
31
|
+
} from "../layout/layoutSlice";
|
|
28
32
|
|
|
29
33
|
const CapytaleMenu: React.FC = () => {
|
|
30
34
|
const dispatch = useAppDispatch();
|
|
@@ -36,6 +40,10 @@ const CapytaleMenu: React.FC = () => {
|
|
|
36
40
|
const workflow = useAppSelector(selectWorkflow);
|
|
37
41
|
const isDirty = useAppSelector(selectIsDirty);
|
|
38
42
|
const showSaveButton = useAppSelector(selectShowSaveButton);
|
|
43
|
+
const showSaveForStudents = useAppSelector(selectShowSaveForStudents);
|
|
44
|
+
const hasSaveButton =
|
|
45
|
+
showSaveButton && !(mode === "assignment" && !showSaveForStudents);
|
|
46
|
+
const showWorkflow = useAppSelector(selectShowWorkflow);
|
|
39
47
|
const isLarge = useMemo(
|
|
40
48
|
() => windowsSize.width && windowsSize.width >= XL,
|
|
41
49
|
[windowsSize.width],
|
|
@@ -82,7 +90,7 @@ const CapytaleMenu: React.FC = () => {
|
|
|
82
90
|
/>
|
|
83
91
|
)}
|
|
84
92
|
{
|
|
85
|
-
|
|
93
|
+
hasSaveButton && (
|
|
86
94
|
<Button
|
|
87
95
|
label={isQuiteSmall ? undefined : "Enregistrer"}
|
|
88
96
|
disabled={!isDirty}
|
|
@@ -145,86 +153,92 @@ const CapytaleMenu: React.FC = () => {
|
|
|
145
153
|
]}
|
|
146
154
|
/>
|
|
147
155
|
)}
|
|
148
|
-
{
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
)}
|
|
158
|
-
{mode === "assignment" && workflow !== "current" && (
|
|
159
|
-
<Button
|
|
160
|
-
outlined
|
|
161
|
-
label={workflow === "finished" ? "Rendue" : "Corrigée"}
|
|
162
|
-
disabled
|
|
163
|
-
icon={
|
|
164
|
-
workflow === "finished" ? "pi pi-envelope" : "pi pi-check-square"
|
|
165
|
-
}
|
|
166
|
-
tooltip="Copie déjà rendue"
|
|
167
|
-
/>
|
|
168
|
-
)}
|
|
169
|
-
{mode === "review" && (
|
|
170
|
-
<div>
|
|
171
|
-
<ButtonDoubleIcon
|
|
172
|
-
severity="secondary"
|
|
173
|
-
size="small"
|
|
174
|
-
outlined
|
|
175
|
-
onClick={(e) => {
|
|
176
|
-
overlayPanelWorkflow.current!.toggle(e);
|
|
177
|
-
}}
|
|
178
|
-
label={
|
|
179
|
-
workflow === "current"
|
|
180
|
-
? "En cours"
|
|
181
|
-
: workflow === "finished"
|
|
182
|
-
? "Rendue"
|
|
183
|
-
: "Corrigée"
|
|
184
|
-
}
|
|
185
|
-
leftIcon={
|
|
186
|
-
"pi " +
|
|
187
|
-
(workflow === "current"
|
|
188
|
-
? "pi pi-pencil"
|
|
189
|
-
: workflow === "finished"
|
|
190
|
-
? "pi pi-envelope"
|
|
191
|
-
: "pi pi-check-square")
|
|
192
|
-
}
|
|
193
|
-
rightIcon="pi pi-angle-down"
|
|
194
|
-
/>
|
|
195
|
-
<OverlayPanel ref={overlayPanelWorkflow}>
|
|
196
|
-
<h3 className={styles.overlayPanelWorkflowTitle}>
|
|
197
|
-
État de la copie
|
|
198
|
-
</h3>
|
|
199
|
-
<CardSelector
|
|
200
|
-
selected={workflow}
|
|
201
|
-
onChange={(option: wf) => {
|
|
202
|
-
changeWorkflow(option);
|
|
203
|
-
|
|
204
|
-
overlayPanelWorkflow.current!.hide();
|
|
156
|
+
{showWorkflow && (
|
|
157
|
+
<>
|
|
158
|
+
{mode === "assignment" && workflow === "current" && (
|
|
159
|
+
<Button
|
|
160
|
+
outlined
|
|
161
|
+
label="Rendre"
|
|
162
|
+
icon="pi pi-envelope"
|
|
163
|
+
onClick={() => {
|
|
164
|
+
confirmFinishAssignment();
|
|
205
165
|
}}
|
|
206
|
-
options={[
|
|
207
|
-
{
|
|
208
|
-
value: "current",
|
|
209
|
-
title: "En cours",
|
|
210
|
-
description:
|
|
211
|
-
"L'élève peut modifier sa copie à tout moment.",
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
value: "finished",
|
|
215
|
-
title: "Rendue",
|
|
216
|
-
description: "L'élève ne peut plus modifier sa copie.",
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
value: "corrected",
|
|
220
|
-
title: "Corrigée",
|
|
221
|
-
description:
|
|
222
|
-
"L'élève ne peut plus modifier sa copie et a reçu une correction.",
|
|
223
|
-
},
|
|
224
|
-
]}
|
|
225
166
|
/>
|
|
226
|
-
|
|
227
|
-
|
|
167
|
+
)}
|
|
168
|
+
{mode === "assignment" && workflow !== "current" && (
|
|
169
|
+
<Button
|
|
170
|
+
outlined
|
|
171
|
+
label={workflow === "finished" ? "Rendue" : "Corrigée"}
|
|
172
|
+
disabled
|
|
173
|
+
icon={
|
|
174
|
+
workflow === "finished"
|
|
175
|
+
? "pi pi-envelope"
|
|
176
|
+
: "pi pi-check-square"
|
|
177
|
+
}
|
|
178
|
+
tooltip="Copie déjà rendue"
|
|
179
|
+
/>
|
|
180
|
+
)}
|
|
181
|
+
{mode === "review" && (
|
|
182
|
+
<div>
|
|
183
|
+
<ButtonDoubleIcon
|
|
184
|
+
severity="secondary"
|
|
185
|
+
size="small"
|
|
186
|
+
outlined
|
|
187
|
+
onClick={(e) => {
|
|
188
|
+
overlayPanelWorkflow.current!.toggle(e);
|
|
189
|
+
}}
|
|
190
|
+
label={
|
|
191
|
+
workflow === "current"
|
|
192
|
+
? "En cours"
|
|
193
|
+
: workflow === "finished"
|
|
194
|
+
? "Rendue"
|
|
195
|
+
: "Corrigée"
|
|
196
|
+
}
|
|
197
|
+
leftIcon={
|
|
198
|
+
"pi " +
|
|
199
|
+
(workflow === "current"
|
|
200
|
+
? "pi pi-pencil"
|
|
201
|
+
: workflow === "finished"
|
|
202
|
+
? "pi pi-envelope"
|
|
203
|
+
: "pi pi-check-square")
|
|
204
|
+
}
|
|
205
|
+
rightIcon="pi pi-angle-down"
|
|
206
|
+
/>
|
|
207
|
+
<OverlayPanel ref={overlayPanelWorkflow}>
|
|
208
|
+
<h3 className={styles.overlayPanelWorkflowTitle}>
|
|
209
|
+
État de la copie
|
|
210
|
+
</h3>
|
|
211
|
+
<CardSelector
|
|
212
|
+
selected={workflow}
|
|
213
|
+
onChange={(option: wf) => {
|
|
214
|
+
changeWorkflow(option);
|
|
215
|
+
|
|
216
|
+
overlayPanelWorkflow.current!.hide();
|
|
217
|
+
}}
|
|
218
|
+
options={[
|
|
219
|
+
{
|
|
220
|
+
value: "current",
|
|
221
|
+
title: "En cours",
|
|
222
|
+
description:
|
|
223
|
+
"L'élève peut modifier sa copie à tout moment.",
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
value: "finished",
|
|
227
|
+
title: "Rendue",
|
|
228
|
+
description: "L'élève ne peut plus modifier sa copie.",
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
value: "corrected",
|
|
232
|
+
title: "Corrigée",
|
|
233
|
+
description:
|
|
234
|
+
"L'élève ne peut plus modifier sa copie et a reçu une correction.",
|
|
235
|
+
},
|
|
236
|
+
]}
|
|
237
|
+
/>
|
|
238
|
+
</OverlayPanel>
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
</>
|
|
228
242
|
)}
|
|
229
243
|
<ConfirmDialog />
|
|
230
244
|
</div>
|