From 19de06a889cf25525515fddc4f71fea47594dec8 Mon Sep 17 00:00:00 2001 From: Alioune Badara FAM Date: Sat, 15 Oct 2022 11:27:51 +0000 Subject: [PATCH] Perform Voter CRUD in Dashboard --- package.json | 1 + pnpm-lock.yaml | 161 ++++++++++++++ src/pages/app/SignUp/SignUp.jsx | 56 +++-- src/pages/dashboard/Departments.jsx | 31 ++- src/pages/dashboard/Voters.jsx | 243 +++++++++++++++++++++- src/services/dashboard/DistrictService.js | 14 +- src/services/dashboard/VoterService.js | 49 +++++ 7 files changed, 512 insertions(+), 43 deletions(-) create mode 100644 src/services/dashboard/VoterService.js diff --git a/package.json b/package.json index e24fca2..6cd12f7 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@mui/lab": "5.0.0-alpha.102", "@mui/material": "^5.10.5", "@mui/x-data-grid": "^5.17.5", + "bcrypt": "^5.1.0", "firebase": "^9.10.0", "hamburger-react": "^2.5.0", "notistack": "^2.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b58688..ada2d62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,6 +13,7 @@ specifiers: '@types/react-dom': ^18.0.6 '@vitejs/plugin-react': ^2.1.0 autoprefixer: ^10.4.11 + bcrypt: ^5.1.0 firebase: ^9.10.0 hamburger-react: ^2.5.0 notistack: ^2.0.5 @@ -39,6 +40,7 @@ dependencies: '@mui/lab': 5.0.0-alpha.102_tirxtp2um5uo2dmuyw4cewlzpq '@mui/material': 5.10.5_af5ln35zuaotaffazii6n6bke4 '@mui/x-data-grid': 5.17.5_nfsb2jhsjja245b65g5s5d4aki + bcrypt: 5.1.0 firebase: 9.10.0 hamburger-react: 2.5.0_react@18.2.0 notistack: 2.0.5_l2qqre42bn772ugwcietgyxzoi @@ -2536,6 +2538,24 @@ packages: resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} dev: false + /@mapbox/node-pre-gyp/1.0.10: + resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==} + hasBin: true + dependencies: + detect-libc: 2.0.1 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.6.7 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.3.7 + tar: 6.1.11 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@mui/base/5.0.0-alpha.100_7ey2zzynotv32rpkwno45fsx4e: resolution: {integrity: sha512-bSoJEKCENtmJrJDECHUe9PiqztIUACuSskyqw9ypqE7Dz3WxL3e8puFsWBkUsz+WOCjXh4B4Xljn88Ucxxv5HA==} engines: {node: '>=12.0.0'} @@ -3720,6 +3740,10 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: false + /abbrev/1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: false + /accepts/1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -3884,6 +3908,18 @@ packages: normalize-path: 3.0.0 picomatch: 2.3.1 + /aproba/2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + dev: false + + /are-we-there-yet/2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.0 + dev: false + /arg/5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -4198,6 +4234,18 @@ packages: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} dev: false + /bcrypt/5.1.0: + resolution: {integrity: sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + '@mapbox/node-pre-gyp': 1.0.10 + node-addon-api: 5.0.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /bfj/7.0.2: resolution: {integrity: sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==} engines: {node: '>= 8.0.0'} @@ -4403,6 +4451,11 @@ packages: optionalDependencies: fsevents: 2.3.2 + /chownr/2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: false + /chrome-trace-event/1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} @@ -4471,6 +4524,11 @@ packages: /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + /color-support/1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: false + /colord/2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} dev: false @@ -4545,6 +4603,10 @@ packages: engines: {node: '>=0.8'} dev: false + /console-control-strings/1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + dev: false + /content-disposition/0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -4968,6 +5030,10 @@ packages: engines: {node: '>=0.4.0'} dev: false + /delegates/1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: false + /depd/1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -4983,6 +5049,11 @@ packages: engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dev: false + /detect-libc/2.0.1: + resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + engines: {node: '>=8'} + dev: false + /detect-newline/3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -6189,6 +6260,13 @@ packages: jsonfile: 6.1.0 universalify: 2.0.0 + /fs-minipass/2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.4 + dev: false + /fs-monkey/1.0.3: resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} dev: false @@ -6218,6 +6296,21 @@ packages: /functions-have-names/1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + /gauge/3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + dev: false + /gensync/1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -6375,6 +6468,10 @@ packages: dependencies: has-symbols: 1.0.3 + /has-unicode/2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + dev: false + /has/1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -7819,6 +7916,21 @@ packages: /minimist/1.2.6: resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + /minipass/3.3.4: + resolution: {integrity: sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==} + engines: {node: '>=8'} + dependencies: + yallist: 4.0.0 + dev: false + + /minizlib/2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.4 + yallist: 4.0.0 + dev: false + /mkdirp/0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -7826,6 +7938,12 @@ packages: minimist: 1.2.6 dev: false + /mkdirp/1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: false + /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false @@ -7870,6 +7988,10 @@ packages: tslib: 2.4.0 dev: false + /node-addon-api/5.0.0: + resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==} + dev: false + /node-fetch/2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -7894,6 +8016,14 @@ packages: /node-releases/2.0.6: resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + /nopt/5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -7937,6 +8067,15 @@ packages: path-key: 3.1.1 dev: false + /npmlog/5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + dev: false + /nth-check/1.0.2: resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} dependencies: @@ -9918,6 +10057,10 @@ packages: - supports-color dev: false + /set-blocking/2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: false + /setimmediate/1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} dev: false @@ -10364,6 +10507,18 @@ packages: engines: {node: '>=6'} dev: false + /tar/6.1.11: + resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==} + engines: {node: '>= 10'} + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 3.3.4 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: false + /temp-dir/2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -10989,6 +11144,12 @@ packages: isexe: 2.0.0 dev: false + /wide-align/1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + dependencies: + string-width: 4.2.3 + dev: false + /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} diff --git a/src/pages/app/SignUp/SignUp.jsx b/src/pages/app/SignUp/SignUp.jsx index 0ff7ba5..5ee725b 100644 --- a/src/pages/app/SignUp/SignUp.jsx +++ b/src/pages/app/SignUp/SignUp.jsx @@ -15,7 +15,9 @@ import ClearIcon from '@mui/icons-material/Clear'; import { Camera } from "react-camera-pro"; import { useRef } from 'react'; import { getAllDistricts } from '../../../services/dashboard/DistrictService'; - +import { addVoter } from '../../../services/dashboard/VoterService'; +import { useAuth } from '../../../context/AuthContext'; +// import bcrypt from "bcrypt"; export default function SignUp() { const [datePickerValue, setDatePickerValue] = useState(undefined); @@ -32,14 +34,14 @@ export default function SignUp() { const [openValidatePictureModal, setOpenValidatePictureModal] = React.useState(false); const handleOpenValidatePictureModal = () => setOpenValidatePictureModal(true); const handleCloseValidatePictureModal = () => setOpenValidatePictureModal(false); + const [lastFindErrorInFieldSnackbarId, setLastFindErrorInFieldSnackbarId] = useState(undefined); + const { signUp } = useAuth(); const resetForm = () => { reset(); setImage(null); setDatePickerValue(undefined); setRandomKeyForResetField(crypto.randomUUID()); - closeSnackbar(); - enqueueSnackbar('Message envoyée avec succès', { variant: 'success' }); } const getDistricts = () => { @@ -55,24 +57,34 @@ export default function SignUp() { if (Object.keys(errors).length != 0) { let fieldValues = Object.values(watch()).filter((e) => e !== "" && e !== undefined && e != null); if (fieldValues.length != 8) { - enqueueSnackbar('Veuillez remplir tous les champs correctement', { variant: 'warning', autoHideDuration: "1000" }) + closeSnackbar(lastFindErrorInFieldSnackbarId); + setLastFindErrorInFieldSnackbarId(enqueueSnackbar('Veuillez remplir tous les champs correctement', { variant: 'warning' })) } } }, [isSubmitting]) - const onSubmit = () => { + const onSubmit = async () => { if (image != null) { - console.log({ ...watch(), image }); + const voterData = { ...watch(), password: crypto.randomUUID().slice(0, 13), photo: image, canVoted: false, isAdmin: false, isSuperAdmin: false, isCandidate: false }; + console.log(voterData); + + await signUp(voterData.emailAddress, voterData.password); + + addVoter(voterData) + - resetForm(); + // resetForm(); + + + closeSnackbar(lastFindErrorInFieldSnackbarId); + enqueueSnackbar('On submit...', { variant: 'success' }); } else { - closeSnackbar(); + closeSnackbar(lastFindErrorInFieldSnackbarId); enqueueSnackbar('Veuillez d\'abord prendre une photo de vous', { variant: 'warning' }) } }; - return (
@@ -110,7 +122,7 @@ export default function SignUp() {
value !== undefined || "Veuillez indiquez votre date de naissance" })} + {...register("dateOfBirth", { validate: value => value !== undefined || "Veuillez indiquez votre date de naissance" })} containerStyle={{ width: "100%" }} @@ -118,7 +130,7 @@ export default function SignUp() { inputMode="none" value={datePickerValue} key={randomKeyForResetField} - onChange={(value) => (setDatePickerValue(value.format()), setValue('date', value.format()))} + onChange={(value) => (setDatePickerValue(value.format()), setValue('dateOfBirth', value.format()))} locale={fr} weekStartDayIndex={1} hideOnScroll @@ -145,15 +157,15 @@ export default function SignUp() { ) }} /> - {datePickerValue == undefined && errors.date?.message && {errors.date.message}} + {datePickerValue == undefined && errors.dateOfBirth?.message && {errors.dateOfBirth.message}}
- - Lieu de naissance + - {errors.lieuNaissance?.message && {errors.lieuNaissance.message}} + {errors.placeOfBirth?.message && {errors.placeOfBirth.message}}
@@ -169,7 +181,7 @@ export default function SignUp() {
- +
@@ -183,19 +195,19 @@ export default function SignUp() { key={randomKeyForResetField} loading={!districts.length ? true : false} loadingText="Récupération de la liste des quartiers..." - onChange={(e, value) => setValue("districts", value.nom)} + onChange={(e, value) => setValue("district", value.nom)} isOptionEqualToValue={(option, value) => option.nom === value.nom} getOptionLabel={(option) => option.nom} onOpen={getDistricts} noOptionsText="Quartier introuvable | Veuillez vérifier l'orthographe de votre quartier." sx={{ - border: "1.5px solid " + (!watch().districts && errors.districts?.message ? "#DC2626" : "#d1d5db"), borderRadius: "5px", padding: "0px", + border: "1.5px solid " + (!watch().district && errors.district?.message ? "#DC2626" : "#d1d5db"), borderRadius: "5px", padding: "0px", '&:focus-within': { - boxShadow: !watch().districts && errors.districts?.message ? "0px 0px 0px 0.8px #DC2626" : "0px 0px 0px 1.3px #7C3AED" + boxShadow: !watch().district && errors.district?.message ? "0px 0px 0px 0.8px #DC2626" : "0px 0px 0px 1.3px #7C3AED" } }} style={{ - borderLeft: !watch().districts && errors.districts?.message ? "10px solid #DC2626" : undefined, + borderLeft: !watch().district && errors.district?.message ? "10px solid #DC2626" : undefined, }} renderInput={ (params) => } /> - {!watch().districts && errors.districts?.message && {errors.districts.message}} + {!watch().district && errors.district?.message && {errors.district.message}}
diff --git a/src/pages/dashboard/Departments.jsx b/src/pages/dashboard/Departments.jsx index 794510e..2044729 100644 --- a/src/pages/dashboard/Departments.jsx +++ b/src/pages/dashboard/Departments.jsx @@ -19,9 +19,9 @@ import LoadingButton from '@mui/lab/LoadingButton'; export default function Departments() { const { enqueueSnackbar } = useSnackbar(); - const [oldEditingCell, setOldEditingCell] = useState({}) - const [openDeleteDepartmentAlert, setOpenDeleteDepartmentAlert] = useState(false) - const [departmentIdWhenDeleting, setDepartmentIdWhenDeleting] = useState() + const [oldEditingCell, setOldEditingCell] = useState({}); + const [openDeleteDepartmentAlert, setOpenDeleteDepartmentAlert] = useState(false); + const [departmentIdWhenDeleting, setDepartmentIdWhenDeleting] = useState(); const [departments, setDepartments] = useState([]); const [regionsNameWithId, setRegionsNameWithId] = useState([]); const [isFetchingData, setIsFetchingData] = useState(false); @@ -29,33 +29,32 @@ export default function Departments() { const updateDepartmentStateWhenAddingNewDepartment = (newDepartmentData) => { const newDepartmentDataWithId = { id: newDepartmentData.nom + newDepartmentData.latitude + newDepartmentData.longitude, ...newDepartmentData } - setDepartments([...departments, newDepartmentDataWithId]) + setDepartments([...departments, newDepartmentDataWithId]); } const getDepartments = () => { - setIsFetchingData(true) + setIsFetchingData(true); getAllDepartments() .then((response) => { if (!response.error) { setDepartments(response); } else - enqueueSnackbar(response.message, { variant: 'error' }) + enqueueSnackbar(response.message, { variant: 'error' }); }) - .catch(() => { }) - .finally(() => setIsFetchingData(false)) - } + .finally(() => setIsFetchingData(false)); + }; const getRegionsNameWithId = async () => { await getAllRegionsNameAndId() .then(res => setRegionsNameWithId(res)) - .catch(err => console.log(err)) - } + .catch(err => console.log(err)); + }; useEffect(() => { getDepartments(); getRegionsNameWithId(); - }, []) + }, []); const handleEditCell = (params, event, details) => { let { id, field, value } = params; @@ -69,7 +68,7 @@ export default function Departments() { return department; } }) - }) + }); if ((event.type !== "click" || (params.field === "regionName" && event.type === "click")) && JSON.stringify(oldEditingCell) !== JSON.stringify({ id, field, value })) { if (field == "regionName") { const [regionNameAndId] = regionsNameWithId.filter(region => region.nom == value); @@ -79,15 +78,15 @@ export default function Departments() { .then(() => enqueueSnackbar('Champ correctement modifié', { variant: 'success' })); } else { - enqueueSnackbar('Modification annulée', { variant: 'success' }) + enqueueSnackbar('Modification annulée', { variant: 'success' }); } }; const handleOldCell = (event) => { const { id, field, value } = event; - setOldEditingCell({ id, field, value }) + setOldEditingCell({ id, field, value }); }; const handleDelete = useCallback((id) => () => { - setIsDeleting(true) + setIsDeleting(true); deleteDepartment(id) .then(() => { setDepartments((departments) => departments.filter((department) => department.id != id)); diff --git a/src/pages/dashboard/Voters.jsx b/src/pages/dashboard/Voters.jsx index f56ced8..cc2a7a3 100644 --- a/src/pages/dashboard/Voters.jsx +++ b/src/pages/dashboard/Voters.jsx @@ -1,7 +1,244 @@ -import React from 'react' +import React from 'react'; +import { DataGrid, GridActionsCellItem, GridToolbarContainer, frFR, GridToolbarExport, GridToolbarColumnsButton, GridToolbarFilterButton, GridToolbarDensitySelector, GridToolbar } from '@mui/x-data-grid'; +import DeleteIcon from '@mui/icons-material/Delete'; +import AddRoundedIcon from '@mui/icons-material/AddRounded'; +import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; +import DoneRoundedIcon from '@mui/icons-material/DoneRounded'; +import { useMemo } from 'react'; +import { useCallback } from 'react'; +import { deleteVoter, updateVoter, addVoter, getAllVoters } from '../../services/dashboard/VoterService'; +import { useState } from 'react'; +import { Backdrop, Box, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, IconButton, LinearProgress, Typography, useMediaQuery } from '@mui/material'; +import { useSnackbar } from 'notistack'; +import { useForm } from 'react-hook-form'; +import { useEffect } from 'react'; +import LoadingButton from '@mui/lab/LoadingButton'; +import { Tooltip } from '@mui/material'; +import ClickAwayListener from '@mui/material/ClickAwayListener'; export default function Voters() { + const { enqueueSnackbar } = useSnackbar(); + const [oldEditingCell, setOldEditingCell] = useState({}) + const [openDeleteAlert, setOpenDeleteAlert] = useState(false) + const [openValidateAlert, setOpenValidateAlert] = useState(false) + const [voterSelectedData, setVoterSelectedData] = useState() + const [voters, setVoters] = useState([]); + const [isFetchingData, setIsFetchingData] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + const [isValidating, setIsValidating] = useState(false); + + const getVoters = () => { + setIsFetchingData(true) + getAllVoters() + .then((response) => { + if (!response.error) + setVoters(response) + else + enqueueSnackbar(response.message, { variant: 'error' }) + }) + .finally(() => setIsFetchingData(false)) + } + + useEffect(() => { + getVoters(); + }, []) + + const handleEditCell = (params, event, details) => { + const { id, field, value } = params; + if (JSON.stringify(oldEditingCell) !== JSON.stringify({ id, field, value })) { + updateVoter(id, { [field]: value }) + .then(() => { + enqueueSnackbar('Champ correctement modifié', { variant: 'success' }) + }); + } + else { + enqueueSnackbar('Aucune modification effectuée', { variant: 'success' }) + } + }; + const handleOldCell = (event) => { + const { id, field, value } = event; + setOldEditingCell({ id, field, value }) + }; + + const handleDelete = useCallback((id) => () => { + setIsDeleting(true); + deleteVoter(id).then(() => { + enqueueSnackbar('Électeur correctement supprimé', { variant: 'success' }); + setVoters((voters) => voters.filter((voter) => voter.id != id)); + setOpenDeleteAlert(false); + }) + .finally(() => setIsDeleting(false)); + }); + const handleClickOpenDeleteAlert = useCallback((id) => () => { + setVoterSelectedData(id) + setOpenDeleteAlert(true); + }); + const handleClickCloseDeleteAlert = () => { + if (!isDeleting) + setOpenDeleteAlert(false); + }; + + const handleClickOpenValidateAlert = useCallback((data) => () => { + setVoterSelectedData(data) + setOpenValidateAlert(true); + }); + const handleClickCloseValidateAlert = () => { + if (!isValidating) + setOpenValidateAlert(false); + + }; + const handleValidate = useCallback((data) => () => { + let { emailAddress: email } = data; + console.log(email); + }); + + const columns = useMemo + (() => [ + { + field: 'photo', headerName: 'Photo', minWidth: 100, flex: 0.5, renderCell: (params) => ( + + } arrow> + photo électeur + + ) + }, + { field: 'firstName', headerName: 'Nom', minWidth: 150, flex: 1, sort: "desc" }, + { field: 'lastName', headerName: 'Prénom', minWidth: 200, flex: 1 }, + { field: 'dateOfBirth', headerName: 'Date de Naissance', type: 'date', minWidth: 140, flex: 0.5 }, + { field: 'placeOfBirth', headerName: 'Lieu de Naissance', minWidth: 145, flex: 0.5 }, + { field: 'phoneNumber', headerName: 'Numéro de téléphone', minWidth: 160, flex: 0.5 }, + { field: 'emailAddress', headerName: 'Adresse email', minWidth: 250, flex: 0.5 }, + { field: 'district', headerName: 'Quartier', minWidth: 200, flex: 0.5 }, + { field: 'isRegistered', headerName: 'Inscrit', type: "boolean", minWidth: 60, flex: 0.2, editable: true }, + { field: 'isCandidate', headerName: 'Candidat', type: "boolean", minWidth: 85, flex: 0.2, editable: true }, + { field: 'isAdmin', headerName: 'Admin', type: "boolean", minWidth: 60, flex: 0.2, editable: true }, + { field: 'isSuperAdmin', headerName: 'Super Admin', type: "boolean", minWidth: 100, flex: 0.2, editable: true }, + { + field: 'actions', + type: 'actions', + headerName: "Actions", + width: 80, + editable: false, + getActions: ({ id, row }) => { + + return [ + } + label="Valider" + onClick={handleClickOpenValidateAlert(row)} + />, + } + label="Supprimer" + onClick={handleClickOpenDeleteAlert(id)} + /> + ] + }, + }, + ], + ); + return ( -
Voters
+ <> + {/* Delete dialog */} + + + {"Voulez-vous vraiment supprimer cet électeur ?"} + + + + ⚠️ Cette action est irréversible + + + + + + Oui, je veux le supprimer + + + + {/* End Delete dialog */} + + {/* Validate dialog */} + + + {"Voulez-vous ajouter cet utilisateur comme électeur ?"} + + + + + Oui, je veux l'ajouter + + + + {/* End Validate dialog */} + + + Gestion des électeurs du Sénégal + +
+
+ Liste de tous les électeurs +
+
+ + +
+
+ ) -} +} \ No newline at end of file diff --git a/src/services/dashboard/DistrictService.js b/src/services/dashboard/DistrictService.js index f28f678..c78b32f 100644 --- a/src/services/dashboard/DistrictService.js +++ b/src/services/dashboard/DistrictService.js @@ -1,4 +1,4 @@ -import { addDoc, collection, deleteDoc, doc, getDocs, serverTimestamp, updateDoc } from "firebase/firestore"; +import { addDoc, collection, deleteDoc, doc, getDocs, serverTimestamp, updateDoc, query, where } from "firebase/firestore"; import { db } from "../firebase"; import { getDocDepartmentRef } from "./DepartmentService"; import { getDocMunicipalitieRef } from "./MunicipalitieService"; @@ -65,4 +65,14 @@ export const getAllDistricts = async () => { } else return error; -} \ No newline at end of file +} + +export const getDistrictFromName = async (districtName) => { + let district = undefined; + const q = query(districtCollectionRef, where("nom", "==", districtName)); + const querySnapshot = await getDocs(q); + querySnapshot.forEach((doc) => { + district = { id: doc.id, ...doc.data() }; + }); + return district; +} diff --git a/src/services/dashboard/VoterService.js b/src/services/dashboard/VoterService.js new file mode 100644 index 0000000..7bc4905 --- /dev/null +++ b/src/services/dashboard/VoterService.js @@ -0,0 +1,49 @@ +import { addDoc, collection, deleteDoc, doc, getDoc, getDocs, serverTimestamp, updateDoc } from "firebase/firestore"; +import { db } from "../firebase"; + +export const voterCollectionRef = collection(db, "voters"); + +export const getVoterRef = (id) => { + return doc(voterCollectionRef, id) +} + +export const addVoter = async (data) => { + + await addDoc(voterCollectionRef, + { + ...data, + created_at: serverTimestamp() + } + ) +} + +export const deleteVoter = async (id) => { + await deleteDoc(getVoterRef(id)) +} + +export const updateVoter = async (id, data) => { + await updateDoc(getVoterRef(id), + { + updated_at: serverTimestamp(), + ...data + } + ) +} + +export const getAllVoters = async () => { + let voters = []; + let error = undefined; + + await getDocs(voterCollectionRef) + .then((response) => { + voters = response.docs.map(voter => ({ id: voter.id, ...voter.data() })) + }) + .catch(errs => { + error = { error: true, message: errs } + }) + if (voters.length != 0) { + return voters + } + else + return error; +}