@capytale/meta-player 0.3.4 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capytale/meta-player",
3
- "version": "0.3.4",
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.0",
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 id="meta-player-content-cover"></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>
@@ -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 { store } from "./app/store";
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
- answerSheetContent: InitialEditorStateType | null;
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
- canSaveAnswerSheet: boolean;
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
- answerSheetContent: null,
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
- canSaveAnswerSheet: true,
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
- setCanSaveAnswerSheet: create.reducer(
162
+ setCanSaveSharedNotes: create.reducer(
152
163
  (state, action: PayloadAction<boolean>) => {
153
- state.canSaveAnswerSheet = action.payload;
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
- setLexicalAnswerSheetState: create.reducer(
176
+ setLexicalSharedNotesState: create.reducer(
166
177
  (state, action: PayloadAction<InitialEditorStateType>) => {
167
- state.answerSheetContent = action.payload;
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
- selectAnswerSheetContent: (data) => data.answerSheetContent,
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
- setCanSaveAnswerSheet,
285
+ setCanSaveSharedNotes,
257
286
  setLexicalInstructionsState,
258
- setLexicalAnswerSheetState,
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
- selectAnswerSheetContent,
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
- console.log("Test accès");
49
- console.log(ab.assignmentNode?.answer_sheet.value);
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
- answerSheetContent: answerSheetContent,
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,
@@ -1,7 +1,7 @@
1
1
  import { FC, useEffect, useRef } from "react";
2
2
  import { useAppDispatch, useAppSelector } from "../../app/hooks";
3
3
  import {
4
- selectAnswerSheetContent,
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 answerSheetContent = useAppSelector(selectAnswerSheetContent);
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
- ab.instructions.value = {
56
- lexical:
57
- instructions?.format === "lexical"
58
- ? typeof instructions.value === "string"
59
- ? instructions.value
60
- : JSON.stringify(instructions.value)
61
- : null,
62
- html:
63
- instructions?.format === "lexical"
64
- ? instructions.htmlValue
65
- : (instructions?.value as string),
66
- };
67
- /*
68
- ab.activityNode.answer_sheet.value =
69
- typeof answerSheetContent === "string"
70
- ? answerSheetContent
71
- : JSON.stringify(answerSheetContent);
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
- ab.assignmentNode!.answer_sheet.value =
83
- typeof answerSheetContent === "string"
84
- ? answerSheetContent
85
- : JSON.stringify(answerSheetContent);
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" | "answerSheet";
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
- showSaveButton && (
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
- {mode === "assignment" && workflow === "current" && (
149
- <Button
150
- outlined
151
- label="Rendre"
152
- icon="pi pi-envelope"
153
- onClick={() => {
154
- confirmFinishAssignment();
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
- </OverlayPanel>
227
- </div>
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>