1- import { FC , useMemo , useState } from "react" ;
1+ import { FC , useEffect , useMemo , useState } from "react" ;
22import {
33 Grid ,
44 TextField ,
@@ -17,7 +17,7 @@ import {
1717import EditIcon from "@mui/icons-material/Edit" ;
1818import AddIcon from "@mui/icons-material/Add" ;
1919import ApiSingleton from "../../api/ApiSingleton" ;
20- import { GroupViewModel , AccountDataDto } from "@/api" ;
20+ import { GroupViewModel , AccountDataDto } from "@/api" ;
2121
2222
2323interface GroupSelectorProps {
@@ -31,8 +31,9 @@ interface GroupSelectorProps {
3131 onCreateNewGroup ?: ( ) => void ,
3232}
3333
34- const GroupSelector : FC < GroupSelectorProps > = ( props ) => {
35- const groups = props . groups || [ ] ;
34+ const GroupSelector : FC < GroupSelectorProps > = ( props ) => {
35+ const groups = [ ...( props . groups || [ ] ) , { id : undefined , name : "Все студенты" } ]
36+
3637 const [ isDialogOpen , setIsDialogOpen ] = useState ( false ) ;
3738 const [ formState , setFormState ] = useState < {
3839 name : string ,
@@ -44,11 +45,9 @@ const GroupSelector: FC<GroupSelectorProps> = (props) => {
4445 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
4546 const [ isError , setIsError ] = useState ( false ) ;
4647
47- const selectedGroup = useMemo ( ( ) =>
48- groups . find ( g => g . id === props . selectedGroupId ) ,
49- [ groups , props . selectedGroupId ] ) ;
48+ const selectedGroup = groups . find ( g => g . id === props . selectedGroupId )
5049
51- const studentsWithousGroup = useMemo ( ( ) => {
50+ const studentsWithoutGroup = useMemo ( ( ) => {
5251 const studentsInGroups = groups . flatMap ( g => g . studentsIds )
5352 return props . courseStudents . filter ( ( cm ) => ! studentsInGroups . includes ( cm . userId ) )
5453 } , [ groups , props . courseStudents ] ) ;
@@ -76,7 +75,7 @@ const GroupSelector: FC<GroupSelectorProps> = (props) => {
7675 selectedGroup . id ! ,
7776 {
7877 name : formState . name ,
79- groupMates : formState . memberIds . map ( studentId => ( { studentId } ) ) ,
78+ groupMates : formState . memberIds . map ( studentId => ( { studentId} ) ) ,
8079 }
8180 ) ;
8281 props . onGroupsUpdate ( ) ;
@@ -103,70 +102,85 @@ const GroupSelector: FC<GroupSelectorProps> = (props) => {
103102 }
104103
105104 return (
106- < Grid item xs = { 12 } style = { { marginBottom : "15px" , marginTop : 1 } } >
107- { props . choiceDisabled ? (
108- < Stack spacing = { 1 } >
109- < TextField
110- label = "Группа"
111- value = { selectedGroup ?. name || "Все студенты" }
112- variant = "outlined"
113- fullWidth
114- disabled
115- />
116- { selectedGroup && (
117- < Button
105+ < Grid container xs = { 12 } spacing = { 1 } >
106+ < Grid item xs = { 12 } >
107+ < Autocomplete
108+ freeSolo
109+ fullWidth
110+ options = { [ ...groups ] }
111+ getOptionLabel = { ( option ) => typeof option === 'string' ? option : option ?. name || "Все студенты" }
112+ value = { groups . find ( g => g . id == props . selectedGroupId ) }
113+ onChange = { ( _ , newGroup ) => {
114+ if ( typeof newGroup === 'string' ) return
115+ props . onGroupIdChange ( newGroup ?. id )
116+ } }
117+ renderInput = { ( params ) => (
118+ < TextField
119+ { ...params }
120+ label = "Группа"
121+ placeholder = "Выберите группу"
118122 variant = "outlined"
119- startIcon = { < EditIcon /> }
120- onClick = { handleOpenEditDialog }
121- fullWidth
122- >
123- Изменить состав группы
124- </ Button >
123+ />
125124 ) }
126- </ Stack >
127- ) : (
128- < Stack spacing = { 1 } >
125+ />
126+ </ Grid >
127+ { selectedGroup && < Grid item xs = { 12 } >
128+ < Stack direction = { "column" } >
129129 < Autocomplete
130- options = { [ { id : undefined , name : "Все студенты" } , ...groups ] }
131- getOptionLabel = { ( option ) => option . name || "" }
132- value = { props . selectedGroupId !== undefined
133- ? groups . find ( g => g . id === props . selectedGroupId ) || null
134- : { id : undefined , name : "Все студенты" } }
135- onChange = { ( _ , newGroup ) => {
136- props . onGroupIdChange ( newGroup ?. id )
130+ multiple
131+ fullWidth
132+ options = { studentsWithoutGroup }
133+ value = { props . courseStudents ?. filter ( s => formState . memberIds . includes ( s . userId ! ) ) || [ ] }
134+ getOptionLabel = { ( option ) =>
135+ `${ option . surname ?? "" } ${ option . name ?? "" } / ${ option . email ?? "" } ` . trim ( )
136+ }
137+ filterSelectedOptions
138+ onChange = { ( _ , values ) => {
139+ if ( selectedGroup ) {
140+ // При редактировании выбранной группы можно только добавлять студентов
141+ setFormState ( prev => ( {
142+ ...prev ,
143+ memberIds : [ ...formState . memberIds ,
144+ ...values . map ( x => ! formState . memberIds . includes ( x . userId ! ) ? x . userId ! : "" ) . filter ( Boolean ) ]
145+ } ) )
146+ } else {
147+ setFormState ( prev => ( {
148+ ...prev ,
149+ memberIds : values
150+ . map ( x => x . userId ! )
151+ . filter ( Boolean )
152+ } ) )
153+ }
137154 } }
155+ disabled = { isSubmitting }
156+ renderTags = { ( tagValue , getTagProps ) =>
157+ tagValue . map ( ( option , index ) => (
158+ < Chip
159+ { ...getTagProps ( { index} ) }
160+ label = { `${ option . surname ?? "" } ${ option . name ?? "" } / ${ option . email ?? "" } ` . trim ( ) }
161+ onDelete = { selectedGroup ? undefined : getTagProps ( { index} ) . onDelete }
162+ key = { option . userId }
163+ />
164+ ) )
165+ }
138166 renderInput = { ( params ) => (
139167 < TextField
140168 { ...params }
141- label = "Группа (необязательно)"
142- placeholder = "Выберите группу"
143- variant = "outlined"
169+ label = "Участники группы"
170+ placeholder = "Выберите студентов"
144171 />
145172 ) }
146173 />
147- { selectedGroup && (
148- < Button
149- variant = "outlined"
150- startIcon = { < EditIcon /> }
151- onClick = { handleOpenEditDialog }
152- fullWidth
153- >
154- Изменить состав группы
155- </ Button >
156- ) }
157- { ! selectedGroup && (
158- < Button
159- variant = "outlined"
160- startIcon = { < AddIcon /> }
161- onClick = { handleOpenEditDialog }
162- fullWidth
163- >
164- Создать группу
165- </ Button >
166- ) }
174+ < Button
175+ onClick = { handleSubmitEdit }
176+ color = "primary"
177+ variant = "contained"
178+ disabled = { isSubmitting || ! formState . name . trim ( ) || formState . memberIds . length === 0 }
179+ >
180+ { isSubmitting ? < CircularProgress size = { 24 } /> : selectedGroup ? "Сохранить" : "Создать" }
181+ </ Button >
167182 </ Stack >
168- ) }
169-
183+ </ Grid > }
170184 < Dialog
171185 fullWidth
172186 maxWidth = "sm"
@@ -199,52 +213,6 @@ const GroupSelector: FC<GroupSelectorProps> = (props) => {
199213 disabled = { isSubmitting || props . choiceDisabled }
200214 />
201215 </ Grid >
202- < Grid item xs = { 12 } >
203- < Autocomplete
204- multiple
205- options = { studentsWithousGroup }
206- value = { props . courseStudents ?. filter ( s => formState . memberIds . includes ( s . userId ! ) ) || [ ] }
207- getOptionLabel = { ( option ) =>
208- `${ option . surname ?? "" } ${ option . name ?? "" } / ${ option . email ?? "" } ` . trim ( )
209- }
210- filterSelectedOptions
211- onChange = { ( _ , values ) => {
212- if ( selectedGroup ) {
213- // При редактировании выбранной группы можно только добавлять студентов
214- setFormState ( prev => ( {
215- ...prev ,
216- memberIds : [ ...formState . memberIds ,
217- ...values . map ( x => ! formState . memberIds . includes ( x . userId ! ) ? x . userId ! : "" ) . filter ( Boolean ) ]
218- } ) )
219- } else {
220- setFormState ( prev => ( {
221- ...prev ,
222- memberIds : values
223- . map ( x => x . userId ! )
224- . filter ( Boolean )
225- } ) )
226- }
227- } }
228- disabled = { isSubmitting }
229- renderTags = { ( tagValue , getTagProps ) =>
230- tagValue . map ( ( option , index ) => (
231- < Chip
232- { ...getTagProps ( { index } ) }
233- label = { `${ option . surname ?? "" } ${ option . name ?? "" } / ${ option . email ?? "" } ` . trim ( ) }
234- onDelete = { selectedGroup ? undefined : getTagProps ( { index } ) . onDelete }
235- key = { option . userId }
236- />
237- ) )
238- }
239- renderInput = { ( params ) => (
240- < TextField
241- { ...params }
242- label = "Участники группы"
243- placeholder = "Выберите студентов"
244- />
245- ) }
246- />
247- </ Grid >
248216 </ Grid >
249217 </ DialogContent >
250218 < DialogActions >
@@ -255,18 +223,9 @@ const GroupSelector: FC<GroupSelectorProps> = (props) => {
255223 >
256224 Отменить
257225 </ Button >
258- < Button
259- onClick = { handleSubmitEdit }
260- color = "primary"
261- variant = "contained"
262- disabled = { isSubmitting || ! formState . name . trim ( ) || formState . memberIds . length === 0 }
263- >
264- { isSubmitting ? < CircularProgress size = { 24 } /> : selectedGroup ? "Сохранить" : "Создать" }
265- </ Button >
266226 </ DialogActions >
267227 </ Dialog >
268- </ Grid >
269- )
228+ </ Grid > )
270229}
271230
272231export default GroupSelector
0 commit comments