Skip to content

Commit 78ac8bc

Browse files
committed
refactor; improve on-page search
1 parent ff7923d commit 78ac8bc

File tree

5 files changed

+74
-69
lines changed

5 files changed

+74
-69
lines changed

HwProj.APIGateway/HwProj.APIGateway.API/TableGenerators/ExcelGenerator.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ private static void AddTasksHeaders(ExcelWorksheet worksheet, CourseDTO course,
139139
}
140140

141141
worksheet.Cells[position.Row, position.Column].Value
142-
= $"{taskNumber.ToString()} {course.Homeworks[i].Tasks[j].Title}";
142+
= $"{taskNumber.ToString()}. {course.Homeworks[i].Tasks[j].Title}";
143143
worksheet.Cells[position.Row, position.Column, position.Row, position.Column + 2].Merge = true;
144144
position.Column += 3;
145145
++taskNumber;
@@ -209,8 +209,7 @@ private static void AddCourseMatesInfo(
209209
var solutions = allSolutions
210210
.Where(solution =>
211211
solution.State == SolutionState.Rated || solution.State == SolutionState.Final);
212-
var min = solutions
213-
.Where(solution => solution.State == SolutionState.Final);
212+
var min = solutions.Any() ? solutions.Last().Rating : 0;
214213
var cnt = solutions.Count();
215214
worksheet.Cells[position.Row, position.Column].Value = min;
216215
worksheet.Cells[position.Row, position.Column + 2].Value = cnt;

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

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ const updatedLastViewedCourseId = (courseId : string) =>
5959
}
6060

