Skip to content

Commit

Permalink
upload regions
Browse files Browse the repository at this point in the history
  • Loading branch information
Janderson Souza Matias authored and Janderson Souza Matias committed Feb 2, 2024
1 parent 493636a commit 0491dc0
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/i18n/langs/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ const enTranslation = {
delete: 'Delete',
total_schools: 'Total school: {{ value }}',
placeholder: 'Region name',
import: 'Import csv',
'import-title': 'Import regions file',
0: {
title: 'Regions',
new: 'New region',
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/langs/np.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ const npTranslation = {
edit: 'सम्पादन गर्नुहोस्',
total_schools: 'कुल विद्यालय: {{ value }}',
placeholder: 'क्षेत्रको नाम',
import: 'csv आयात गर्नुहोस्',
'import-title': 'क्षेत्र फाइल आयात गर्नुहोस्',
0: {
title: 'क्षेत्रहरू',
new: 'नयाँ क्षेत्र',
Expand Down
158 changes: 158 additions & 0 deletions src/pages/Settings/Regions/RegionImportModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import RegionService, { RegionBatchResponse } from '@/services/region';
import {
Box,
Button,
HStack,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Spinner,
Text,
VStack,
} from '@chakra-ui/react';
import Papa from 'papaparse';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';

type Props = {
isOpen: boolean;
onClose: (reload?: boolean) => void;
};

const RegionImportModal: React.FC<Props> = ({ isOpen, onClose }) => {
const { t } = useTranslation();
const [header, setHeader] = useState<string[]>([]);
const [arrayData, setArrayData] = useState<string[][]>([]);
const [isLoading, setIsLoading] = useState(false);
const [response, setResponse] = useState<RegionBatchResponse>();

const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
Papa.parse<string[]>(file, {
header: false,
complete: function (results) {
const [header, ...data] = results.data;

setArrayData(data);
setHeader(header);
},
});
}
};

const startImport = async () => {
setIsLoading(true);
const response = await RegionService.createRegionBatch(arrayData);
setResponse(response);
setIsLoading(false);
};

const onCancel = () => {
onClose();
setArrayData([]);
setIsLoading(false);
setResponse(undefined);
};

const onFinish = () => {
onClose();
setArrayData([]);
setIsLoading(false);
setResponse(undefined);
};

return (
<Modal isOpen={isOpen} onClose={onClose} scrollBehavior="inside" size="5xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>{t('settings.tabs.region.import-title')}</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<ModalBody>
<Spinner />
</ModalBody>
) : response ? (
<ModalBody>
<VStack w="full">
<Text fontSize={28} mb={8}>
{!!response.failItems?.length ? 'Items that failed to import' : 'All items was imported'}
</Text>
<HStack w="full" px={4} borderBottom="1px solid">
<Box fontWeight="bold" w="50px"></Box>
{header.map((_, index) => (
<Box flex={index === 1 ? 2 : 1} fontWeight="black">
{index === 0 ? 'Code' : index === 1 ? 'Name' : 'Region-' + (index - 2)}
</Box>
))}
</HStack>
{response?.failItems?.map((row, index) => (
<HStack w="full" bg={index % 2 ? '#f5f5f5' : '#ffffff'} px={4}>
<Box fontWeight="bold" w="50px">
{index + 1 + '.'}
</Box>
{row.map((item: string, itemIndex) => (
<Box flex={itemIndex === 1 ? 2 : 1}> {item}</Box>
))}
</HStack>
))}
</VStack>
</ModalBody>
) : (
<ModalBody>
{arrayData.length === 0 ? (
<input type="file" accept=".csv" onChange={handleFileUpload} />
) : (
<VStack w="full">
<HStack w="full" px={4} borderBottom="1px solid">
<Box fontWeight="bold" w="50px"></Box>
{header.map((_, index) => (
<Box flex={index === 1 ? 2 : 1} fontWeight="black">
{'Region-' + index}
</Box>
))}
</HStack>
{arrayData.map((row, index) => (
<HStack w="full" bg={index % 2 ? '#f5f5f5' : '#ffffff'} px={4}>
<Box fontWeight="bold" w="50px">
{index + 1 + '.'}
</Box>
{row.map((item: string, itemIndex) => (
<Box flex={itemIndex === 1 ? 2 : 1}> {item}</Box>
))}
</HStack>
))}
</VStack>
)}
</ModalBody>
)}

<ModalFooter>
{!!arrayData.length && <Text mr="auto">{'Total items: ' + arrayData.length}</Text>}
{!response && (
<Button variant="ghost" onClick={onCancel} isDisabled={isLoading}>
{t('common.cancel')}
</Button>
)}
{!isLoading && !response && (
<Button colorScheme="blue" mr={3} isDisabled={!arrayData.length} onClick={startImport}>
{t('common.import')}
</Button>
)}

{!!response && (
<Button colorScheme="blue" mr={3} isDisabled={!arrayData.length} onClick={onFinish}>
Finish
</Button>
)}
</ModalFooter>
</ModalContent>
</Modal>
);
};

