diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f7195a7..c80190c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,8 +16,10 @@ "@types/react": "^18.0.9", "@types/react-dom": "^18.0.4", "axios": "^0.27.2", + "daisyui": "^2.15.2", "react": "^18.1.0", "react-dom": "^18.1.0", + "react-hook-form": "^7.31.3", "react-query": "^3.39.0", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", @@ -5314,6 +5316,18 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -5327,6 +5341,31 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/colord": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", @@ -5782,6 +5821,15 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -5981,6 +6029,21 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, + "node_modules/daisyui": { + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.15.2.tgz", + "integrity": "sha512-12hjzlOUkGVzMPKhaBc8soqNY2mJWGTYJF8sTuCibDcdGbHDmYgBUY0OX1HHIVq/qh2vELh5yHb4QCgCDWUAwg==", + "dependencies": { + "color": "^4.2", + "css-selector-tokenizer": "^0.8.0", + "postcss-js": "^4.0.0", + "tailwindcss": "^3.0" + }, + "peerDependencies": { + "autoprefixer": "^10.0.2", + "postcss": "^8.1.6" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -7400,6 +7463,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -13561,6 +13629,21 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-hook-form": { + "version": "7.31.3", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.31.3.tgz", + "integrity": "sha512-NVZdCWViIWXXXlQ3jxVQH0NuNfwPf8A/0KvuCxrM9qxtP1qYosfR2ZudarziFrVOC7eTUbWbm1T4OyYCwv9oSQ==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -14423,6 +14506,19 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -20186,6 +20282,30 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -20199,6 +20319,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colord": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", @@ -20517,6 +20646,15 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "css-selector-tokenizer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz", + "integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==", + "requires": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, "css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -20659,6 +20797,17 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" }, + "daisyui": { + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-2.15.2.tgz", + "integrity": "sha512-12hjzlOUkGVzMPKhaBc8soqNY2mJWGTYJF8sTuCibDcdGbHDmYgBUY0OX1HHIVq/qh2vELh5yHb4QCgCDWUAwg==", + "requires": { + "color": "^4.2", + "css-selector-tokenizer": "^0.8.0", + "postcss-js": "^4.0.0", + "tailwindcss": "^3.0" + } + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -21705,6 +21854,11 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -26009,6 +26163,12 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "react-hook-form": { + "version": "7.31.3", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.31.3.tgz", + "integrity": "sha512-NVZdCWViIWXXXlQ3jxVQH0NuNfwPf8A/0KvuCxrM9qxtP1qYosfR2ZudarziFrVOC7eTUbWbm1T4OyYCwv9oSQ==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -26636,6 +26796,21 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index ddab8d5..d9a6894 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,8 +11,10 @@ "@types/react": "^18.0.9", "@types/react-dom": "^18.0.4", "axios": "^0.27.2", + "daisyui": "^2.15.2", "react": "^18.1.0", "react-dom": "^18.1.0", + "react-hook-form": "^7.31.3", "react-query": "^3.39.0", "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a123770..9d6ddae 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,6 +2,7 @@ import React from 'react' import { QueryClient, QueryClientProvider } from 'react-query' import { Route, Routes } from 'react-router-dom' import './App.css' +import AddStudent from './views/coordinator/AddStudent' import Coordinator from './views/coordinator/Coordinator' import Groups from './views/coordinator/Groups' import Leaders from './views/coordinator/Leaders' @@ -25,6 +26,7 @@ function App() { } /> } /> } /> + } /> diff --git a/frontend/src/api/students.ts b/frontend/src/api/students.ts index f598ee6..22fc39e 100644 --- a/frontend/src/api/students.ts +++ b/frontend/src/api/students.ts @@ -2,13 +2,15 @@ import axiosInstance from './axiosInstance' interface StudentResponse { max_pages: number - students: { - first_name: string - last_name: string - index: number - mode: boolean - group: any - }[] + students: Student[] +} + +export interface Student { + first_name: string + last_name: string + index: number + mode: boolean + group?: any } export const getStudents = () => @@ -16,6 +18,9 @@ export const getStudents = () => 'http://127.0.0.1:5000/api/coordinator/students', ) +export const createStudent = (payload: Student) => + axiosInstance.post('http://127.0.0.1:5000/api/coordinator/students/', payload) + export const uploadStudents = (payload: FormData) => axiosInstance.post( 'http://127.0.0.1:5000/api/coordinator/students/upload/', diff --git a/frontend/src/components/InputError.tsx b/frontend/src/components/InputError.tsx new file mode 100644 index 0000000..ba00540 --- /dev/null +++ b/frontend/src/components/InputError.tsx @@ -0,0 +1,4 @@ +const InputError = (props: { children: React.ReactNode }) => ( +

{props.children}

+) +export default InputError diff --git a/frontend/src/views/coordinator/AddStudent.tsx b/frontend/src/views/coordinator/AddStudent.tsx new file mode 100644 index 0000000..472f43f --- /dev/null +++ b/frontend/src/views/coordinator/AddStudent.tsx @@ -0,0 +1,125 @@ +import { useState } from 'react' +import { useForm } from 'react-hook-form' +import { useMutation } from 'react-query' +import { createStudent, Student } from '../../api/students' +import InputError from '../../components/InputError' + +const AddStudent = () => { + const [isAlertVisible, setIsAlertVisible] = useState(false) + const { + register, + handleSubmit, + formState: { errors }, + reset, + } = useForm({ mode: 'onBlur' }) + + const { mutate: mutateCreateStudent } = useMutation( + 'createStudent', + (payload: Student) => createStudent(payload), + { + onSuccess: () => { + reset() + setIsAlertVisible(true) + }, + }, + ) + + const onSubmit = (data: Student) => { + mutateCreateStudent(data) + } + + return ( +
+ {isAlertVisible && ( +
+ Udało się dodać studenta! +
+ )} + +
+ + + {errors.first_name?.type === 'required' && ( + Imię jest wymagane + )} +
+
+ + + {errors.last_name?.type === 'required' && ( + Nazwisko jest wymagane + )} +
+
+ + + {errors.index?.type === 'required' && ( + Indeks jest wymagany + )} + {errors.index?.type === 'pattern' && ( + Indeks musi mieć 6 cyfr + )} +
+
+ +
+ + +
+
+ + +
+ {errors.mode?.type === 'required' && ( + Wybierz tryb studiów + )} +
+ +
+ ) +} + +export default AddStudent diff --git a/frontend/src/views/coordinator/Students.tsx b/frontend/src/views/coordinator/Students.tsx index a0d6d96..f786551 100644 --- a/frontend/src/views/coordinator/Students.tsx +++ b/frontend/src/views/coordinator/Students.tsx @@ -1,14 +1,12 @@ import React, { useState } from 'react' import { useMutation, useQuery } from 'react-query' +import { useNavigate } from 'react-router-dom' import { getStudents, uploadStudents } from '../../api/students' -const TableHeader = (props: { children: React.ReactNode }) => ( - - {props.children} - -) - const Students = () => { + let navigate = useNavigate() + const [showGroupless, setShowGroupless] = useState(false) + const { isLoading: isStudentsLoading, data: students, @@ -30,23 +28,19 @@ const Students = () => { mutateUpload(payload) } - const [state, setState] = useState({ - showGroupless: false - }); - if (isStudentsLoading) { return
Ładowanie
} return (
- -
- +
- Imie - Nazwisko - Indeks - Zapisany - Tryb + + + + + - {students?.data?.students?.filter( - (st) => (st.group === null || !state.showGroupless) - ).map( - ({ first_name, last_name, index, group, mode }) => ( + {students?.data?.students + ?.filter((st) => st.group === null || !showGroupless) + .map(({ first_name, last_name, index, group, mode }) => ( - - - - - + + + + + - ), - )} + ))}
ImięNazwiskoIndeksZapisanyTryb
- {first_name} - - {last_name} - - {index} - - {group === null ? 'Nie' : 'Tak'} - - {mode ? 'stacjonarny' : 'niestacjonarny'} - {first_name}{last_name}{index}{group === null ? 'Nie' : 'Tak'}{mode ? 'stacjonarny' : 'niestacjonarny'}
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 115da8e..c0b9ebc 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,9 +1,7 @@ module.exports = { - content: [ - "./src/**/*.{js,jsx,ts,tsx}", - ], + content: ['./src/**/*.{js,jsx,ts,tsx}'], theme: { extend: {}, }, - plugins: [], + plugins: [require('daisyui')], }