6161
const Course: React.FC = () => {
62-
const {initialCourseId, tab} = useParams()
62+
const {courseId, tab} = useParams()
6363

64-
const isFromYandex = initialCourseId === undefined
65-
const courseId = isFromYandex ? getLastViewedCourseId() : initialCourseId
64+
const isFromYandex = courseId === undefined
65+
const validatedCourseId = isFromYandex ? getLastViewedCourseId() : courseId
6666

6767

6868
const navigate = useNavigate()
@@ -111,20 +111,22 @@ const Course: React.FC = () => {
111111
const showApplicationsTab = isCourseMentor
112112

113113
const changeTab = (newTab: string) => {
114-
if (isAcceptableTabValue(newTab) && newTab !== pageState.tabValue) {
115-
if (newTab === "stats" && !showStatsTab) return;
116-
if (newTab === "applications" && !showApplicationsTab) return;
117-
118-
setPageState(prevState => ({
119-
...prevState,
120-
tabValue: newTab
121-
}));
114+
if (!isFromYandex) {
115+
if (isAcceptableTabValue(newTab) && newTab !== pageState.tabValue) {
116+
if (newTab === "stats" && !showStatsTab) return;
117+
if (newTab === "applications" && !showApplicationsTab) return;
118+
119+
setPageState(prevState => ({
120+
...prevState,
121+
tabValue: newTab
122+
}));
123+
}
122124
}
123125
}
124126

125127
const setCurrentState = async () => {
126-
updatedLastViewedCourseId(courseId)
127-
const course = await ApiSingleton.coursesApi.apiCoursesByCourseIdGet(+courseId!)
128+
updatedLastViewedCourseId(validatedCourseId)
129+
const course = await ApiSingleton.coursesApi.apiCoursesByCourseIdGet(+validatedCourseId!)
128130

129131
// У пользователя изменилась роль (иначе он не может стать лектором в курсе),
130132
// однако он все ещё использует токен с прежней ролью
@@ -138,7 +140,7 @@ const Course: React.FC = () => {
138140
return
139141
}
140142

141-
const solutions = await ApiSingleton.statisticsApi.apiStatisticsByCourseIdGet(+courseId!)
143+
const solutions = await ApiSingleton.statisticsApi.apiStatisticsByCourseIdGet(+validatedCourseId!)
142144

143145
setCourseState(prevState => ({
144146
...prevState,
@@ -152,23 +154,22 @@ const Course: React.FC = () => {
152154
studentSolutions: solutions,
153155
tabValue: isFromYandex ? "stats" : "homeworks"
154156
}))
155-
if (isFromYandex)
156-
{
157-
window.history.replaceState(null, "", `/courses/${courseId}`)
157+
if (isFromYandex) {
158+
window.history.replaceState(null, "", `/courses/${validatedCourseId}/stats`)
158159
}
159160
}
160161

161162
useEffect(() => {
162163
setCurrentState()
163164
}, [])
164165

165-
useEffect(() => changeTab(tab || "homeworks"), [tab, courseId, isFound])
166+
useEffect(() => changeTab(tab || "homeworks"), [tab, validatedCourseId, isFound])
166167

167168
const yandexCode = new URLSearchParams(window.location.search).get("code")
168169

169170
const joinCourse = async () => {
170171
await ApiSingleton.coursesApi
171-
.apiCoursesSignInCourseByCourseIdPost(+courseId!)
172+
.apiCoursesSignInCourseByCourseIdPost(+validatedCourseId!)
172173
.then(() => setCurrentState());
173174
}
174175

@@ -216,7 +217,7 @@ const Course: React.FC = () => {
216217
</IconButton>
217218
}
218219
{isCourseMentor && !isReadingMode! && (
219-
<RouterLink to={`/courses/${courseId}/edit`}>
220+
<RouterLink to={`/courses/${validatedCourseId}/edit`}>
220221
<EditIcon fontSize="small"/>
221222
</RouterLink>
222223
)}
@@ -259,9 +260,9 @@ const Course: React.FC = () => {
259260
style={{marginTop: 15}}
260261
indicatorColor="primary"
261262
onChange={(event, value) => {
262-
if (value === 0) navigate(`/courses/${courseId}/homeworks`)
263-
if (value === 1) navigate(`/courses/${courseId}/stats`)
264-
if (value === 2) navigate(`/courses/${courseId}/applications`)
263+
if (value === 0) navigate(`/courses/${validatedCourseId}/homeworks`)
264+
if (value === 1) navigate(`/courses/${validatedCourseId}/stats`)
265+
if (value === 2) navigate(`/courses/${validatedCourseId}/applications`)
265266
}}
266267
>
267268
<Tab label="Домашние задания"/>
@@ -293,7 +294,7 @@ const Course: React.FC = () => {
293294
<Grid container>
294295
<Grid item xs={12}>
295296
<AddHomework
296-
id={+courseId!}
297+
id={+validatedCourseId!}
297298
onCancel={() => setCurrentState()}
298299
onSubmit={() => setCurrentState()}
299300
/>
@@ -369,7 +370,7 @@ const Course: React.FC = () => {
369370
onUpdate={() => setCurrentState()}
370371
course={courseState.course}
371372
students={courseState.newStudents}
372-
courseId={courseId!}
373+
courseId={validatedCourseId!}
373374
/>
374375
}
375376
</Grid>

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,34 @@ interface IStudentStatsProps {
1616

1717
interface IStudentStatsState {
1818
searched: string
19+
isSaveStatsActionOpened: boolean
1920
}
2021

2122
const StudentStats: React.FC<IStudentStatsProps> = (props) => {
2223
const [state, setSearched] = useState<IStudentStatsState>({
23-
searched: ""
24+
searched: "",
25+
isSaveStatsActionOpened: false
2426
});
2527

26-
const {searched} = state
28+
const {searched, isSaveStatsActionOpened} = state
2729

2830
useEffect(() => {
2931
const keyDownHandler = (event: KeyboardEvent) => {
32+
if (isSaveStatsActionOpened) return
3033
if (event.ctrlKey || event.altKey) return
3134
if (searched && event.key === "Escape") {
32-
setSearched({searched: ""});
35+
setSearched({...state, searched: ""});
3336
} else if (searched && event.key === "Backspace") {
34-
setSearched({searched: searched.slice(0, -1)})
37+
setSearched({...state, searched: searched.slice(0, -1)})
3538
} else if (event.key.length === 1 && event.key.match(/[a-zA-Zа-яА-Я\s]/i)
3639
) {
37-
setSearched({searched: searched + event.key})
40+
setSearched({...state, searched: searched + event.key})
3841
}
3942
};
4043

4144
document.addEventListener('keydown', keyDownHandler);
4245
return () => document.removeEventListener('keydown', keyDownHandler);
43-
}, [searched]);
46+
}, [searched, isSaveStatsActionOpened]);
4447

4548
const homeworks = props.homeworks.filter(h => h.tasks && h.tasks.length > 0)
4649
const solutions = searched
@@ -127,6 +130,8 @@ const StudentStats: React.FC<IStudentStatsProps> = (props) => {
127130
courseId={props.course.id}
128131
userId={props.userId}
129132
yandexCode={props.yandexCode}
133+
onActionOpening={() => setSearched({searched, isSaveStatsActionOpened: true})}
134+
onActionClosing={() => setSearched({searched, isSaveStatsActionOpened: false})}
130135
/>
131136
</div>
132137
</div>

hwproj.front/src/components/Solutions/ExportToYandex.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { FC, useState } from "react";
22
import { useEffect } from 'react';
33
import { ResultString } from "../../api";
4-
import { ResultExternalService } from "../../api";
54
import { Alert, Box, Button, CircularProgress, Grid, Link, MenuItem, Select, TextField } from "@mui/material";
65
import apiSingleton from "../../api/ApiSingleton";
76
import { green, red } from "@material-ui/core/colors";

hwproj.front/src/components/Solutions/SaveStats.tsx

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {FC, useState} from "react";
1+
import React, {FC, useState, useEffect} from "react";
22
import {Alert, Box, Button, CircularProgress, Grid, MenuItem, Select, TextField} from "@mui/material";
33
import SpeedDialIcon from '@mui/material/SpeedDialIcon';
44
import GetAppIcon from '@material-ui/icons/GetApp';
@@ -19,61 +19,62 @@ interface SaveStatsProps {
1919
courseId : number | undefined;
2020
userId : string;
2121
yandexCode: string | null;
22+
onActionOpening: () => void;
23+
onActionClosing: () => void;
24+
2225
}
2326

24-
enum SpeedDialActions {
25-
None,
27+
enum SpeedDialView {
28+
Collapsed,
29+
Expanded,
2630
Download,
2731
ShareWithGoogle,
2832
ShareWithYandex
2933
}
3034

31-
enum SpeedDialView {
32-
Opened,
33-
Expanded
34-
}
35-
3635
interface SaveStatsState {
37-
selectedAction: SpeedDialActions;
38-
speedDialView : SpeedDialView;
36+
selectedView : SpeedDialView;
3937
}
4038

4139
const SaveStats: FC<SaveStatsProps> = (props : SaveStatsProps) => {
42-
const [state, setState] = useState<SaveStatsState>({
43-
selectedAction: props.yandexCode === null ? SpeedDialActions.None : SpeedDialActions.ShareWithYandex,
44-
speedDialView: SpeedDialView.Opened
40+
const [state, setSelectedView] = useState<SaveStatsState>({
41+
selectedView: props.yandexCode === null ? SpeedDialView.Collapsed : SpeedDialView.ShareWithYandex,
4542
})
4643

47-
const {selectedAction, speedDialView} = state
44+
const {selectedView} = state
4845

49-
const handleCancellation = () => {
50-
setState(prevState => ({...prevState, speedDialView: SpeedDialView.Opened, selectedAction: SpeedDialActions.None}));
51-
}
46+
useEffect(() => {
47+
if (selectedView === SpeedDialView.Download ||
48+
selectedView === SpeedDialView.ShareWithGoogle ||
49+
selectedView === SpeedDialView.ShareWithYandex) {
50+
props.onActionOpening()
51+
return
52+
}
53+
props.onActionClosing()
54+
}, [selectedView]);
5255

5356
const handleSpeedDialItemClick = (operation : string) => {
5457
switch ( operation ) {
5558
case 'download':
56-
setState(prevState =>
57-
({...prevState, selectedAction: SpeedDialActions.Download}));
59+
setSelectedView({selectedView: SpeedDialView.Download});
5860
break;
5961
case 'shareWithGoogle':
60-
setState(prevState =>
61-
({...prevState, selectedAction: SpeedDialActions.ShareWithGoogle}));
62+
setSelectedView({selectedView: SpeedDialView.ShareWithGoogle});
6263
break;
6364
case 'shareWithYandex':
64-
setState(prevState =>
65-
({...prevState, selectedAction: SpeedDialActions.ShareWithYandex}));
65+
setSelectedView({selectedView: SpeedDialView.ShareWithYandex});
6666
break;
6767
default:
6868
break;
6969
}
7070
}
7171

72-
const handleChangeSpeedDialView = () =>
73-
setState(prevState => ({
74-
...prevState,
75-
speedDialView: speedDialView === SpeedDialView.Opened ? SpeedDialView.Expanded : SpeedDialView.Opened
76-
}))
72+
const handleCancellation = () => setSelectedView({selectedView: SpeedDialView.Collapsed});
73+
74+
const handleChangingView = () =>
75+
setSelectedView({selectedView: selectedView === SpeedDialView.Collapsed ?
76+
SpeedDialView.Expanded : SpeedDialView.Collapsed
77+
})
7778

7879
const actions = [
7980
{ icon: <SaveIcon sx={{ fontSize: 30 }} />, name: 'Сохранить', operation: 'download' },
@@ -84,15 +85,15 @@ const SaveStats: FC<SaveStatsProps> = (props : SaveStatsProps) => {
8485

8586
return (
8687
<div>
87-
{selectedAction === SpeedDialActions.None &&
88+
{(selectedView === SpeedDialView.Collapsed || selectedView === SpeedDialView.Expanded) &&
8889
<Box sx={{ fontSize: 100 }}>
8990
<SpeedDial
9091
ariaLabel="SpeedDial basic example"
9192
icon={<GetAppIcon/>}
9293
direction="right"
93-
onClick={handleChangeSpeedDialView}
94+
onClickCapture={handleChangingView}
9495
sx={{ '& .MuiFab-primary': { width: 45, height: 45 } }}
95-
open={speedDialView === SpeedDialView.Expanded}
96+
open={selectedView !== SpeedDialView.Collapsed}
9697
>
9798
{actions.map((action) => (
9899
<SpeedDialAction
@@ -107,21 +108,21 @@ const SaveStats: FC<SaveStatsProps> = (props : SaveStatsProps) => {
107108
</SpeedDial>
108109
</Box>
109110
}
110-
{selectedAction === SpeedDialActions.Download &&
111+
{selectedView === SpeedDialView.Download &&
111112
<DownloadStats
112113
courseId={props.courseId}
113114
userId={props.userId}
114115
onCancellation={() => handleCancellation()}
115116
/>
116117
}
117-
{selectedAction === SpeedDialActions.ShareWithGoogle &&
118+
{selectedView === SpeedDialView.ShareWithGoogle &&
118119
<ExportToGoogle
119120
courseId={props.courseId}
120121
userId={props.userId}
121122
onCancellation={() => handleCancellation()}
122123
/>
123124
}
124-
{selectedAction === SpeedDialActions.ShareWithYandex &&
125+
{selectedView === SpeedDialView.ShareWithYandex &&
125126
<ExportToYandex
126127
courseId={props.courseId}
127128
userId={props.userId}

0 commit comments

Comments
 (0)