export default RegionImportModal;
32 changes: 28 additions & 4 deletions src/pages/Settings/Regions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import Icon from '@/components/Base/Icon';
import Loader from '@/components/Base/Loader';
import Menu from '@/components/Menu';
import { IRegion, IUser } from '@/types';
import { IRegion } from '@/types';
import {
Button,
Center,
Expand All @@ -22,10 +22,12 @@ import RegionForm from './Form';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
import RegionService from '@/services/region';
import RegionImportModal from './RegionImportModal';

const Regions = () => {
const theme = useTheme();
const { t } = useTranslation();
const [importIsOpen, setImportIsOpen] = useState(false);
const [formIsOpen, setFormIsOpen] = useState(false);
const [deleteIsOpen, setDeleteIsOpen] = useState(false);
const [currentRegion, setCurrentRegion] = useState<IRegion>();
Expand Down Expand Up @@ -145,9 +147,29 @@ const Regions = () => {
</HStack>
))}

<HStack px={'16px'} py={'12px'} cursor={'pointer'} onClick={() => setFormIsOpen(true)}>
<Icon name={'plus'} color={theme.colors.Primary['$200']} />
<Text color={'Primary.$200'}>{t('settings.tabs.region.new')}</Text>
<HStack w="full">
<Button
px={'16px'}
py={'12px'}
cursor={'pointer'}
onClick={() => setFormIsOpen(true)}
variant="outline"
leftIcon={<Icon name={'plus'} color={theme.colors.Primary['$200']} />}
>
<Text color={'Primary.$200'}>{t('settings.tabs.region.new')}</Text>
</Button>
<Button
ml="auto"
px={'16px'}
py={'12px'}
cursor={'pointer'}
variant="solid"
justifyContent="center"
onClick={() => setImportIsOpen(true)}
leftIcon={<Icon name={'file-alt-solid'} color={theme.colors.Primary['$200']} />}
>
<Text color={theme.colors.Primary['$200']}>{t('settings.tabs.region.import')}</Text>
</Button>
</HStack>
</>
)}
Expand All @@ -169,6 +191,8 @@ const Regions = () => {
</ModalFooter>
</ModalContent>
</Modal>

<RegionImportModal isOpen={importIsOpen} onClose={() => setImportIsOpen(false)} />
</>
);
};
Expand Down
7 changes: 7 additions & 0 deletions src/services/region/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import _axios from '..';
import { IRegion } from '../../types';

export type RegionBatchResponse = {
successCount?: number;
failItems?: string[][];
};

export const RegionService = {
deleteRegion: async (regionId: string): Promise<IRegion[]> => (await _axios.delete(`region/${regionId}`)).data,
getRegions: async (): Promise<IRegion[]> => (await _axios.get('region')).data,
getRegionsTree: async (): Promise<IRegion[]> => (await _axios.get('region/tree')).data,
getRegion: async (id: string): Promise<IRegion> => (await _axios.get(`region/${id}`)).data,
saveRegion: async (region: Partial<IRegion>): Promise<IRegion> => (await _axios.post('region', region)).data,
createRegionBatch: async (batch: string[][]): Promise<RegionBatchResponse> =>
(await _axios.post(`region/batch`, { batch })).data,
};

export default RegionService;

0 comments on commit 0491dc0

Please sign in to comment.