diff --git a/package.json b/package.json index 025467a..4b4da96 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,14 @@ "@fontsource/noto-sans": "^5.0.17", "@hookform/resolvers": "^3.1.1", "@superset-ui/embedded-sdk": "^0.1.0-alpha.9", + "@types/papaparse": "^5.3.14", "@types/styled-system": "^5.1.16", "axios": "^1.3.4", "chart.js": "^4.4.0", "date-fns": "^2.30.0", "framer-motion": "^10.2.3", "i18next": "^22.4.11", + "papaparse": "^5.4.1", "qrcode": "^1.5.3", "qrcode.react": "^3.1.0", "react": "^18.2.0", diff --git a/src/components/HeaderPage/index.tsx b/src/components/HeaderPage/index.tsx index f303ee4..7440135 100644 --- a/src/components/HeaderPage/index.tsx +++ b/src/components/HeaderPage/index.tsx @@ -9,9 +9,17 @@ type Props = { newButtonValue?: string | null; onClickNew?: () => void; onClickDownload?: () => void; + onClickImport?: () => void; }; -const HeaderPage: React.FC = ({ title, subtitle, newButtonValue, onClickNew, onClickDownload }) => { +const HeaderPage: React.FC = ({ + title, + subtitle, + newButtonValue, + onClickImport, + onClickNew, + onClickDownload, +}) => { const { t } = useTranslation(); return ( @@ -25,6 +33,12 @@ const HeaderPage: React.FC = ({ title, subtitle, newButtonValue, onClickN + {onClickImport && ( + + )} + {onClickDownload && ( + )} + {!isLoading && !response && ( + + )} + + {!!response && ( + + )} + + + + ); +}; + +export default SchoolImportModal; diff --git a/src/pages/Schools/index.tsx b/src/pages/Schools/index.tsx index 4d4d8ee..edc9249 100644 --- a/src/pages/Schools/index.tsx +++ b/src/pages/Schools/index.tsx @@ -19,6 +19,7 @@ import HeaderPage from '@/components/HeaderPage'; import { useTranslation } from 'react-i18next'; import SchoolForm from './SchoolForm'; import handleDownloadJSON from '@/common/download'; +import SchoolImportModal from './SchoolImportModal'; const SchoolsPage: React.FC = () => { const { t } = useTranslation(); @@ -28,6 +29,7 @@ const SchoolsPage: React.FC = () => { const [isLoadingDelete, setIsLoadingDelete] = useState(false); const [schoolToEdit, setSchoolToEdit] = useState(); const [schoolToDelete, setSchoolToDelete] = useState(); + const [showImportModal, setShowImportModal] = useState(false); useEffect(() => { loadSchools(); @@ -67,6 +69,11 @@ const SchoolsPage: React.FC = () => { setSchoolToDelete(undefined); }; + const onCloseImport = (reload?: boolean) => { + setShowImportModal(false); + if (reload) loadSchools(); + }; + return ( { title={t('Navbar.schools')} newButtonValue={t('school.new-school')} onClickNew={() => setNewSchool(true)} + onClickImport={() => setShowImportModal(true)} onClickDownload={() => handleDownloadJSON(schools, t('Navbar.schools').toLowerCase().replace(' ', '-'))} /> { ) : ( )} + + + diff --git a/src/pages/Settings/Regions/index.tsx b/src/pages/Settings/Regions/index.tsx index 74e77e8..a83ab23 100644 --- a/src/pages/Settings/Regions/index.tsx +++ b/src/pages/Settings/Regions/index.tsx @@ -3,7 +3,21 @@ import Icon from '@/components/Base/Icon'; import Loader from '@/components/Base/Loader'; import Menu from '@/components/Menu'; import { IRegion, IUser } from '@/types'; -import { Center, HStack, Text, VStack, useTheme } from '@chakra-ui/react'; +import { + Button, + Center, + HStack, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Text, + VStack, + useTheme, +} from '@chakra-ui/react'; import RegionForm from './Form'; import { toast } from 'react-toastify'; import { useTranslation } from 'react-i18next'; @@ -13,6 +27,7 @@ const Regions = () => { const theme = useTheme(); const { t } = useTranslation(); const [formIsOpen, setFormIsOpen] = useState(false); + const [deleteIsOpen, setDeleteIsOpen] = useState(false); const [currentRegion, setCurrentRegion] = useState(); const [regions, setRegions] = useState({ isLoading: true, @@ -43,9 +58,9 @@ const Regions = () => { const menuOptions = [ { label: t('settings.tabs.region.edit'), - handleClick: (user: IUser) => { + handleClick: (region: IRegion) => { setFormIsOpen(true); - setCurrentRegion(user); + setCurrentRegion(region); }, }, ]; @@ -55,6 +70,18 @@ const Regions = () => { setCurrentRegion(undefined); }; + const deleteRegion = async () => { + if (currentRegion?.id) { + setRegions({ + isLoading: true, + data: [] as IRegion[], + }); + setDeleteIsOpen(false); + await RegionService.deleteRegion(currentRegion.id); + refreshRegions(); + } + }; + return ( <> { - + { + setDeleteIsOpen(true); + setCurrentRegion(region); + }, + }, + ]), + ]} + currentItem={region} + /> ))} @@ -109,6 +152,23 @@ const Regions = () => { )} + + setDeleteIsOpen(false)}> + + + Confirm Deletion + + Are you sure you want to delete this region? + + + + + + ); }; diff --git a/src/services/region/index.ts b/src/services/region/index.ts index 47490ae..c7b4d02 100644 --- a/src/services/region/index.ts +++ b/src/services/region/index.ts @@ -2,6 +2,7 @@ import _axios from '..'; import { IRegion } from '../../types'; export const RegionService = { + deleteRegion: async (regionId: string): Promise => (await _axios.delete(`region/${regionId}`)).data, getRegions: async (): Promise => (await _axios.get('region')).data, getRegionsTree: async (): Promise => (await _axios.get('region/tree')).data, getRegion: async (id: string): Promise => (await _axios.get(`region/${id}`)).data, diff --git a/src/services/school/index.ts b/src/services/school/index.ts index af52429..57a8c7a 100644 --- a/src/services/school/index.ts +++ b/src/services/school/index.ts @@ -1,6 +1,11 @@ import _axios from '..'; import { ISchool } from '../../types'; +export type SchoolBatchResponse = { + successCount?: number; + failItems?: string[][]; +}; + export const SchoolService = { getSchool: async (id: string): Promise => (await _axios.get(`school/${id}`)).data, getSchools: async (): Promise => (await _axios.get('school')).data, @@ -10,6 +15,8 @@ export const SchoolService = { generateQrCode: async (schoolId: string): Promise => (await _axios.get(`school/qrcode/${schoolId}`)).data, findAllDistrictsByRegion: async (regionId: string): Promise => (await _axios.get(`school/${regionId}/districts`)).data, + createSchoolBatch: async (batch: string[][]): Promise => + (await _axios.post(`school/batch`, { batch })).data, }; export default SchoolService; diff --git a/yarn.lock b/yarn.lock index b1c57ed..83d6c5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1563,11 +1563,25 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.197.tgz#e95c5ddcc814ec3e84c891910a01e0c8a378c54b" integrity sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g== +"@types/node@*": + version "20.10.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198" + integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg== + dependencies: + undici-types "~5.26.4" + "@types/node@^18.14.6": version "18.17.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.5.tgz#c58b12bca8c2a437b38c15270615627e96dd0bc5" integrity sha512-xNbS75FxH6P4UXTPUJp/zNPq6/xsfdJKussCWNOnz4aULWIRwMgP1LgaB5RiBnMX1DPCYenuqGZfnIAx5mbFLA== +"@types/papaparse@^5.3.14": + version "5.3.14" + resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.3.14.tgz#345cc2a675a90106ff1dc33b95500dfb30748031" + integrity sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g== + dependencies: + "@types/node" "*" + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -3435,6 +3449,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +papaparse@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.4.1.tgz#f45c0f871853578bd3a30f92d96fdcfb6ebea127" + integrity sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -4102,6 +4121,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + update-browserslist-db@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940"