@capytale/meta-player 0.5.3 → 0.5.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 +2 -2
- package/src/App.tsx +2 -4
- package/src/demo.tsx +2 -2
- package/src/features/activityData/activityDataSlice.ts +11 -96
- package/src/features/activityData/activityJsData.ts +81 -0
- package/src/features/activityData/hooks.ts +41 -7
- package/src/features/activityData/metaPlayerOptions.ts +6 -4
- package/src/features/activityData/uiState.ts +21 -0
- package/src/features/activityJS/ActivityJSProvider.tsx +1 -0
- package/src/features/navbar/{ActivityInfo.tsx → activity-info/index.tsx} +3 -3
- package/src/features/navbar/activity-info/style.module.scss +38 -0
- package/src/features/navbar/{ActivityQuickActions.tsx → activity-menu/ActivityQuickActions.tsx} +3 -3
- package/src/features/navbar/{ActivityMenu.tsx → activity-menu/index.tsx} +9 -9
- package/src/features/navbar/activity-menu/style.module.scss +7 -0
- package/src/features/navbar/{Countdown.tsx → capytale-menu/Countdown.tsx} +5 -5
- package/src/features/navbar/capytale-menu/CountdownAndSaveButton.tsx +115 -0
- package/src/features/navbar/{CapytaleMenu.tsx → capytale-menu/index.tsx} +12 -51
- package/src/features/navbar/capytale-menu/style.module.scss +9 -0
- package/src/features/navbar/index.tsx +3 -3
- package/src/features/navbar/{GradingNav.tsx → review-navbar/GradingNav.tsx} +6 -4
- package/src/features/navbar/review-navbar/style.module.scss +22 -0
- package/src/features/navbar/{ActivitySidebarActions.tsx → sidebars/ActivitySidebarActions.tsx} +3 -3
- package/src/features/navbar/{AttachedFilesSidebarContent.tsx → sidebars/AttachedFilesSidebarContent.tsx} +5 -5
- package/src/features/navbar/{SettingsSidebarContent.tsx → sidebars/SettingsSidebarContent.tsx} +8 -8
- package/src/features/navbar/sidebars/style.module.scss +15 -0
- package/src/features/navbar/student-utils.ts +1 -1
- package/src/features/navbar/style.module.scss +0 -96
- package/src/features/pedago/PedagoCommands.tsx +2 -0
- package/src/features/pedago/index.tsx +14 -0
- package/src/index.tsx +10 -4
- /package/src/features/navbar/{ReviewNavbar.tsx → review-navbar/index.tsx} +0 -0
- /package/src/features/navbar/{AttachedFilesSidebarContent.module.scss → sidebars/AttachedFilesSidebarContent.module.scss} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capytale/meta-player",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@capytale/activity.js": "^3.1.
|
|
19
|
+
"@capytale/activity.js": "^3.1.14",
|
|
20
20
|
"@capytale/capytale-anti-triche": "^0.2.1",
|
|
21
21
|
"@capytale/capytale-rich-text-editor": "^0.4.3",
|
|
22
22
|
"@reduxjs/toolkit": "^2.0.1",
|
package/src/App.tsx
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
selectMode,
|
|
23
23
|
} from "./features/activityData/activityDataSlice";
|
|
24
24
|
import settings from "./settings";
|
|
25
|
-
import ReviewNavbar from "./features/navbar/
|
|
25
|
+
import ReviewNavbar from "./features/navbar/review-navbar";
|
|
26
26
|
import { useSave } from "./features/activityData/hooks";
|
|
27
27
|
|
|
28
28
|
type AppProps = PropsWithChildren<{}>;
|
|
@@ -45,9 +45,7 @@ const App: FC<AppProps> = (props) => {
|
|
|
45
45
|
(e: KeyboardEvent<HTMLDivElement>) => {
|
|
46
46
|
if ((e.ctrlKey || e.metaKey) && e.key === "s") {
|
|
47
47
|
e.preventDefault();
|
|
48
|
-
if (
|
|
49
|
-
save();
|
|
50
|
-
}
|
|
48
|
+
save(); // Checks if can save inside of save()
|
|
51
49
|
}
|
|
52
50
|
},
|
|
53
51
|
[isDirty, save],
|
package/src/demo.tsx
CHANGED
|
@@ -2,8 +2,8 @@ import React, { FC } from "react";
|
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
3
|
|
|
4
4
|
import { MetaPlayer } from ".";
|
|
5
|
-
import { ActivityQuickActionsSetter } from "./features/navbar/ActivityQuickActions";
|
|
6
|
-
import { ActivitySidebarActionsSetter } from "./features/navbar/ActivitySidebarActions";
|
|
5
|
+
import { ActivityQuickActionsSetter } from "./features/navbar/activity-menu/ActivityQuickActions";
|
|
6
|
+
import { ActivitySidebarActionsSetter } from "./features/navbar/sidebars/ActivitySidebarActions";
|
|
7
7
|
// import { useActivityJS } from "./features/activityJS/ActivityJSProvider";
|
|
8
8
|
import BeforeSaveAction from "./features/activityJS/BeforeSaveAction";
|
|
9
9
|
import MetaPlayerOptionsSetter from "./features/activityData/MetaPlayerOptionsSetter";
|
|
@@ -1,113 +1,22 @@
|
|
|
1
1
|
import type { PayloadAction } from "@reduxjs/toolkit";
|
|
2
2
|
import { createAppSlice } from "../../app/createAppSlice";
|
|
3
|
-
import { ActivityMode } from "@capytale/activity.js/activity/activitySession";
|
|
4
|
-
import { TimeRange } from "@capytale/activity.js/common/field";
|
|
5
3
|
import { InitialEditorStateType } from "@capytale/capytale-rich-text-editor";
|
|
6
4
|
import {
|
|
7
5
|
MetaPlayerOptions,
|
|
8
6
|
defaultMetaPlayerOptions,
|
|
9
7
|
} from "./metaPlayerOptions";
|
|
10
8
|
import { wf } from "@capytale/activity.js/activity/field/workflow";
|
|
9
|
+
import { ActivityJSData, defaultActivityJSData, EditorType } from "./activityJsData";
|
|
10
|
+
import { defaultUIState, SaveState, UIState } from "./uiState";
|
|
11
11
|
|
|
12
|
-
export type StudentInfo = {
|
|
13
|
-
firstName: string;
|
|
14
|
-
lastName: string;
|
|
15
|
-
class: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type Instructions = {
|
|
19
|
-
value: InitialEditorStateType | null;
|
|
20
|
-
htmlValue: string | null;
|
|
21
|
-
format: "html" | "markdown" | "lexical";
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type Icon = {
|
|
25
|
-
path: string;
|
|
26
|
-
style?: any;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export type EditorType = "rich" | "none";
|
|
30
12
|
|
|
31
|
-
interface ActivityJSData {
|
|
32
|
-
title: string;
|
|
33
|
-
mode: ActivityMode;
|
|
34
|
-
returnUrl: string;
|
|
35
|
-
helpUrl: string;
|
|
36
|
-
code: string | null;
|
|
37
|
-
nid: number;
|
|
38
|
-
activityNid: number;
|
|
39
|
-
accessTrMode: string;
|
|
40
|
-
accessTimerange: TimeRange | null;
|
|
41
|
-
studentInfo: StudentInfo | null;
|
|
42
|
-
instructions: Instructions | null;
|
|
43
|
-
instructionsType: EditorType;
|
|
44
|
-
pdfInstructions: Blob | null;
|
|
45
|
-
sharedNotesContent: InitialEditorStateType | null;
|
|
46
|
-
sharedNotesType: EditorType;
|
|
47
|
-
codeLink: string | null;
|
|
48
|
-
icon: Icon | null;
|
|
49
|
-
friendlyType: string;
|
|
50
|
-
comments: string | null;
|
|
51
|
-
grading: string | null;
|
|
52
|
-
workflow: wf | null | undefined;
|
|
53
|
-
antiCheat?: null | {
|
|
54
|
-
passwordHash?: string | null;
|
|
55
|
-
startLocked: boolean;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
13
|
|
|
59
|
-
type
|
|
60
|
-
|
|
61
|
-
interface UIState {
|
|
62
|
-
canSaveInstructions: boolean;
|
|
63
|
-
canSaveSharedNotes: boolean;
|
|
64
|
-
saveState: SaveState;
|
|
65
|
-
isPlayerDirty: boolean;
|
|
66
|
-
isMPDirty: boolean;
|
|
67
|
-
isAntiCheatExitDetectionDisabled: boolean;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface ActivityDataState
|
|
71
|
-
extends ActivityJSData,
|
|
72
|
-
MetaPlayerOptions,
|
|
73
|
-
UIState {}
|
|
14
|
+
export type ActivityDataState = ActivityJSData & MetaPlayerOptions & UIState;
|
|
74
15
|
|
|
75
16
|
const initialState: ActivityDataState = {
|
|
76
|
-
|
|
77
|
-
mode: "view",
|
|
78
|
-
returnUrl: "",
|
|
79
|
-
helpUrl: "",
|
|
80
|
-
code: null,
|
|
81
|
-
nid: 0,
|
|
82
|
-
activityNid: 0,
|
|
83
|
-
accessTrMode: "",
|
|
84
|
-
accessTimerange: null,
|
|
85
|
-
studentInfo: {
|
|
86
|
-
firstName: "",
|
|
87
|
-
lastName: "",
|
|
88
|
-
class: "",
|
|
89
|
-
},
|
|
90
|
-
instructions: null,
|
|
91
|
-
instructionsType: "rich",
|
|
92
|
-
pdfInstructions: null,
|
|
93
|
-
sharedNotesContent: null,
|
|
94
|
-
sharedNotesType: "none",
|
|
95
|
-
codeLink: null,
|
|
96
|
-
icon: null,
|
|
97
|
-
friendlyType: "",
|
|
98
|
-
comments: null,
|
|
99
|
-
grading: null,
|
|
100
|
-
workflow: null,
|
|
101
|
-
|
|
17
|
+
...defaultActivityJSData,
|
|
102
18
|
...defaultMetaPlayerOptions,
|
|
103
|
-
|
|
104
|
-
canSaveInstructions: true,
|
|
105
|
-
canSaveSharedNotes: true,
|
|
106
|
-
saveState: "idle",
|
|
107
|
-
isPlayerDirty: false,
|
|
108
|
-
isMPDirty: false,
|
|
109
|
-
|
|
110
|
-
isAntiCheatExitDetectionDisabled: false,
|
|
19
|
+
...defaultUIState,
|
|
111
20
|
};
|
|
112
21
|
|
|
113
22
|
// If you are not using async thunks you can use the standalone `createSlice`.
|
|
@@ -144,6 +53,7 @@ export const activityDataSlice = createAppSlice({
|
|
|
144
53
|
state.activityNid = action.payload.activityNid;
|
|
145
54
|
state.workflow = action.payload.workflow;
|
|
146
55
|
state.antiCheat = action.payload.antiCheat;
|
|
56
|
+
state.hasEvaluations = action.payload.hasEvaluations;
|
|
147
57
|
},
|
|
148
58
|
),
|
|
149
59
|
setPlayerSettings: create.reducer(
|
|
@@ -153,6 +63,7 @@ export const activityDataSlice = createAppSlice({
|
|
|
153
63
|
state.pedagoLayout = action.payload.pedagoLayout;
|
|
154
64
|
state.supportsLightTheme = action.payload.supportsLightTheme;
|
|
155
65
|
state.supportsDarkTheme = action.payload.supportsDarkTheme;
|
|
66
|
+
state.preventEditIfHasEvaluations = action.payload.preventEditIfHasEvaluations;
|
|
156
67
|
},
|
|
157
68
|
),
|
|
158
69
|
setCanSaveInstructions: create.reducer(
|
|
@@ -243,6 +154,7 @@ export const activityDataSlice = createAppSlice({
|
|
|
243
154
|
selectCanReset: (data) => data.canReset,
|
|
244
155
|
selectPedagoLayout: (data) => data.pedagoLayout,
|
|
245
156
|
selectHasGradingOrComments: (data) => !!(data.grading || data.comments),
|
|
157
|
+
selectPreventEditIfHasEvaluations: (data) => data.preventEditIfHasEvaluations,
|
|
246
158
|
selectSaveState: (data) => data.saveState,
|
|
247
159
|
selectReturnUrl: (data) => data.returnUrl,
|
|
248
160
|
selectNid: (data) => data.nid,
|
|
@@ -275,6 +187,7 @@ export const activityDataSlice = createAppSlice({
|
|
|
275
187
|
),
|
|
276
188
|
selectIsAntiCheatExitDetectionDisabled: (data) =>
|
|
277
189
|
data.isAntiCheatExitDetectionDisabled,
|
|
190
|
+
selectHasEvaluations: (data) => data.hasEvaluations,
|
|
278
191
|
},
|
|
279
192
|
});
|
|
280
193
|
|
|
@@ -315,6 +228,7 @@ export const {
|
|
|
315
228
|
selectCanReset,
|
|
316
229
|
selectPedagoLayout,
|
|
317
230
|
selectHasGradingOrComments,
|
|
231
|
+
selectPreventEditIfHasEvaluations,
|
|
318
232
|
selectSaveState,
|
|
319
233
|
selectReturnUrl,
|
|
320
234
|
selectNid,
|
|
@@ -332,4 +246,5 @@ export const {
|
|
|
332
246
|
selectAntiCheat,
|
|
333
247
|
selectHasAntiCheat,
|
|
334
248
|
selectIsAntiCheatExitDetectionDisabled,
|
|
249
|
+
selectHasEvaluations,
|
|
335
250
|
} = activityDataSlice.selectors;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ActivityMode } from "@capytale/activity.js/activity/activitySession";
|
|
2
|
+
import { TimeRange } from "@capytale/activity.js/common/field";
|
|
3
|
+
import { InitialEditorStateType } from "@capytale/capytale-rich-text-editor";
|
|
4
|
+
import { wf } from "@capytale/activity.js/activity/field/workflow";
|
|
5
|
+
|
|
6
|
+
export type StudentInfo = {
|
|
7
|
+
firstName: string;
|
|
8
|
+
lastName: string;
|
|
9
|
+
class: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type Instructions = {
|
|
13
|
+
value: InitialEditorStateType | null;
|
|
14
|
+
htmlValue: string | null;
|
|
15
|
+
format: "html" | "markdown" | "lexical";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type Icon = {
|
|
19
|
+
path: string;
|
|
20
|
+
style?: any;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type EditorType = "rich" | "none";
|
|
24
|
+
|
|
25
|
+
export type ActivityJSData = {
|
|
26
|
+
title: string;
|
|
27
|
+
mode: ActivityMode;
|
|
28
|
+
returnUrl: string;
|
|
29
|
+
helpUrl: string;
|
|
30
|
+
code: string | null;
|
|
31
|
+
nid: number;
|
|
32
|
+
activityNid: number;
|
|
33
|
+
accessTrMode: string;
|
|
34
|
+
accessTimerange: TimeRange | null;
|
|
35
|
+
studentInfo: StudentInfo | null;
|
|
36
|
+
instructions: Instructions | null;
|
|
37
|
+
instructionsType: EditorType;
|
|
38
|
+
pdfInstructions: Blob | null;
|
|
39
|
+
sharedNotesContent: InitialEditorStateType | null;
|
|
40
|
+
sharedNotesType: EditorType;
|
|
41
|
+
codeLink: string | null;
|
|
42
|
+
icon: Icon | null;
|
|
43
|
+
friendlyType: string;
|
|
44
|
+
comments: string | null;
|
|
45
|
+
grading: string | null;
|
|
46
|
+
workflow: wf | null | undefined;
|
|
47
|
+
antiCheat?: null | {
|
|
48
|
+
passwordHash?: string | null;
|
|
49
|
+
startLocked: boolean;
|
|
50
|
+
};
|
|
51
|
+
hasEvaluations: boolean | null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const defaultActivityJSData: ActivityJSData = {
|
|
55
|
+
title: "",
|
|
56
|
+
mode: "view",
|
|
57
|
+
returnUrl: "",
|
|
58
|
+
helpUrl: "",
|
|
59
|
+
code: null,
|
|
60
|
+
nid: 0,
|
|
61
|
+
activityNid: 0,
|
|
62
|
+
accessTrMode: "",
|
|
63
|
+
accessTimerange: null,
|
|
64
|
+
studentInfo: {
|
|
65
|
+
firstName: "",
|
|
66
|
+
lastName: "",
|
|
67
|
+
class: "",
|
|
68
|
+
},
|
|
69
|
+
instructions: null,
|
|
70
|
+
instructionsType: "rich",
|
|
71
|
+
pdfInstructions: null,
|
|
72
|
+
sharedNotesContent: null,
|
|
73
|
+
sharedNotesType: "none",
|
|
74
|
+
codeLink: null,
|
|
75
|
+
icon: null,
|
|
76
|
+
friendlyType: "",
|
|
77
|
+
comments: null,
|
|
78
|
+
grading: null,
|
|
79
|
+
workflow: null,
|
|
80
|
+
hasEvaluations: null,
|
|
81
|
+
};
|
|
@@ -1,14 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
|
3
|
+
import { selectShowSaveForStudents } from "../layout/layoutSlice";
|
|
4
|
+
import { selectHasEvaluations, selectIsDirty, selectMode, selectPreventEditIfHasEvaluations, selectShowSaveButton, setIsPlayerDirty, setSaveState } from "./activityDataSlice";
|
|
3
5
|
|
|
4
|
-
const useNotifyIsDirty = () => {
|
|
6
|
+
export const useNotifyIsDirty = () => {
|
|
5
7
|
const dispatch = useAppDispatch();
|
|
6
8
|
return (isDirty: boolean = true) => dispatch(setIsPlayerDirty(isDirty));
|
|
7
9
|
};
|
|
8
10
|
|
|
9
|
-
const
|
|
11
|
+
export const useCanSave = () => {
|
|
12
|
+
const isDirty = useAppSelector(selectIsDirty);
|
|
13
|
+
const preventEditIfHasEvaluations = useAppSelector(
|
|
14
|
+
selectPreventEditIfHasEvaluations,
|
|
15
|
+
);
|
|
16
|
+
const hasEvaluations = useAppSelector(selectHasEvaluations);
|
|
17
|
+
|
|
18
|
+
const mode = useAppSelector(selectMode);
|
|
19
|
+
const showSaveButton = useAppSelector(selectShowSaveButton);
|
|
20
|
+
const showSaveForStudents = useAppSelector(selectShowSaveForStudents);
|
|
21
|
+
const hasSaveButton =
|
|
22
|
+
showSaveButton && !(mode === "assignment" && !showSaveForStudents);
|
|
23
|
+
|
|
24
|
+
if (!hasSaveButton) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (mode === "create" && hasEvaluations && preventEditIfHasEvaluations) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!isDirty) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const useSave = () => {
|
|
10
40
|
const dispatch = useAppDispatch();
|
|
11
|
-
|
|
41
|
+
const canSave = useCanSave();
|
|
42
|
+
const setShouldSave = useCallback(() => {
|
|
43
|
+
if (canSave) {
|
|
44
|
+
dispatch(setSaveState("should-save"))
|
|
45
|
+
}
|
|
46
|
+
}, [dispatch, canSave]);
|
|
47
|
+
return setShouldSave;
|
|
12
48
|
};
|
|
13
|
-
|
|
14
|
-
export { useNotifyIsDirty, useSave };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export type MetaPlayerOptions = {
|
|
2
2
|
hasInstructions: boolean;
|
|
3
3
|
canReset: boolean;
|
|
4
|
+
preventEditIfHasEvaluations: boolean;
|
|
4
5
|
pedagoLayout:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
| "horizontal"
|
|
7
|
+
| "vertical"
|
|
8
|
+
| "default-horizontal"
|
|
9
|
+
| "default-vertical";
|
|
9
10
|
supportsLightTheme: boolean;
|
|
10
11
|
supportsDarkTheme: boolean;
|
|
11
12
|
};
|
|
@@ -13,6 +14,7 @@ export type MetaPlayerOptions = {
|
|
|
13
14
|
export const defaultMetaPlayerOptions: MetaPlayerOptions = {
|
|
14
15
|
hasInstructions: true,
|
|
15
16
|
canReset: true,
|
|
17
|
+
preventEditIfHasEvaluations: false,
|
|
16
18
|
pedagoLayout: "default-horizontal",
|
|
17
19
|
supportsLightTheme: true,
|
|
18
20
|
supportsDarkTheme: false,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type SaveState = "idle" | "should-save" | "saving";
|
|
2
|
+
|
|
3
|
+
export type UIState = {
|
|
4
|
+
canSaveInstructions: boolean;
|
|
5
|
+
canSaveSharedNotes: boolean;
|
|
6
|
+
saveState: SaveState;
|
|
7
|
+
isPlayerDirty: boolean;
|
|
8
|
+
isMPDirty: boolean;
|
|
9
|
+
|
|
10
|
+
isAntiCheatExitDetectionDisabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const defaultUIState: UIState = {
|
|
14
|
+
canSaveInstructions: true,
|
|
15
|
+
canSaveSharedNotes: true,
|
|
16
|
+
saveState: "idle",
|
|
17
|
+
isPlayerDirty: false,
|
|
18
|
+
isMPDirty: false,
|
|
19
|
+
|
|
20
|
+
isAntiCheatExitDetectionDisabled: false,
|
|
21
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useAppSelector } from "
|
|
1
|
+
import { useAppSelector } from "../../../app/hooks";
|
|
2
2
|
import {
|
|
3
3
|
selectActivityInfo,
|
|
4
4
|
selectIcon,
|
|
5
5
|
selectMode,
|
|
6
|
-
} from "
|
|
7
|
-
import { studentNameFromInfo } from "
|
|
6
|
+
} from "../../activityData/activityDataSlice";
|
|
7
|
+
import { studentNameFromInfo } from "../student-utils";
|
|
8
8
|
import styles from "./style.module.scss";
|
|
9
9
|
|
|
10
10
|
const ActivityInfo: React.FC = () => {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
.activityInfo {
|
|
2
|
+
flex-shrink: 1;
|
|
3
|
+
flex-grow: 1;
|
|
4
|
+
display: flex;
|
|
5
|
+
justify-content: center;
|
|
6
|
+
align-items: center;
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
gap: 12px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.activityInfoText {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: center;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
& > * {
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
text-overflow: ellipsis;
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
max-width: 100%;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.activityInfoTitle {
|
|
26
|
+
font-size: 20px;
|
|
27
|
+
font-weight: 500;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.activityLogo {
|
|
31
|
+
width: 35px;
|
|
32
|
+
height: 37px;
|
|
33
|
+
border-radius: 6px;
|
|
34
|
+
flex-shrink: 0;
|
|
35
|
+
@media only screen and (max-width: 992px) {
|
|
36
|
+
display: none;
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/features/navbar/{ActivityQuickActions.tsx → activity-menu/ActivityQuickActions.tsx}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { FC, useEffect } from "react";
|
|
2
|
-
import { useAppDispatch, useAppSelector } from "
|
|
2
|
+
import { useAppDispatch, useAppSelector } from "../../../app/hooks";
|
|
3
3
|
import {
|
|
4
4
|
QuickAction,
|
|
5
5
|
selectQuickActions,
|
|
6
6
|
setQuickActions,
|
|
7
7
|
usePerformQuickAction,
|
|
8
|
-
} from "
|
|
8
|
+
} from "../navbarSlice";
|
|
9
9
|
import { Button } from "primereact/button";
|
|
10
|
-
import settings from "
|
|
10
|
+
import settings from "../../../settings";
|
|
11
11
|
|
|
12
12
|
const ActivityQuickActions: FC<{}> = () => {
|
|
13
13
|
const quickActions = useAppSelector(selectQuickActions);
|
|
@@ -3,22 +3,22 @@ import styles from "./style.module.scss";
|
|
|
3
3
|
|
|
4
4
|
import { Button } from "primereact/button";
|
|
5
5
|
import { Sidebar } from "primereact/sidebar";
|
|
6
|
-
import SettingsSidebarContent from "
|
|
7
|
-
import settings from "
|
|
6
|
+
import SettingsSidebarContent from "../sidebars/SettingsSidebarContent";
|
|
7
|
+
import settings from "../../../settings";
|
|
8
8
|
import ActivityQuickActions from "./ActivityQuickActions";
|
|
9
|
-
import useFullscreen from "
|
|
10
|
-
import { useActivityJS } from "
|
|
9
|
+
import useFullscreen from "../../../utils/useFullscreen";
|
|
10
|
+
import { useActivityJS } from "../../activityJS/ActivityJSProvider";
|
|
11
11
|
import { Dialog } from "primereact/dialog";
|
|
12
12
|
import capytaleUI from "@capytale/activity.js/backend/capytale/ui";
|
|
13
|
-
import { useAppSelector } from "
|
|
14
|
-
import { selectHasAntiCheat } from "
|
|
15
|
-
import AttachedFilesSidebarContent from "
|
|
13
|
+
import { useAppSelector } from "../../../app/hooks";
|
|
14
|
+
import { selectHasAntiCheat } from "../../activityData/activityDataSlice";
|
|
15
|
+
import AttachedFilesSidebarContent from "../sidebars/AttachedFilesSidebarContent";
|
|
16
16
|
import { Badge } from "primereact/badge";
|
|
17
|
-
import { useAttachedFiles } from "
|
|
17
|
+
import { useAttachedFiles } from "../../functionalities/hooks";
|
|
18
18
|
import {
|
|
19
19
|
selectAttachedFilesEnabled,
|
|
20
20
|
selectAttachedFilesOptions,
|
|
21
|
-
} from "
|
|
21
|
+
} from "../../functionalities/functionalitiesSlice";
|
|
22
22
|
|
|
23
23
|
const ActivityMenu: React.FC = memo(() => {
|
|
24
24
|
const [settingsSidebarVisible, setSettingsSidebarVisible] = useState(false);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { FC, ReactNode, useCallback, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { useInterval } from "primereact/hooks";
|
|
3
3
|
import { Toast } from "primereact/toast";
|
|
4
|
-
import { useActivityJS } from "
|
|
5
|
-
import { useAppSelector } from "
|
|
6
|
-
import { selectIsDirty, selectMode } from "
|
|
4
|
+
import { useActivityJS } from "../../activityJS/ActivityJSProvider";
|
|
5
|
+
import { useAppSelector } from "../../../app/hooks";
|
|
6
|
+
import { selectIsDirty, selectMode } from "../../activityData/activityDataSlice";
|
|
7
7
|
import { Button } from "primereact/button";
|
|
8
|
-
import { useSave } from "
|
|
9
|
-
import serverClock from "
|
|
8
|
+
import { useSave } from "../../activityData/hooks";
|
|
9
|
+
import serverClock from "../../../utils/server-clock";
|
|
10
10
|
|
|
11
11
|
// TODO use https://capytale2.ac-paris.fr/vanilla/time-s.php
|
|
12
12
|
// https://forge.apps.education.fr/capytale/activity-js/-/blob/main/src/backend/capytale/clock.ts?ref_type=heads
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { FC, useMemo, useState } from "react";
|
|
2
|
+
import Countdown from "./Countdown";
|
|
3
|
+
import { Button } from "primereact/button";
|
|
4
|
+
import { Dialog } from "primereact/dialog";
|
|
5
|
+
import { ML } from "../../../utils/breakpoints";
|
|
6
|
+
import { useAppDispatch, useAppSelector } from "../../../app/hooks";
|
|
7
|
+
import {
|
|
8
|
+
selectHasEvaluations,
|
|
9
|
+
selectIsDirty,
|
|
10
|
+
selectMode,
|
|
11
|
+
selectPreventEditIfHasEvaluations,
|
|
12
|
+
selectSaveState,
|
|
13
|
+
selectShowSaveButton,
|
|
14
|
+
setSaveState,
|
|
15
|
+
} from "../../activityData/activityDataSlice";
|
|
16
|
+
import { useWindowSize } from "@uidotdev/usehooks";
|
|
17
|
+
import { selectShowSaveForStudents } from "../../layout/layoutSlice";
|
|
18
|
+
import settings from "../../../settings";
|
|
19
|
+
|
|
20
|
+
const CountdownAndSaveButton: FC = () => {
|
|
21
|
+
const dispatch = useAppDispatch();
|
|
22
|
+
const saveState = useAppSelector(selectSaveState);
|
|
23
|
+
const windowsSize = useWindowSize();
|
|
24
|
+
const isQuiteSmall = useMemo(
|
|
25
|
+
() => windowsSize.width && windowsSize.width < ML,
|
|
26
|
+
[windowsSize.width],
|
|
27
|
+
);
|
|
28
|
+
const isDirty = useAppSelector(selectIsDirty);
|
|
29
|
+
const preventEditIfHasEvaluations = useAppSelector(
|
|
30
|
+
selectPreventEditIfHasEvaluations,
|
|
31
|
+
);
|
|
32
|
+
const hasEvaluations = useAppSelector(selectHasEvaluations);
|
|
33
|
+
|
|
34
|
+
const mode = useAppSelector(selectMode);
|
|
35
|
+
const showSaveButton = useAppSelector(selectShowSaveButton);
|
|
36
|
+
const showSaveForStudents = useAppSelector(selectShowSaveForStudents);
|
|
37
|
+
const hasSaveButton =
|
|
38
|
+
showSaveButton && !(mode === "assignment" && !showSaveForStudents);
|
|
39
|
+
|
|
40
|
+
const [editForbiddenDialogVisible, setEditForbiddenDialogVisible] =
|
|
41
|
+
useState(false);
|
|
42
|
+
|
|
43
|
+
if (!hasSaveButton) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (mode === "create" && hasEvaluations && preventEditIfHasEvaluations) {
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<Button
|
|
51
|
+
label={isQuiteSmall ? undefined : "Modification interdite"}
|
|
52
|
+
aria-label="Modification interdite"
|
|
53
|
+
severity="danger"
|
|
54
|
+
size="small"
|
|
55
|
+
icon="pi pi-save"
|
|
56
|
+
loading={saveState !== "idle"}
|
|
57
|
+
onClick={() => {
|
|
58
|
+
setEditForbiddenDialogVisible(true);
|
|
59
|
+
}}
|
|
60
|
+
tooltip={"Évaluations déjà attribuées"}
|
|
61
|
+
tooltipOptions={{
|
|
62
|
+
position: "bottom",
|
|
63
|
+
showDelay: settings.TOOLTIP_SHOW_DELAY,
|
|
64
|
+
showOnDisabled: true,
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
<Dialog
|
|
68
|
+
header="Modification interdite"
|
|
69
|
+
visible={editForbiddenDialogVisible}
|
|
70
|
+
onHide={() => {
|
|
71
|
+
if (!editForbiddenDialogVisible) return;
|
|
72
|
+
setEditForbiddenDialogVisible(false);
|
|
73
|
+
}}
|
|
74
|
+
style={{ maxWidth: "35rem" }}
|
|
75
|
+
>
|
|
76
|
+
<p className="m-0">
|
|
77
|
+
Vous avez déjà donné des évaluations à vos élèves. Ce type
|
|
78
|
+
d'activité ne supporte pas la modification dans ce cas car cela
|
|
79
|
+
rendrait les résultats des élèves incohérents.
|
|
80
|
+
</p>
|
|
81
|
+
<p className="m-0 mt-1">
|
|
82
|
+
Si vous souhaitez modifier cette activité, vous pouvez la clôner et
|
|
83
|
+
modifier la copie, que vous pouvez ensuite proposer à vos élèves.
|
|
84
|
+
</p>
|
|
85
|
+
</Dialog>
|
|
86
|
+
</>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<>
|
|
92
|
+
<Countdown />
|
|
93
|
+
<Button
|
|
94
|
+
label={isQuiteSmall ? undefined : "Enregistrer"}
|
|
95
|
+
aria-label="Enregistrer"
|
|
96
|
+
disabled={!isDirty}
|
|
97
|
+
severity={"warning"}
|
|
98
|
+
size="small"
|
|
99
|
+
icon="pi pi-save"
|
|
100
|
+
loading={saveState !== "idle"}
|
|
101
|
+
onClick={() => {
|
|
102
|
+
dispatch(setSaveState("should-save"));
|
|
103
|
+
}} /**
|
|
104
|
+
tooltip={isDirty ? "Enregistrer l'activité" : "Rien à enregistrer"}
|
|
105
|
+
tooltipOptions={{
|
|
106
|
+
position: "bottom",
|
|
107
|
+
showDelay: settings.TOOLTIP_SHOW_DELAY,
|
|
108
|
+
showOnDisabled: true,
|
|
109
|
+
}} */
|
|
110
|
+
/>
|
|
111
|
+
</>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default CountdownAndSaveButton;
|
|
@@ -7,52 +7,36 @@ import { OverlayPanel } from "primereact/overlaypanel";
|
|
|
7
7
|
import styles from "./style.module.scss";
|
|
8
8
|
import { useMemo, useRef } from "react";
|
|
9
9
|
import { useWindowSize } from "@uidotdev/usehooks";
|
|
10
|
-
import {
|
|
11
|
-
import { useAppDispatch, useAppSelector } from "
|
|
10
|
+
import { XL } from "../../../utils/breakpoints";
|
|
11
|
+
import { useAppDispatch, useAppSelector } from "../../../app/hooks";
|
|
12
12
|
import {
|
|
13
|
-
selectIsDirty,
|
|
14
13
|
selectMode,
|
|
15
|
-
selectSaveState,
|
|
16
14
|
selectSharingInfo,
|
|
17
|
-
selectShowSaveButton,
|
|
18
15
|
selectWorkflow,
|
|
19
16
|
setSaveState,
|
|
20
|
-
} from "
|
|
21
|
-
import { copyToClipboard } from "
|
|
22
|
-
import settings from "
|
|
17
|
+
} from "../../activityData/activityDataSlice";
|
|
18
|
+
import { copyToClipboard } from "../../../utils/clipboard";
|
|
19
|
+
import settings from "../../../settings";
|
|
23
20
|
import { wf } from "@capytale/activity.js/activity/field/workflow";
|
|
24
21
|
import capytaleUI from "@capytale/activity.js/backend/capytale/ui";
|
|
25
|
-
import ButtonDoubleIcon from "
|
|
26
|
-
import CardSelector from "
|
|
27
|
-
import { useActivityJS } from "
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
selectShowWorkflow,
|
|
31
|
-
} from "../layout/layoutSlice";
|
|
32
|
-
import Countdown from "./Countdown";
|
|
22
|
+
import ButtonDoubleIcon from "../../../utils/ButtonDoubleIcon";
|
|
23
|
+
import CardSelector from "../../../utils/CardSelector";
|
|
24
|
+
import { useActivityJS } from "../../activityJS/ActivityJSProvider";
|
|
25
|
+
import { selectShowWorkflow } from "../../layout/layoutSlice";
|
|
26
|
+
import CountdownAndSaveButton from "./CountdownAndSaveButton";
|
|
33
27
|
|
|
34
28
|
const CapytaleMenu: React.FC = () => {
|
|
35
29
|
const dispatch = useAppDispatch();
|
|
36
30
|
const activityJS = useActivityJS();
|
|
37
31
|
const sharingInfo = useAppSelector(selectSharingInfo);
|
|
38
|
-
const saveState = useAppSelector(selectSaveState);
|
|
39
32
|
const windowsSize = useWindowSize();
|
|
40
33
|
const mode = useAppSelector(selectMode);
|
|
41
34
|
const workflow = useAppSelector(selectWorkflow);
|
|
42
|
-
const isDirty = useAppSelector(selectIsDirty);
|
|
43
|
-
const showSaveButton = useAppSelector(selectShowSaveButton);
|
|
44
|
-
const showSaveForStudents = useAppSelector(selectShowSaveForStudents);
|
|
45
|
-
const hasSaveButton =
|
|
46
|
-
showSaveButton && !(mode === "assignment" && !showSaveForStudents);
|
|
47
35
|
const showWorkflow = useAppSelector(selectShowWorkflow);
|
|
48
36
|
const isLarge = useMemo(
|
|
49
37
|
() => windowsSize.width && windowsSize.width >= XL,
|
|
50
38
|
[windowsSize.width],
|
|
51
39
|
);
|
|
52
|
-
const isQuiteSmall = useMemo(
|
|
53
|
-
() => windowsSize.width && windowsSize.width < ML,
|
|
54
|
-
[windowsSize.width],
|
|
55
|
-
);
|
|
56
40
|
const isInIframe = useMemo(() => capytaleUI.isInCapytaletIframe(), []);
|
|
57
41
|
const toast = useRef<Toast>(null);
|
|
58
42
|
const changeWorkflow = (value: wf) => {
|
|
@@ -91,31 +75,7 @@ const CapytaleMenu: React.FC = () => {
|
|
|
91
75
|
}}
|
|
92
76
|
/>
|
|
93
77
|
)}
|
|
94
|
-
|
|
95
|
-
hasSaveButton && (
|
|
96
|
-
<>
|
|
97
|
-
<Countdown />
|
|
98
|
-
<Button
|
|
99
|
-
label={isQuiteSmall ? undefined : "Enregistrer"}
|
|
100
|
-
aria-label="Enregistrer"
|
|
101
|
-
disabled={!isDirty}
|
|
102
|
-
severity={"warning"}
|
|
103
|
-
size="small"
|
|
104
|
-
icon="pi pi-save"
|
|
105
|
-
loading={saveState !== "idle"}
|
|
106
|
-
onClick={() => {
|
|
107
|
-
dispatch(setSaveState("should-save"));
|
|
108
|
-
}}
|
|
109
|
-
/>
|
|
110
|
-
</>
|
|
111
|
-
) /**
|
|
112
|
-
tooltip={isDirty ? "Enregistrer l'activité" : "Rien à enregistrer"}
|
|
113
|
-
tooltipOptions={{
|
|
114
|
-
position: "bottom",
|
|
115
|
-
showDelay: settings.TOOLTIP_SHOW_DELAY,
|
|
116
|
-
showOnDisabled: true,
|
|
117
|
-
}} */
|
|
118
|
-
}
|
|
78
|
+
<CountdownAndSaveButton />
|
|
119
79
|
|
|
120
80
|
{mode === "create" && sharingInfo.code && (
|
|
121
81
|
<ButtonGroup>
|
|
@@ -172,6 +132,7 @@ const CapytaleMenu: React.FC = () => {
|
|
|
172
132
|
{mode === "assignment" && workflow === "current" && (
|
|
173
133
|
<Button
|
|
174
134
|
aria-label="Rendre la copie"
|
|
135
|
+
size="small"
|
|
175
136
|
outlined
|
|
176
137
|
label="Rendre"
|
|
177
138
|
icon="pi pi-envelope"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { MD } from "../../utils/breakpoints";
|
|
2
2
|
import { useWindowSize } from "@uidotdev/usehooks";
|
|
3
|
-
import ActivityInfo from "./
|
|
4
|
-
import ActivityMenu from "./
|
|
5
|
-
import CapytaleMenu from "./
|
|
3
|
+
import ActivityInfo from "./activity-info";
|
|
4
|
+
import ActivityMenu from "./activity-menu";
|
|
5
|
+
import CapytaleMenu from "./capytale-menu";
|
|
6
6
|
import styles from "./style.module.scss";
|
|
7
7
|
import { useMemo } from "react";
|
|
8
8
|
import { useAppSelector } from "../../app/hooks";
|
|
@@ -2,19 +2,21 @@ import { useState, useEffect, useMemo } from "react";
|
|
|
2
2
|
import evalApi from "@capytale/activity.js/backend/capytale/evaluation";
|
|
3
3
|
import { Dropdown } from "primereact/dropdown";
|
|
4
4
|
|
|
5
|
-
import { useAppSelector } from "
|
|
5
|
+
import { useAppSelector } from "../../../app/hooks";
|
|
6
6
|
import {
|
|
7
|
+
selectActivityInfo,
|
|
7
8
|
selectActivityNid,
|
|
8
9
|
selectMode,
|
|
9
10
|
selectNid,
|
|
10
|
-
} from "
|
|
11
|
-
import { studentNameFromInfo } from "
|
|
11
|
+
} from "../../activityData/activityDataSlice";
|
|
12
|
+
import { studentNameFromInfo } from "../student-utils";
|
|
12
13
|
|
|
13
14
|
import { SaInfo } from "@capytale/activity.js/activity/evaluation/evaluation";
|
|
14
15
|
import { SelectItem, SelectItemOptionsType } from "primereact/selectitem";
|
|
15
16
|
import { Button } from "primereact/button";
|
|
16
17
|
|
|
17
18
|
const GradingNav: React.FC = () => {
|
|
19
|
+
const activityInfo = useAppSelector(selectActivityInfo);
|
|
18
20
|
const [studentList, setStudentList] = useState<SaInfo[]>([]);
|
|
19
21
|
const nid = useAppSelector(selectNid) as number;
|
|
20
22
|
const mode = useAppSelector(selectMode);
|
|
@@ -71,7 +73,7 @@ const GradingNav: React.FC = () => {
|
|
|
71
73
|
}, [studentList]);
|
|
72
74
|
|
|
73
75
|
if (studentList.length === 0) {
|
|
74
|
-
return <
|
|
76
|
+
return <span>{studentNameFromInfo(activityInfo.studentInfo)}</span>;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
const firstNid = studentList[0].nid as number;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.reviewNavbar {
|
|
2
|
+
color-scheme: dark;
|
|
3
|
+
background-color: var(--surface-500);
|
|
4
|
+
--review-navbar-text-color: rgba(255, 255, 255, 0.95);
|
|
5
|
+
color: var(--review-navbar-text-color);
|
|
6
|
+
padding: 4px 16px;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
& :global(.p-button-secondary) {
|
|
11
|
+
border-color: var(--review-navbar-text-color);
|
|
12
|
+
color: var(--review-navbar-text-color);
|
|
13
|
+
}
|
|
14
|
+
& :global(.p-dropdown) {
|
|
15
|
+
border-color: var(--review-navbar-text-color);
|
|
16
|
+
border-left-width: 0;
|
|
17
|
+
border-right-width: 0;
|
|
18
|
+
}
|
|
19
|
+
:global(.dark-theme) & {
|
|
20
|
+
background-color: var(--surface-100);
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/features/navbar/{ActivitySidebarActions.tsx → sidebars/ActivitySidebarActions.tsx}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { FC, useEffect } from "react";
|
|
2
|
-
import { useAppDispatch, useAppSelector } from "
|
|
2
|
+
import { useAppDispatch, useAppSelector } from "../../../app/hooks";
|
|
3
3
|
import {
|
|
4
4
|
QuickAction,
|
|
5
5
|
selectSidebarActions,
|
|
6
6
|
setSidebarActions,
|
|
7
7
|
usePerformQuickAction,
|
|
8
|
-
} from "
|
|
8
|
+
} from "../navbarSlice";
|
|
9
9
|
import { Button } from "primereact/button";
|
|
10
|
-
import settings from "
|
|
10
|
+
import settings from "../../../settings";
|
|
11
11
|
|
|
12
12
|
const ActivitySidebarActions: FC<{}> = () => {
|
|
13
13
|
const sidebarActions = useAppSelector(selectSidebarActions);
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { FC, useRef } from "react";
|
|
2
|
-
import { useAttachedFiles } from "
|
|
3
|
-
import { useAppSelector } from "
|
|
2
|
+
import { useAttachedFiles } from "../../functionalities/hooks";
|
|
3
|
+
import { useAppSelector } from "../../../app/hooks";
|
|
4
4
|
import {
|
|
5
5
|
AttachedFileData,
|
|
6
6
|
selectAttachedFilesOptions,
|
|
7
|
-
} from "
|
|
7
|
+
} from "../../functionalities/functionalitiesSlice";
|
|
8
8
|
import { Button } from "primereact/button";
|
|
9
9
|
import { Toast } from "primereact/toast";
|
|
10
10
|
|
|
11
11
|
import styles from "./AttachedFilesSidebarContent.module.scss";
|
|
12
|
-
import { copyToClipboard } from "
|
|
13
|
-
import { downloadFile } from "
|
|
12
|
+
import { copyToClipboard } from "../../../utils/clipboard";
|
|
13
|
+
import { downloadFile } from "../../../utils/download";
|
|
14
14
|
import { useFileUpload } from "use-file-upload";
|
|
15
15
|
|
|
16
16
|
const AttachedFilesSidebarContent: FC = () => {
|
package/src/features/navbar/{SettingsSidebarContent.tsx → sidebars/SettingsSidebarContent.tsx}
RENAMED
|
@@ -2,8 +2,8 @@ import { FC } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { Fieldset } from "primereact/fieldset";
|
|
4
4
|
|
|
5
|
-
import { selectOrientation, setLayout } from "
|
|
6
|
-
import { useAppDispatch, useAppSelector } from "
|
|
5
|
+
import { selectOrientation, setLayout } from "../../layout/layoutSlice";
|
|
6
|
+
import { useAppDispatch, useAppSelector } from "../../../app/hooks";
|
|
7
7
|
import { RadioButton } from "primereact/radiobutton";
|
|
8
8
|
|
|
9
9
|
import styles from "./style.module.scss";
|
|
@@ -11,19 +11,19 @@ import {
|
|
|
11
11
|
selectThemeNameOrAuto,
|
|
12
12
|
setAutoSwitch,
|
|
13
13
|
switchToTheme,
|
|
14
|
-
} from "
|
|
15
|
-
import { selectSidebarActions } from "
|
|
14
|
+
} from "../../theming/themingSlice";
|
|
15
|
+
import { selectSidebarActions } from "../navbarSlice";
|
|
16
16
|
import ActivitySidebarActions from "./ActivitySidebarActions";
|
|
17
17
|
import { classNames } from "primereact/utils";
|
|
18
18
|
import {
|
|
19
19
|
selectCanChoosePedagoLayout,
|
|
20
20
|
selectCanChooseTheme,
|
|
21
|
-
} from "
|
|
22
|
-
import { ActivitySettingsDisplay } from "
|
|
21
|
+
} from "../../activityData/activityDataSlice";
|
|
22
|
+
import { ActivitySettingsDisplay } from "../../activitySettings";
|
|
23
23
|
import { Button } from "primereact/button";
|
|
24
24
|
import { ConfirmPopup, confirmPopup } from "primereact/confirmpopup";
|
|
25
|
-
import settings from "
|
|
26
|
-
import { useReset } from "
|
|
25
|
+
import settings from "../../../settings";
|
|
26
|
+
import { useReset } from "../../activityJS/hooks";
|
|
27
27
|
|
|
28
28
|
type SettingsSidebarContentProps = {
|
|
29
29
|
showHelp?: () => void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.sidebarCapytaleActions {
|
|
2
|
+
margin-bottom: 8px;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: 8px;
|
|
6
|
+
& > button {
|
|
7
|
+
width: 100%;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.sidebarFieldsetButtons :global(.p-fieldset-content) {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: 6px;
|
|
15
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StudentInfo } from "../activityData/
|
|
1
|
+
import { StudentInfo } from "../activityData/activityJsData";
|
|
2
2
|
|
|
3
3
|
export function studentNameFromInfo(studentInfo: StudentInfo | null) {
|
|
4
4
|
if (!studentInfo || (!studentInfo.firstName && !studentInfo.lastName)) {
|
|
@@ -31,29 +31,6 @@
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
.reviewNavbar {
|
|
35
|
-
color-scheme: dark;
|
|
36
|
-
background-color: var(--surface-500);
|
|
37
|
-
--review-navbar-text-color: rgba(255, 255, 255, 0.95);
|
|
38
|
-
color: var(--review-navbar-text-color);
|
|
39
|
-
padding: 4px 16px;
|
|
40
|
-
display: flex;
|
|
41
|
-
align-items: center;
|
|
42
|
-
justify-content: space-between;
|
|
43
|
-
& :global(.p-button-secondary) {
|
|
44
|
-
border-color: var(--review-navbar-text-color);
|
|
45
|
-
color: var(--review-navbar-text-color);
|
|
46
|
-
}
|
|
47
|
-
& :global(.p-dropdown) {
|
|
48
|
-
border-color: var(--review-navbar-text-color);
|
|
49
|
-
border-left-width: 0;
|
|
50
|
-
border-right-width: 0;
|
|
51
|
-
}
|
|
52
|
-
:global(.dark-theme) & {
|
|
53
|
-
background-color: var(--surface-100);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
34
|
.navbarContainer {
|
|
58
35
|
height: 60px;
|
|
59
36
|
flex-grow: 1;
|
|
@@ -86,76 +63,3 @@
|
|
|
86
63
|
}
|
|
87
64
|
}
|
|
88
65
|
}
|
|
89
|
-
|
|
90
|
-
.capytaleMenu {
|
|
91
|
-
align-items: center;
|
|
92
|
-
display: flex;
|
|
93
|
-
gap: 8px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.activityInfo {
|
|
97
|
-
flex-shrink: 1;
|
|
98
|
-
flex-grow: 1;
|
|
99
|
-
display: flex;
|
|
100
|
-
justify-content: center;
|
|
101
|
-
align-items: center;
|
|
102
|
-
overflow: hidden;
|
|
103
|
-
gap: 12px;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.activityInfoText {
|
|
107
|
-
display: flex;
|
|
108
|
-
flex-direction: column;
|
|
109
|
-
justify-content: center;
|
|
110
|
-
align-items: center;
|
|
111
|
-
overflow: hidden;
|
|
112
|
-
& > * {
|
|
113
|
-
overflow: hidden;
|
|
114
|
-
text-overflow: ellipsis;
|
|
115
|
-
white-space: nowrap;
|
|
116
|
-
max-width: 100%;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.activityInfoTitle {
|
|
121
|
-
font-size: 20px;
|
|
122
|
-
font-weight: 500;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.activityLogo {
|
|
126
|
-
width: 35px;
|
|
127
|
-
height: 37px;
|
|
128
|
-
border-radius: 6px;
|
|
129
|
-
flex-shrink: 0;
|
|
130
|
-
@media only screen and (max-width: 992px) {
|
|
131
|
-
display: none;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.activityMenu {
|
|
136
|
-
padding-right: 16px;
|
|
137
|
-
display: flex;
|
|
138
|
-
gap: 8px;
|
|
139
|
-
justify-content: center;
|
|
140
|
-
align-items: center;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.sidebarFieldsetButtons :global(.p-fieldset-content) {
|
|
144
|
-
display: flex;
|
|
145
|
-
flex-direction: column;
|
|
146
|
-
gap: 6px;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.sidebarCapytaleActions {
|
|
150
|
-
margin-bottom: 8px;
|
|
151
|
-
display: flex;
|
|
152
|
-
flex-direction: column;
|
|
153
|
-
gap: 8px;
|
|
154
|
-
& > button {
|
|
155
|
-
width: 100%;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.overlayPanelWorkflowTitle {
|
|
160
|
-
margin-top: 0;
|
|
161
|
-
}
|
|
@@ -215,6 +215,8 @@ const HorizontalDocumentSelector = () => {
|
|
|
215
215
|
{items.map((item) => (
|
|
216
216
|
<div
|
|
217
217
|
className={styles.pedagoHorizontalTab}
|
|
218
|
+
role="button"
|
|
219
|
+
aria-label={`Basculer vers l'onglet ${item.name}`}
|
|
218
220
|
onClick={() => dispatch(setPedagoTab(item.value))}
|
|
219
221
|
data-selected={item.value === pedagoTab}
|
|
220
222
|
key={item.value}
|
|
@@ -57,6 +57,13 @@ const Pedago: React.FC<DivProps> = ({ className, ...props }) => {
|
|
|
57
57
|
<PedagoCommands />
|
|
58
58
|
<div
|
|
59
59
|
className={styles.pedagoContent}
|
|
60
|
+
aria-label={
|
|
61
|
+
pedagoTab === "instructions"
|
|
62
|
+
? "Consignes"
|
|
63
|
+
: pedagoTab === "pdf"
|
|
64
|
+
? "PDF"
|
|
65
|
+
: "Notes partagées"
|
|
66
|
+
}
|
|
60
67
|
data-grading-visible={isGradingVisible}
|
|
61
68
|
>
|
|
62
69
|
{!isGradingVisible && (
|
|
@@ -90,6 +97,13 @@ const Pedago: React.FC<DivProps> = ({ className, ...props }) => {
|
|
|
90
97
|
>
|
|
91
98
|
<Panel
|
|
92
99
|
className={styles.fullSizePanel}
|
|
100
|
+
aria-label={
|
|
101
|
+
pedagoTab === "instructions"
|
|
102
|
+
? "Consignes"
|
|
103
|
+
: pedagoTab === "pdf"
|
|
104
|
+
? "PDF"
|
|
105
|
+
: "Notes partagées"
|
|
106
|
+
}
|
|
93
107
|
header={
|
|
94
108
|
pedagoTab === "instructions"
|
|
95
109
|
? "Consignes"
|
package/src/index.tsx
CHANGED
|
@@ -13,17 +13,21 @@ import {
|
|
|
13
13
|
useActivityJS,
|
|
14
14
|
useActivityJsEssentials,
|
|
15
15
|
} from "./features/activityJS/ActivityJSProvider";
|
|
16
|
-
import { useNotifyIsDirty, useSave } from "./features/activityData/hooks";
|
|
16
|
+
import { useNotifyIsDirty, useCanSave, useSave } from "./features/activityData/hooks";
|
|
17
17
|
import { useOrientation } from "./features/layout/hooks";
|
|
18
|
-
import { ActivitySidebarActionsSetter } from "./features/navbar/ActivitySidebarActions";
|
|
19
|
-
import { ActivityQuickActionsSetter } from "./features/navbar/ActivityQuickActions";
|
|
18
|
+
import { ActivitySidebarActionsSetter } from "./features/navbar/sidebars/ActivitySidebarActions";
|
|
19
|
+
import { ActivityQuickActionsSetter } from "./features/navbar/activity-menu/ActivityQuickActions";
|
|
20
20
|
import ActivitySettingsSetter from "./features/activitySettings/ActivitySettingsSetter";
|
|
21
21
|
import IsDirtySetter from "./features/activityData/IsDirtySetter";
|
|
22
22
|
import AttachedFilesFunctionality from "./features/functionalities/AttachedFilesFunctionality";
|
|
23
23
|
import { useRefreshAttachedFiles } from "./features/functionalities/hooks";
|
|
24
|
+
import { useActivitySettings } from "./features/activitySettings/hooks";
|
|
24
25
|
import { Toast } from "./external/prime";
|
|
25
26
|
import type { ToastMessage } from "./external/prime";
|
|
26
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
AttachedFileData,
|
|
29
|
+
UploadedFileInfo,
|
|
30
|
+
} from "./features/functionalities/functionalitiesSlice";
|
|
27
31
|
|
|
28
32
|
export {
|
|
29
33
|
MetaPlayer,
|
|
@@ -37,9 +41,11 @@ export {
|
|
|
37
41
|
useActivityJS,
|
|
38
42
|
useActivityJsEssentials,
|
|
39
43
|
useNotifyIsDirty,
|
|
44
|
+
useCanSave,
|
|
40
45
|
useSave,
|
|
41
46
|
useOrientation,
|
|
42
47
|
useThemeType,
|
|
48
|
+
useActivitySettings,
|
|
43
49
|
ActivitySidebarActionsSetter,
|
|
44
50
|
ActivityQuickActionsSetter,
|
|
45
51
|
ActivitySettingsSetter,
|
|
File without changes
|