@@ -19,7 +19,7 @@ import { useRetryProcessing, useStopProcessing, useUploads } from "../entities/u
1919import { getUploadStatusLabel } from "../shared/config/ui" ;
2020import { api } from "../shared/api/client" ;
2121import { dateFilterValue , matchesDateRange , matchesNumberRange , matchesText } from "../shared/lib/clientFilters" ;
22- import { DateTimeIsoInput } from "../shared/ui/DateTimeIsoInput " ;
22+ import { FilterDateTimeRange , FilterFormField , FilterNumberRange } from "../shared/ui/FilterField " ;
2323
2424type SortField = "id" | "date" | "author" | "status" ;
2525type SortDir = "asc" | "desc" ;
@@ -174,58 +174,57 @@ export function UploadHistoryPage() {
174174 </ button >
175175 ) }
176176 </ div >
177- < div className = "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-3" >
178- < TextInput
179- placeholder = "ID загрузки"
180- size = "l"
181- value = { idFilter }
182- onUpdate = { setIdFilter }
183- startContent = { < Search className = "w-3.5 h-3.5 text-muted-foreground" /> }
184- />
185- < div className = "grid grid-cols-2 gap-2 xl:col-span-2" >
186- < DateTimeIsoInput label = "Дата от" value = { dateFrom } onUpdate = { setDateFrom } />
187- < DateTimeIsoInput label = "Дата до" value = { dateTo } onUpdate = { setDateTo } />
177+ < div className = "space-y-4" >
178+ < div className = "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-3" >
179+ < FilterFormField label = "ID загрузки" >
180+ < TextInput
181+ placeholder = "Поиск по ID"
182+ size = "l"
183+ value = { idFilter }
184+ onUpdate = { setIdFilter }
185+ startContent = { < Search className = "w-3.5 h-3.5 text-muted-foreground" /> }
186+ />
187+ </ FilterFormField >
188+ < FilterFormField label = "Статус" >
189+ < Select
190+ value = { [ statusFilter ] }
191+ onUpdate = { ( v ) => setStatusFilter ( v [ 0 ] ?? "all" ) }
192+ options = { [
193+ { value : "all" , content : "Все статусы" } ,
194+ { value : "success" , content : "Успешно" } ,
195+ { value : "warning" , content : "Предупреждения" } ,
196+ { value : "error" , content : "Ошибка" } ,
197+ { value : "processing" , content : "Обработка" } ,
198+ { value : "cancelled" , content : "Остановлено" } ,
199+ { value : "stale" , content : "Зависло" } ,
200+ ] }
201+ size = "l"
202+ width = "max"
203+ />
204+ </ FilterFormField >
205+ < FilterFormField label = "Автор" >
206+ < Select
207+ value = { [ authorFilter ] }
208+ onUpdate = { ( v ) => setAuthorFilter ( v [ 0 ] ?? "all" ) }
209+ options = { [
210+ { value : "all" , content : "Все авторы" } ,
211+ ...authors . map ( ( a ) => ( { value : a , content : a } ) ) ,
212+ ] }
213+ size = "l"
214+ width = "max"
215+ />
216+ </ FilterFormField >
217+ < FilterFormField label = "Типы файлов" >
218+ < TextInput placeholder = "Например: moodle, camera" size = "l" value = { fileTypesFilter } onUpdate = { setFileTypesFilter } />
219+ </ FilterFormField >
188220 </ div >
189- < Select
190- value = { [ statusFilter ] }
191- onUpdate = { ( v ) => setStatusFilter ( v [ 0 ] ?? "all" ) }
192- options = { [
193- { value : "all" , content : "Все статусы" } ,
194- { value : "success" , content : "Успешно" } ,
195- { value : "warning" , content : "Предупреждения" } ,
196- { value : "error" , content : "Ошибка" } ,
197- { value : "processing" , content : "Обработка" } ,
198- { value : "cancelled" , content : "Остановлено" } ,
199- { value : "stale" , content : "Зависло" } ,
200- ] }
201- size = "l"
202- />
203- < Select
204- value = { [ authorFilter ] }
205- onUpdate = { ( v ) => setAuthorFilter ( v [ 0 ] ?? "all" ) }
206- options = { [
207- { value : "all" , content : "Все авторы" } ,
208- ...authors . map ( ( a ) => ( { value : a , content : a } ) ) ,
209- ] }
210- size = "l"
211- />
212- < TextInput
213- placeholder = "Типы файлов"
214- size = "l"
215- value = { fileTypesFilter }
216- onUpdate = { setFileTypesFilter }
217- />
218- < div className = "grid grid-cols-2 gap-2" >
219- < input className = "w-full h-10" type = "number" placeholder = "Файлов от" value = { filesMin } onChange = { ( event ) => setFilesMin ( event . target . value ) } />
220- < input className = "w-full h-10" type = "number" placeholder = "Файлов до" value = { filesMax } onChange = { ( event ) => setFilesMax ( event . target . value ) } />
221+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
222+ < FilterDateTimeRange label = "Дата загрузки" from = { dateFrom } to = { dateTo } onFromChange = { setDateFrom } onToChange = { setDateTo } />
221223 </ div >
222- < div className = "grid grid-cols-2 gap-2" >
223- < input className = "w-full h-10" type = "number" placeholder = "Строк от" value = { rowsMin } onChange = { ( event ) => setRowsMin ( event . target . value ) } />
224- < input className = "w-full h-10" type = "number" placeholder = "Строк до" value = { rowsMax } onChange = { ( event ) => setRowsMax ( event . target . value ) } />
225- </ div >
226- < div className = "grid grid-cols-2 gap-2" >
227- < input className = "w-full h-10" type = "number" placeholder = "Студентов от" value = { studentsMin } onChange = { ( event ) => setStudentsMin ( event . target . value ) } />
228- < input className = "w-full h-10" type = "number" placeholder = "Студентов до" value = { studentsMax } onChange = { ( event ) => setStudentsMax ( event . target . value ) } />
224+ < div className = "grid grid-cols-1 md:grid-cols-3 gap-3" >
225+ < FilterNumberRange label = "Файлов" from = { filesMin } to = { filesMax } onFromChange = { setFilesMin } onToChange = { setFilesMax } />
226+ < FilterNumberRange label = "Строк" from = { rowsMin } to = { rowsMax } onFromChange = { setRowsMin } onToChange = { setRowsMax } />
227+ < FilterNumberRange label = "Студентов" from = { studentsMin } to = { studentsMax } onFromChange = { setStudentsMin } onToChange = { setStudentsMax } />
229228 </ div >
230229 </ div >
231230 </ div >
0 commit comments