Skip to content

Commit b4a04d3

Browse files
committed
fix: add new group and homework to mentor filter after creation
1 parent 3406795 commit b4a04d3

6 files changed

Lines changed: 157 additions & 49 deletions

File tree

hwproj.front/src/components/Common/GroupSelector.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ interface GroupSelectorProps {
2020
courseStudents: AccountDataDto[],
2121
groups: GroupViewModel[],
2222
onGroupIdChange: (groupId?: number) => void,
23-
onGroupsUpdate: () => void,
23+
onGroupsUpdate: () => Promise<void>,
2424
selectedGroupId?: number,
2525
choiceDisabled?: boolean,
26-
onCreateNewGroup?: () => void,
26+
onCreateNewGroup?: (group: GroupViewModel) => Promise<void>,
2727
}
2828

2929
const GroupSelector: FC<GroupSelectorProps> = (props) => {
@@ -80,14 +80,19 @@ const GroupSelector: FC<GroupSelectorProps> = (props) => {
8080
groupMates: formState.memberIds.map(studentId => ({studentId})),
8181
}
8282
);
83-
props.onGroupsUpdate();
83+
await props.onGroupsUpdate();
8484
} else {
8585
const groupId = await ApiSingleton.courseGroupsApi.courseGroupsCreateCourseGroup(props.courseId, {
8686
name: formState.name.trim(),
8787
groupMatesIds: formState.memberIds,
8888
courseId: props.courseId,
8989
});
90-
props.onGroupsUpdate();
90+
await props.onCreateNewGroup?.({
91+
id: groupId,
92+
name: formState.name.trim(),
93+
studentsIds: formState.memberIds,
94+
});
95+
await props.onGroupsUpdate();
9196
props.onGroupIdChange(groupId);
9297
}
9398
} catch (error) {

hwproj.front/src/components/Courses/Course.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,10 @@ const Course: React.FC = () => {
9393
groups
9494
} = courseState
9595

96-
const loadGroups = async () => {
97-
const groups = await ApiSingleton.courseGroupsApi.courseGroupsGetAllCourseGroups(course.id!)
96+
const loadGroups = async (targetCourseId: number = course.id!) => {
97+
if (!targetCourseId) return;
98+
99+
const groups = await ApiSingleton.courseGroupsApi.courseGroupsGetAllCourseGroups(targetCourseId)
98100
setCourseState(prevState => ({
99101
...prevState,
100102
groups: groups
@@ -153,15 +155,15 @@ const Course: React.FC = () => {
153155
courseHomeworks: course.homeworks!,
154156
createHomework: false,
155157
mentors: course.mentors!,
156-
groups: course.groups || [],
157158
acceptedStudents: course.acceptedStudents!,
158159
newStudents: course.newStudents!,
159160
}))
160161
}
161162

162163
useEffect(() => {
163164
setCurrentState()
164-
}, [])
165+
loadGroups(+courseId!)
166+
}, [courseId])
165167

166168
useEffect(() => {
167169
ApiSingleton.statisticsApi.statisticsGetCourseStatistics(+courseId!)

hwproj.front/src/components/Courses/CourseExperimental.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ interface ICourseExperimentalProps {
6161
previouslyExistingFilesCount: number,
6262
waitingNewFilesCount: number,
6363
deletingFilesIds: number[]) => void;
64-
onGroupsUpdate: () => void;
64+
onGroupsUpdate: () => Promise<void>;
6565
groups: GroupViewModel[];
6666
}
6767

@@ -428,6 +428,7 @@ export const CourseExperimental: FC<ICourseExperimentalProps> = (props) => {
428428
getAllHomeworks={() => homeworks}
429429
homeworkAndFilesInfo={{homework, filesInfo}}
430430
isMentor={isMentor}
431+
userId={props.userId}
431432
initialEditMode={initialEditMode || homeworkEditMode}
432433
onMount={onSelectedItemMount}
433434
onAddTask={addNewTask}

hwproj.front/src/components/Courses/CourseFilter.tsx

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ApiSingleton from "../../api/ApiSingleton";
77
import ErrorsHandler from "../Utils/ErrorsHandler";
88
import {DotLottieReact} from '@lottiefiles/dotlottie-react';
99
import Button from "@material-ui/core/Button";
10+
import {getSelectedCourseView} from "./MentorWorkspaceUtils";
1011

1112
interface ICourseFilterProps {
1213
courseId: number;
@@ -63,35 +64,11 @@ const CourseFilter: FC<ICourseFilterProps> = (props) => {
6364
const mentorWorkspace =
6465
await ApiSingleton.coursesApi.coursesGetMentorWorkspace(props.courseId, props.mentorId);
6566

66-
props.onSelectedStudentsChange(mentorWorkspace.students ?? [])
67-
props.onSelectedHomeworksChange(mentorWorkspace.homeworks ?? [])
68-
props.onSelectedGroupsChange(mentorWorkspace.groups ?? [])
69-
70-
const initSelectedGroupsView = (mentorWorkspace.groups?.length === course.groups?.length ?
71-
[] : (mentorWorkspace.groups ?? []))
72-
.filter(g => g.name?.trim());
73-
const selectedGroupsStudents = initSelectedGroupsView.flatMap(g =>g.studentsIds ?? []);
74-
75-
const selectedStudentWithoutGroups = mentorWorkspace.students?.filter(st => !selectedGroupsStudents.includes(st.userId!));
76-
// Для корректного отображения "Все" при инцициализации (получении данных с бэкенда)
77-
const allCourseStudentsCount = (course.acceptedStudents?.length ?? 0) + (course.newStudents?.length ?? 0);
78-
const initSelectedStudentsView = selectedStudentWithoutGroups?.length === allCourseStudentsCount ?
79-
[] : selectedStudentWithoutGroups ?? [];
80-
81-
const courseHomeworks = initSelectedGroupsView.length > 0
82-
? course.homeworks?.filter(h => !h.groupId || initSelectedGroupsView?.some(g => g.id === h.groupId))
83-
: course.homeworks;
84-
const initSelectedHomeworksView = mentorWorkspace.homeworks?.length === courseHomeworks?.length ?
85-
[] : mentorWorkspace.homeworks ?? [];
67+
const selectedCourseView = getSelectedCourseView(course, mentorWorkspace);
8668

8769
setState(prevState => ({
8870
...prevState,
89-
courseHomeworks: course.homeworks ?? [],
90-
courseStudents: course.acceptedStudents ?? [],
91-
courseGroups: course.groups?.filter(g => g.name?.trim()) ?? [],
92-
selectedStudents: initSelectedStudentsView.filter(s => !initSelectedGroupsView.some(g => g.studentsIds?.includes(s.userId!))),
93-
selectedHomeworks: initSelectedHomeworksView,
94-
selectedGroups: initSelectedGroupsView,
71+
...selectedCourseView,
9572
mentors: course.mentors!,
9673
assignedStudents: assignedStudents.filter(x => x.mentorId !== props.mentorId)
9774
}))
@@ -158,9 +135,9 @@ const CourseFilter: FC<ICourseFilterProps> = (props) => {
158135
fullWidth
159136
options={state.courseHomeworks.filter(h =>
160137
!h.groupId
161-
|| !state.selectedGroups
162-
|| state.selectedGroups.some(g => g.id === h.groupId))
163-
}
138+
|| state.selectedGroups.length === 0
139+
|| state.selectedGroups.some(g => g.id === h.groupId)
140+
)}
164141
getOptionLabel={(option: HomeworkViewModel) => option.title ?? "Без названия"}
165142
getOptionKey={(option: HomeworkViewModel) => option.id ?? 0}
166143
filterSelectedOptions
@@ -319,4 +296,4 @@ const CourseFilter: FC<ICourseFilterProps> = (props) => {
319296
)
320297
}
321298

322-
export default CourseFilter;
299+
export default CourseFilter;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {AccountDataDto, CourseViewModel, GroupViewModel, HomeworkViewModel, WorkspaceViewModel} from "@/api";
2+
3+
export interface SelectedCourseView {
4+
courseHomeworks: HomeworkViewModel[];
5+
courseStudents: AccountDataDto[];
6+
courseGroups: GroupViewModel[];
7+
selectedHomeworks: HomeworkViewModel[];
8+
selectedStudents: AccountDataDto[];
9+
selectedGroups: GroupViewModel[];
10+
}
11+
12+
export const getSelectedCourseView = (
13+
course: CourseViewModel,
14+
mentorWorkspace: WorkspaceViewModel
15+
): SelectedCourseView => {
16+
const courseGroups = course.groups?.filter(g => g.name?.trim()) ?? [];
17+
18+
const selectedGroups = (mentorWorkspace.groups?.length === courseGroups.length
19+
? []
20+
: mentorWorkspace.groups ?? []
21+
)
22+
.filter(g => g.name?.trim());
23+
24+
const selectedGroupsStudents = selectedGroups.flatMap(g => g.studentsIds ?? []);
25+
const selectedStudentWithoutGroups = mentorWorkspace.students
26+
?.filter(st => !selectedGroupsStudents.includes(st.userId!)) ?? [];
27+
const allCourseStudentsCount = (course.acceptedStudents?.length ?? 0) + (course.newStudents?.length ?? 0);
28+
const selectedStudents = selectedStudentWithoutGroups.length === allCourseStudentsCount
29+
? []
30+
: selectedStudentWithoutGroups;
31+
32+
const availableHomeworks = selectedGroups.length > 0
33+
? course.homeworks?.filter(h => !h.groupId || selectedGroups.some(g => g.id === h.groupId))
34+
: course.homeworks;
35+
const selectedHomeworks = mentorWorkspace.homeworks?.length === availableHomeworks?.length
36+
? []
37+
: mentorWorkspace.homeworks ?? [];
38+
39+
return {
40+
courseHomeworks: course.homeworks ?? [],
41+
courseStudents: course.acceptedStudents ?? [],
42+
courseGroups,
43+
selectedHomeworks,
44+
selectedStudents: selectedStudents.filter(s => !selectedGroups.some(g => g.studentsIds?.includes(s.userId!))),
45+
selectedGroups,
46+
};
47+
}

hwproj.front/src/components/Homeworks/CourseHomeworkExperimental.tsx

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {IFileInfo} from "components/Files/IFileInfo";
2121
import {FC, useEffect, useState} from "react"
2222
import Utils from "services/Utils";
2323
import {
24-
HomeworkViewModel, ActionOptions, HomeworkTaskViewModel, PostTaskViewModel, AccountDataDto, GroupViewModel
24+
HomeworkViewModel, ActionOptions, HomeworkTaskViewModel, PostTaskViewModel, AccountDataDto, GroupViewModel,
25+
WorkspaceViewModel, CourseViewModel
2526
} from "@/api";
2627
import ApiSingleton from "../../api/ApiSingleton";
2728
import Tags from "../Common/Tags";
@@ -43,6 +44,7 @@ import {FilesHandler} from "@/components/Files/FilesHandler";
4344
import GroupSelector from "../Common/GroupSelector";
4445
import GroupIcon from '@mui/icons-material/Group';
4546
import AssignmentIcon from '@mui/icons-material/Assignment';
47+
import {getSelectedCourseView} from "../Courses/MentorWorkspaceUtils";
4648

4749
export interface HomeworkAndFilesInfo {
4850
homework: HomeworkViewModel & { isModified?: boolean },
@@ -59,6 +61,7 @@ interface IEditHomeworkState {
5961

6062
const CourseHomeworkEditor: FC<{
6163
homeworkAndFilesInfo: HomeworkAndFilesInfo,
64+
mentorId: string,
6265
getAllHomeworks: () => HomeworkViewModel[],
6366
onUpdate: (update: { homework: HomeworkViewModel } & {
6467
isDeleted?: boolean,
@@ -69,7 +72,7 @@ const CourseHomeworkEditor: FC<{
6972
previouslyExistingFilesCount: number,
7073
waitingNewFilesCount: number,
7174
deletingFilesIds: number[]) => void;
72-
onGroupsUpdate: () => void;
75+
onGroupsUpdate: () => Promise<void>;
7376
groups: GroupViewModel[];
7477
}> = (props) => {
7578
const homework = props.homeworkAndFilesInfo.homework
@@ -124,19 +127,29 @@ const CourseHomeworkEditor: FC<{
124127
const [description, setDescription] = useState<string>(loadedHomework.description!)
125128
const [selectedGroupId, setSelectedGroupId] = useState(loadedHomework.groupId)
126129
const [courseStudents, setCourseStudents] = useState<AccountDataDto[]>([])
130+
const [course, setCourse] = useState<CourseViewModel | undefined>(undefined)
131+
const [mentorWorkspace, setMentorWorkspace] = useState<WorkspaceViewModel | undefined>(undefined)
127132
const [page, setPage] = useState<"homework" | "group">("homework")
128133

129134
useEffect(() => {
130-
const loadCourseStudents = async () => {
135+
const loadMentorWorkspace = async () => {
131136
try {
132-
const courseData = await ApiSingleton.coursesApi.coursesGetAllCourseData(courseId)
137+
const [courseData, mentorWorkspace] = await Promise.all([
138+
ApiSingleton.coursesApi.coursesGetAllCourseData(courseId),
139+
isNewHomework
140+
? ApiSingleton.coursesApi.coursesGetMentorWorkspace(courseId, props.mentorId)
141+
.catch(() => undefined)
142+
: Promise.resolve(undefined)
143+
]);
133144
setCourseStudents(courseData.course?.acceptedStudents || [])
145+
setCourse(courseData.course)
146+
setMentorWorkspace(mentorWorkspace)
134147
} catch (error) {
135-
console.error('Failed to load course students:', error)
148+
console.error('Failed to load course data:', error)
136149
}
137150
}
138-
loadCourseStudents()
139-
}, [courseId])
151+
loadMentorWorkspace()
152+
}, [courseId, props.mentorId, isNewHomework])
140153

141154
const [hasErrors, setHasErrors] = useState<boolean>(false)
142155

@@ -227,6 +240,62 @@ const CourseHomeworkEditor: FC<{
227240
props.onUpdate({homework: loadedHomework, isDeleted: true})
228241
}
229242

243+
const updateMentorFilter = async (update: {
244+
newGroup?: GroupViewModel,
245+
newHomework?: HomeworkViewModel
246+
}) => {
247+
if (!course || !mentorWorkspace) return;
248+
249+
const {newGroup, newHomework} = update;
250+
const selectedCourseView = getSelectedCourseView(course, mentorWorkspace);
251+
let updatedGroups = selectedCourseView.selectedGroups;
252+
let updatedHomeworks = selectedCourseView.selectedHomeworks;
253+
let hasChanges = false;
254+
255+
if (newGroup?.id !== undefined &&
256+
selectedCourseView.selectedGroups.length > 0 &&
257+
!selectedCourseView.selectedGroups.some(g => g.id === newGroup.id)) {
258+
updatedGroups = [...selectedCourseView.selectedGroups, newGroup];
259+
hasChanges = true;
260+
}
261+
262+
if (newHomework?.id !== undefined &&
263+
selectedCourseView.selectedHomeworks.length > 0 &&
264+
!selectedCourseView.selectedHomeworks.some(h => h.id === newHomework.id)) {
265+
updatedHomeworks = [...selectedCourseView.selectedHomeworks, newHomework];
266+
hasChanges = true;
267+
}
268+
269+
setCourse(prev => prev ? ({
270+
...prev,
271+
groups: newGroup?.id !== undefined && !prev.groups?.some(g => g.id === newGroup.id)
272+
? [...(prev.groups ?? []), newGroup]
273+
: prev.groups,
274+
homeworks: newHomework?.id !== undefined && !prev.homeworks?.some(h => h.id === newHomework.id)
275+
? [...(prev.homeworks ?? []), newHomework]
276+
: prev.homeworks,
277+
}) : prev);
278+
279+
if (!hasChanges) return;
280+
281+
await ApiSingleton.coursesApi.coursesEditMentorWorkspace(
282+
courseId,
283+
props.mentorId,
284+
{
285+
homeworkIds: updatedHomeworks.map(h => h.id).filter((id): id is number => id !== undefined),
286+
studentIds: selectedCourseView.selectedStudents.map(s => s.userId).filter((id): id is string => id !== undefined),
287+
groupIds: updatedGroups.map(g => g.id).filter((id): id is number => id !== undefined),
288+
}
289+
);
290+
291+
setMentorWorkspace(prev => prev ? ({
292+
...prev,
293+
homeworks: updatedHomeworks,
294+
students: selectedCourseView.selectedStudents,
295+
groups: updatedGroups
296+
}) : prev);
297+
}
298+
230299
const getDeleteMessage = (homeworkName: string, filesInfo: IFileInfo[]) => {
231300
let message = `Вы точно хотите удалить задание "${homeworkName}"?`;
232301
if (filesInfo.length > 0) {
@@ -268,6 +337,10 @@ const CourseHomeworkEditor: FC<{
268337
? await ApiSingleton.homeworksApi.homeworksAddHomework(courseId!, update)
269338
: await ApiSingleton.homeworksApi.homeworksUpdateHomework(+homeworkId!, update)
270339

340+
if (isNewHomework && updatedHomework.value) {
341+
await updateMentorFilter({newHomework: updatedHomework.value});
342+
}
343+
271344
const updatedHomeworkId = updatedHomework.value!.id!
272345
await handleFilesChange(
273346
courseId, CourseUnitType.Homework, updatedHomeworkId,
@@ -430,7 +503,8 @@ const CourseHomeworkEditor: FC<{
430503
selectedGroupId={selectedGroupId}
431504
choiceDisabled={!isNewHomework}
432505
onGroupsUpdate={props.onGroupsUpdate}
433-
groups={props.groups}
506+
onCreateNewGroup={(newGroup: GroupViewModel) => updateMentorFilter({newGroup})}
507+
groups={mentorWorkspace?.groups ?? props.groups}
434508
/>
435509
</CardContent>
436510
{!isNewHomework && !isPublished &&
@@ -458,6 +532,7 @@ const CourseHomeworkExperimental: FC<{
458532
homeworkAndFilesInfo: HomeworkAndFilesInfo,
459533
getAllHomeworks: () => HomeworkViewModel[],
460534
isMentor: boolean,
535+
userId: string,
461536
initialEditMode: boolean,
462537
onMount: () => void,
463538
onUpdate: (x: { homework: HomeworkViewModel } & {
@@ -470,7 +545,7 @@ const CourseHomeworkExperimental: FC<{
470545
previouslyExistingFilesCount: number,
471546
waitingNewFilesCount: number,
472547
deletingFilesIds: number[]) => void;
473-
onGroupsUpdate: () => void;
548+
onGroupsUpdate: () => Promise<void>;
474549
groups: GroupViewModel[];
475550
}> = (props) => {
476551
const {homework, filesInfo} = props.homeworkAndFilesInfo
@@ -488,6 +563,7 @@ const CourseHomeworkExperimental: FC<{
488563
if (editMode) return <CourseHomeworkEditor
489564
getAllHomeworks={props.getAllHomeworks}
490565
homeworkAndFilesInfo={{homework, filesInfo}}
566+
mentorId={props.userId}
491567
onUpdate={update => {
492568
if (update.isSaved) setEditMode(false)
493569
props.onUpdate(update)
@@ -569,4 +645,4 @@ const CourseHomeworkExperimental: FC<{
569645
}
570646
</CardContent>
571647
}
572-
export default CourseHomeworkExperimental;
648+
export default CourseHomeworkExperimental;

0 commit comments

Comments
 (0)