From 9f01669c6ba5c17a80dcc66a593c215fe0f107a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vikt=C3=B3ria=20Brezinov=C3=A1?= <52294703+vikibrezinova@users.noreply.github.com> Date: Fri, 22 Nov 2024 22:16:56 +0100 Subject: [PATCH] RA upload solution (#377) * add solution to RA * create solution uploader * fix showing event-registration references * client-side filter * create even registration in RA * add event field to event registration * add late_tag and is_online to solution * Results changes after backend now returns the entire grade object See https://github.com/ZdruzenieSTROM/webstrom-backend/pull/426 * RA create solution also sending false-y but non-null values (e. g. 0) * RA EventRegistration changes after backend update * RA EventRegistrations fixed typing --------- Co-authored-by: rtrembecky Co-authored-by: Michal Masrna --- src/components/Admin/Admin.tsx | 27 ++++++++++++++++ src/components/Admin/dataProvider.ts | 20 ++++++++++-- .../EventRegistrationCreate.tsx | 23 +++++++++++++ .../EventRegistrationEdit.tsx | 30 +++++++++++++++++ .../EventRegistrationList.tsx | 19 +++++++++++ .../EventRegistrationShow.tsx | 20 ++++++++++++ .../competition/solution/SolutionCreate.tsx | 32 +++++++++++++++++++ .../competition/solution/SolutionEdit.tsx | 32 +++++++++++++++++++ .../competition/solution/SolutionList.tsx | 17 ++++++++++ .../competition/solution/SolutionShow.tsx | 16 ++++++++++ .../solution/createSolutionFormData.tsx | 17 ++++++++++ src/components/Results/ResultsRow.tsx | 9 ++++-- 12 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 src/components/Admin/resources/competition/event-registration/EventRegistrationCreate.tsx create mode 100644 src/components/Admin/resources/competition/event-registration/EventRegistrationEdit.tsx create mode 100644 src/components/Admin/resources/competition/event-registration/EventRegistrationList.tsx create mode 100644 src/components/Admin/resources/competition/event-registration/EventRegistrationShow.tsx create mode 100644 src/components/Admin/resources/competition/solution/SolutionCreate.tsx create mode 100644 src/components/Admin/resources/competition/solution/SolutionEdit.tsx create mode 100644 src/components/Admin/resources/competition/solution/SolutionList.tsx create mode 100644 src/components/Admin/resources/competition/solution/SolutionShow.tsx create mode 100644 src/components/Admin/resources/competition/solution/createSolutionFormData.tsx diff --git a/src/components/Admin/Admin.tsx b/src/components/Admin/Admin.tsx index fdfed139..71198c37 100644 --- a/src/components/Admin/Admin.tsx +++ b/src/components/Admin/Admin.tsx @@ -18,6 +18,10 @@ import {EventCreate} from './resources/competition/event/EventCreate' import {EventEdit} from './resources/competition/event/EventEdit' import {EventList} from './resources/competition/event/EventList' import {EventShow} from './resources/competition/event/EventShow' +import {EventRegistrationCreate} from './resources/competition/event-registration/EventRegistrationCreate' +import {EventRegistrationEdit} from './resources/competition/event-registration/EventRegistrationEdit' +import {EventRegistrationList} from './resources/competition/event-registration/EventRegistrationList' +import {EventRegistrationShow} from './resources/competition/event-registration/EventRegistrationShow' import {ProblemCreate} from './resources/competition/problems/ProblemCreate' import {ProblemEdit} from './resources/competition/problems/ProblemEdit' import {ProblemList} from './resources/competition/problems/ProblemList' @@ -30,6 +34,10 @@ import {SeriesCreate} from './resources/competition/series/SeriesCreate' import {SeriesEdit} from './resources/competition/series/SeriesEdit' import {SeriesList} from './resources/competition/series/SeriesList' import {SeriesShow} from './resources/competition/series/SeriesShow' +import {SolutionCreate} from './resources/competition/solution/SolutionCreate' +import {SolutionEdit} from './resources/competition/solution/SolutionEdit' +import {SolutionList} from './resources/competition/solution/SolutionList' +import {SolutionShow} from './resources/competition/solution/SolutionShow' import {useAuthProvider} from './useAuthProvider' export const Admin: FC = () => { @@ -85,6 +93,25 @@ export const Admin: FC = () => { show={ProblemShow} create={ProblemCreate} /> + + + + + ) } diff --git a/src/components/Admin/dataProvider.ts b/src/components/Admin/dataProvider.ts index a4a17782..c5efdcbb 100644 --- a/src/components/Admin/dataProvider.ts +++ b/src/components/Admin/dataProvider.ts @@ -42,18 +42,32 @@ export const dataProvider: DataProvider = { const stringifiedQuery = stringify(query) const {data} = await axios.get(`${apiUrl}/${resource}${stringifiedQuery ? `/?${stringifiedQuery}` : ''}`) + // client-side filter + const filter = params.filter.q + let filteredData = data + if (filter) { + // v podstate vygenerovane Copilotom :D + // vyhladava to filter string vo vsetkych fieldoch kazdeho recordu + filteredData = data.filter((record: RaRecord) => { + return Object.keys(record).some((key) => { + const value = record[key] + return value && value.toString().toLowerCase().includes(filter.toLowerCase()) + }) + }) + } + // client-side sort const {field, order} = params.sort - data.sort(dynamicSort(field, order)) + filteredData.sort(dynamicSort(field, order)) // client-side pagination const {page, perPage} = params.pagination - const pagedData = data.slice((page - 1) * perPage, page * perPage) + const pagedData = filteredData.slice((page - 1) * perPage, page * perPage) return { data: pagedData, - total: data.length, + total: filteredData.length, } }, getOne: async (resource, params) => { diff --git a/src/components/Admin/resources/competition/event-registration/EventRegistrationCreate.tsx b/src/components/Admin/resources/competition/event-registration/EventRegistrationCreate.tsx new file mode 100644 index 00000000..91f64fc6 --- /dev/null +++ b/src/components/Admin/resources/competition/event-registration/EventRegistrationCreate.tsx @@ -0,0 +1,23 @@ +import {FC} from 'react' +import {AutocompleteInput, ReferenceInput, required, SimpleForm} from 'react-admin' + +import {MyCreate} from '@/components/Admin/custom/MyCreate' + +export const EventRegistrationCreate: FC = () => ( + + + + + + + + + + + + + + + + +) diff --git a/src/components/Admin/resources/competition/event-registration/EventRegistrationEdit.tsx b/src/components/Admin/resources/competition/event-registration/EventRegistrationEdit.tsx new file mode 100644 index 00000000..2c289635 --- /dev/null +++ b/src/components/Admin/resources/competition/event-registration/EventRegistrationEdit.tsx @@ -0,0 +1,30 @@ +import {FC} from 'react' +import {AutocompleteInput, ReferenceInput, required, SimpleForm} from 'react-admin' + +import {MyEdit} from '@/components/Admin/custom/MyEdit' + +export const EventRegistrationEdit: FC = () => ( + { + record.profile = record.profile.id + record.school = record.school.code + record.grade = record.grade.id + return record + }} + > + + + + + + + + + + + + + + + +) diff --git a/src/components/Admin/resources/competition/event-registration/EventRegistrationList.tsx b/src/components/Admin/resources/competition/event-registration/EventRegistrationList.tsx new file mode 100644 index 00000000..2f55eab4 --- /dev/null +++ b/src/components/Admin/resources/competition/event-registration/EventRegistrationList.tsx @@ -0,0 +1,19 @@ +import {FC} from 'react' +import {Datagrid, FunctionField, List, ReferenceField, TextField} from 'react-admin' + +import {EventRegistration} from '@/types/api/competition' + +export const EventRegistrationList: FC = () => ( + + + `${record.profile.first_name} ${record.profile.last_name}`} + /> + + + + + +) diff --git a/src/components/Admin/resources/competition/event-registration/EventRegistrationShow.tsx b/src/components/Admin/resources/competition/event-registration/EventRegistrationShow.tsx new file mode 100644 index 00000000..0992ecdf --- /dev/null +++ b/src/components/Admin/resources/competition/event-registration/EventRegistrationShow.tsx @@ -0,0 +1,20 @@ +import {FC} from 'react' +import {FunctionField, ReferenceField, SimpleShowLayout, TextField} from 'react-admin' + +import {MyShow} from '@/components/Admin/custom/MyShow' +import {EventRegistration} from '@/types/api/competition' + +export const EventRegistrationShow: FC = () => ( + + + `${record.profile.first_name} ${record.profile.last_name}`} + /> + + + + + +) diff --git a/src/components/Admin/resources/competition/solution/SolutionCreate.tsx b/src/components/Admin/resources/competition/solution/SolutionCreate.tsx new file mode 100644 index 00000000..a8235fcb --- /dev/null +++ b/src/components/Admin/resources/competition/solution/SolutionCreate.tsx @@ -0,0 +1,32 @@ +import {FC} from 'react' +import {AutocompleteInput, BooleanInput, FileInput, ReferenceInput, required, SimpleForm} from 'react-admin' + +import {MyCreate} from '@/components/Admin/custom/MyCreate' +import {MyFileField} from '@/components/Admin/custom/MyFileField' + +import {createSolutionFormData} from './createSolutionFormData' + +export const SolutionCreate: FC = () => ( + { + record.formData = createSolutionFormData(record) + return record + }} + > + + + + + + + + + + + + + + + + +) diff --git a/src/components/Admin/resources/competition/solution/SolutionEdit.tsx b/src/components/Admin/resources/competition/solution/SolutionEdit.tsx new file mode 100644 index 00000000..72f7540a --- /dev/null +++ b/src/components/Admin/resources/competition/solution/SolutionEdit.tsx @@ -0,0 +1,32 @@ +import {FC} from 'react' +import {AutocompleteInput, BooleanInput, FileInput, ReferenceInput, required, SimpleForm} from 'react-admin' + +import {MyEdit} from '@/components/Admin/custom/MyEdit' +import {MyFileField} from '@/components/Admin/custom/MyFileField' + +import {createSolutionFormData} from './createSolutionFormData' + +export const SolutionEdit: FC = () => ( + { + record.formData = createSolutionFormData(record) + return record + }} + > + + + + + + + + + + + + + + + + +) diff --git a/src/components/Admin/resources/competition/solution/SolutionList.tsx b/src/components/Admin/resources/competition/solution/SolutionList.tsx new file mode 100644 index 00000000..f39ce30e --- /dev/null +++ b/src/components/Admin/resources/competition/solution/SolutionList.tsx @@ -0,0 +1,17 @@ +import {FC} from 'react' +import {BooleanField, Datagrid, FunctionField, List, RaRecord, ReferenceField} from 'react-admin' + +export const SolutionList: FC = () => ( + + + + + + label="Má nahraté riešenie" + render={(record) => record && } + /> + + + + +) diff --git a/src/components/Admin/resources/competition/solution/SolutionShow.tsx b/src/components/Admin/resources/competition/solution/SolutionShow.tsx new file mode 100644 index 00000000..c0331c1e --- /dev/null +++ b/src/components/Admin/resources/competition/solution/SolutionShow.tsx @@ -0,0 +1,16 @@ +import {FC} from 'react' +import {BooleanField, FileField, ReferenceField, SimpleShowLayout} from 'react-admin' + +import {MyShow} from '@/components/Admin/custom/MyShow' + +export const SolutionShow: FC = () => ( + + + + + + + + + +) diff --git a/src/components/Admin/resources/competition/solution/createSolutionFormData.tsx b/src/components/Admin/resources/competition/solution/createSolutionFormData.tsx new file mode 100644 index 00000000..6cc81a8a --- /dev/null +++ b/src/components/Admin/resources/competition/solution/createSolutionFormData.tsx @@ -0,0 +1,17 @@ +import {Solution} from '@/types/api/competition' + +export const createSolutionFormData = ({ + solution, + ...data +}: Omit & { + solution?: {rawFile: File} +}) => { + const formData = new FormData() + // vzdy appendneme kazdy kluc, aj tieto fily, len null sa tu neda pouzit. null znamena, ze file odstranujeme + formData.append('solution', solution?.rawFile ?? '') + Object.entries(data).forEach(([key, value]) => { + if (value != null) formData.append(key, value.toString()) + }) + + return formData +} diff --git a/src/components/Results/ResultsRow.tsx b/src/components/Results/ResultsRow.tsx index e6e78dc6..a32f443e 100644 --- a/src/components/Results/ResultsRow.tsx +++ b/src/components/Results/ResultsRow.tsx @@ -11,7 +11,12 @@ interface Registration { city: string zip_code: string } - grade: string + grade: { + name: string + tag: string + years_until_graduation: number + is_active: boolean + } profile: { first_name: string last_name: string @@ -63,7 +68,7 @@ export const ResultsRow: FC<{result: Result}> = ({result}) => { - {registration.grade} + {registration.grade.tag}