diff --git a/package.json b/package.json index a560c0c..229374d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "date-fns-jalali": "^2.13.0-0", "dayjs": "^1.11.9", "eslint": "8.36.0", + "file-saver": "^2.0.5", "formik": "^2.2.9", "fs-extra": "^11.1.1", "image-resize": "^1.3.2", diff --git a/src/components/dashboard/reports/ExcelExport/index.jsx b/src/components/dashboard/reports/ExcelExport/index.jsx index fd3a789..2ab8e35 100644 --- a/src/components/dashboard/reports/ExcelExport/index.jsx +++ b/src/components/dashboard/reports/ExcelExport/index.jsx @@ -2,42 +2,108 @@ import {Accordion, AccordionDetails, AccordionSummary, Box, Button, Stack, Typog import FileUploadIcon from '@mui/icons-material/FileUpload'; import AssignmentIcon from '@mui/icons-material/Assignment'; import {useTranslations} from "next-intl"; +import ProvinceFilter from "@/components/dashboard/reports/Filter/ProvinceFilter"; +import YearFilter from "@/components/dashboard/reports/Filter/YearFilter"; +import {useState} from "react"; +import {useFormik} from "formik"; +import {GET_EXPORT} from "@/core/data/apiRoutes"; +import moment from "jalali-moment"; +import FileSaver from 'file-saver'; +import useRequest from "@/lib/app/hooks/useRequest"; const ExcelExport = () => { const t = useTranslations(); + const requestServer = useRequest() + const filterItem = [ + {type: "province", type_fa: "استان", multiple: false}, + {type: "year", type_fa: "سال", multiple: false} + ]; + const filter_by_province = filterItem.find((item) => item.type === "province"); + const filter_by_year = filterItem.find((item) => item.type === "year"); + + const [filteredText, setFilteredText] = useState({province: "", year: ""}); + + const formik = useFormik({ + initialValues: { + ...(filter_by_year && { + date: filter_by_year.multiple ? [] : `${moment().format('jYYYY')}` + }), + ...(filter_by_province && { + province_id: filter_by_province.multiple ? [] : "" + }), + from_date: "", + to_date: "" + }, onSubmit: (values, {setSubmitting}) => { + const province = values.province_id; + const fields = [ + values.from_date ? {key: "from_date", value: values.from_date} : null, + values.to_date ? {key: "to_date", value: values.to_date} : null, + ...(Array.isArray(province) + ? province.length !== 0 + ? province.map((item, index) => ({key: `province_id[${index}]`, value: item})) + : [] + : province + ? [{key: "province_id", value: province}] + : [] + ), + ].filter(Boolean); + const queryString = fields.length !== 0 + ? `?${fields.map((option) => `${option.key}=${option.value}`).join('&')}` + : ''; + requestServer(`${GET_EXPORT}${queryString}`, 'GET', {auth: true, requestOptions: {responseType: 'blob'}}) + .then((response) => { + const filename = `گزارش درخواست های وام ناوگان تاریخ_${moment().format('jYYYY_jMM_jDD')}.xlsx`; + FileSaver.saveAs(response.data, filename); + }).catch(() => { + }).finally(() => { + setSubmitting(false); + }); + }, + }); return ( - - - - - - - {t("filters.excel_export")} - + + + + + + + + {t("filters.excel_export")} + + + - - - - - - - - - - - - + + + + {filter_by_province ? + : "" + } + {filter_by_year ? + : "" + } + + + + + + + ) }; diff --git a/src/components/dashboard/reports/Filter/ProvinceFilter.jsx b/src/components/dashboard/reports/Filter/ProvinceFilter.jsx index 12ec7dc..16a1133 100644 --- a/src/components/dashboard/reports/Filter/ProvinceFilter.jsx +++ b/src/components/dashboard/reports/Filter/ProvinceFilter.jsx @@ -1,6 +1,17 @@ -import {Box, FormControl, InputLabel, MenuItem, OutlinedInput, Select, Typography} from "@mui/material"; +import { + Box, + FormControl, + IconButton, + InputAdornment, + InputLabel, + MenuItem, + OutlinedInput, + Select, + Typography +} from "@mui/material"; import {useTranslations} from "next-intl"; import useProvince from "@/lib/app/hooks/useProvince"; +import ClearIcon from '@mui/icons-material/Clear'; const provinceList = [ {name: "قزوین", id: 1}, @@ -32,6 +43,9 @@ const ProvinceFilter = ({formik, multiple, setFilteredText}) => { })); } }; + const needAdorment = multiple + ? formik.values.province_id.length === 0 : + formik.values.province_id === ""; return ( {t("filters.choose_province")} @@ -48,7 +62,7 @@ const ProvinceFilter = ({formik, multiple, setFilteredText}) => { renderValue={multiple ? (selected) => ( {selected.map((value) => { const selectedProvince = provinceList.find(province => province.id === value); @@ -60,6 +74,19 @@ const ProvinceFilter = ({formik, multiple, setFilteredText}) => { })} ) : undefined} + endAdornment={ + !needAdorment ? + + multiple + ? formik.setFieldValue('province_id', []) + : formik.setFieldValue('province_id', "") + } + size="small"> + + + : null + } > {isLoadingProvinceList ? ( diff --git a/src/components/dashboard/reports/Filter/YearFilter.jsx b/src/components/dashboard/reports/Filter/YearFilter.jsx index 0f7aff6..66a3db6 100644 --- a/src/components/dashboard/reports/Filter/YearFilter.jsx +++ b/src/components/dashboard/reports/Filter/YearFilter.jsx @@ -1,20 +1,11 @@ import {Box, Chip, FormControl, InputLabel, MenuItem, OutlinedInput, Select} from "@mui/material"; import moment from "jalali-moment"; import {useTranslations} from "next-intl"; +import {periodOfYear} from "@/core/utils/yearPeriodFinder"; const currentShamsiYear = moment().format('jYYYY'); const shamsiYearList = Array.from({length: currentShamsiYear - 1399}, (_, index) => (currentShamsiYear - index).toString()); -const periodOfYear = (year) => { - const isLeapYear = moment.jIsLeapYear(year); - const firstDayGeorgian = moment.from(`${year}/01/01`, 'fa').format('YYYY-MM-DD'); - const lastDayGeorgian = moment.from(`${year}/12/${isLeapYear ? '30' : '29'}`, 'fa').format('YYYY-MM-DD'); - return { - from: firstDayGeorgian, - to: lastDayGeorgian - } -} - const YearFilter = ({formik, multiple, setFilteredText}) => { const t = useTranslations(); const yearChange = (event) => { diff --git a/src/components/dashboard/reports/Filter/index.jsx b/src/components/dashboard/reports/Filter/index.jsx index b5b0a23..f8757c1 100644 --- a/src/components/dashboard/reports/Filter/index.jsx +++ b/src/components/dashboard/reports/Filter/index.jsx @@ -1,4 +1,14 @@ -import {Accordion, AccordionDetails, AccordionSummary, Box, Button, Stack, Typography} from "@mui/material"; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Box, + Button, + CircularProgress, + IconButton, + Stack, + Typography +} from "@mui/material"; import {useFormik} from "formik"; import ProvinceFilter from "../Filter/ProvinceFilter"; import YearFilter from "../Filter/YearFilter"; @@ -6,26 +16,35 @@ import FilterAltIcon from '@mui/icons-material/FilterAlt'; import EqualizerIcon from '@mui/icons-material/Equalizer'; import {useTranslations} from "next-intl"; import TroubleshootIcon from '@mui/icons-material/Troubleshoot'; -import {useState} from "react"; +import CachedIcon from '@mui/icons-material/Cached'; +import {useEffect, useState} from "react"; +import moment from "jalali-moment"; +import {periodOfYear} from "@/core/utils/yearPeriodFinder"; -const Filter = ({title, filterItem, setFilterOption}) => { +CachedIcon +const Filter = ({title, filterItem, setFilterOption, isLoadingReportList, mutate}) => { const t = useTranslations(); const filter_by_province = filterItem.find((item) => item.type === "province"); const filter_by_year = filterItem.find((item) => item.type === "year"); + const [expanded, setExpanded] = useState(false); const [filteredText, setFilteredText] = useState({province: "", year: ""}); + const [stepValue, setStepValue] = useState({ + ...(filter_by_year && { + date: filter_by_year.multiple ? [] : `${moment().format('jYYYY')}` + }), + ...(filter_by_province && { + province_id: filter_by_province.multiple ? [] : "" + }), + from_date: periodOfYear(moment().format('jYYYY')).from, + to_date: periodOfYear(moment().format('jYYYY')).to + }) + const formik = useFormik({ - initialValues: { - ...(filter_by_year && { - date: filter_by_year.multiple ? [] : "" - }), - ...(filter_by_province && { - province_id: filter_by_province.multiple ? [] : "" - }), - from_date: "", - to_date: "" - }, onSubmit: (values, {setSubmitting}) => { + enableReinitialize: true, + initialValues: stepValue, onSubmit: (values, {setSubmitting}) => { + setStepValue(values) const province = values.province_id; const fields = [ values.from_date ? {key: "from_date", value: values.from_date} : null, @@ -39,28 +58,54 @@ const Filter = ({title, filterItem, setFilterOption}) => { : [] ), ].filter(Boolean); - console.log("fields", fields) setFilterOption(fields) - setSubmitting(false) }, }); + + useEffect(() => { + const province = formik.values.province_id; + const fields = [ + formik.values.from_date ? {key: "from_date", value: formik.values.from_date} : null, + formik.values.to_date ? {key: "to_date", value: formik.values.to_date} : null, + ...(Array.isArray(province) + ? province.length !== 0 + ? province.map((item, index) => ({key: `province_id[${index}]`, value: item})) + : [] + : province + ? [{key: "province_id", value: province}] + : [] + ), + ].filter(Boolean); + setFilterOption(fields) + }, []); + return ( - { + }} sx={{ mb: 2, borderBottom: 2, borderBottomColor: "divider", - backgroundColor: "#f1f1f1" + backgroundColor: "#f1f1f1", }}> - + - دریافت گزارش تکمیلی + {title} {filteredText.province} {filteredText.year} - + + mutate()} + size="small"> + + + setExpanded((prevExpanded) => !prevExpanded)} + size="small"> + + + @@ -76,12 +121,13 @@ const Filter = ({title, filterItem, setFilterOption}) => { - ) }; diff --git a/src/components/dashboard/reports/LoanDetails/index.jsx b/src/components/dashboard/reports/LoanDetails/index.jsx index 111535b..aaf5090 100644 --- a/src/components/dashboard/reports/LoanDetails/index.jsx +++ b/src/components/dashboard/reports/LoanDetails/index.jsx @@ -1,6 +1,5 @@ import {Grid, Paper} from "@mui/material"; import {useTranslations} from "next-intl"; -import Filter from "@/components/dashboard/reports/Filter"; import RadialBarSemiCircularChart from "@/components/dashboard/reports/LoanDetails/RadialBarSemiCircularChart"; import RadialBarStrokedChart from "@/components/dashboard/reports/LoanDetails/RadialBarStrokedChart"; @@ -9,12 +8,14 @@ const LoanDetails = () => { const t = useTranslations(); return ( - + {/**/} diff --git a/src/components/dashboard/reports/LoanDistribution/index.jsx b/src/components/dashboard/reports/LoanDistribution/index.jsx index 9872291..1bf2abb 100644 --- a/src/components/dashboard/reports/LoanDistribution/index.jsx +++ b/src/components/dashboard/reports/LoanDistribution/index.jsx @@ -8,8 +8,7 @@ import {GET_LOAN_DISTRIBUTION} from "@/core/data/apiRoutes"; const LoanDistribution = () => { const t = useTranslations(); - const {errorReportList, isLoadingReportList, reportList, setFilterOption} = useChart(GET_LOAN_DISTRIBUTION); - + const {errorReportList, isLoadingReportList, reportList, setFilterOption, mutate} = useChart(GET_LOAN_DISTRIBUTION); return ( { {type: "year", type_fa: "سال", multiple: false} ]} setFilterOption={setFilterOption} + isLoadingReportList={isLoadingReportList} + mutate={mutate} /> {isLoadingReportList ? ( diff --git a/src/components/dashboard/reports/LoanProgress/index.jsx b/src/components/dashboard/reports/LoanProgress/index.jsx index 7bc6a35..0c7bf11 100644 --- a/src/components/dashboard/reports/LoanProgress/index.jsx +++ b/src/components/dashboard/reports/LoanProgress/index.jsx @@ -7,7 +7,7 @@ import {GET_PROVINCE_PROGRESS} from "@/core/data/apiRoutes"; const LoanProgress = () => { const t = useTranslations(); - const {errorReportList, isLoadingReportList, reportList, setFilterOption} = useChart(GET_PROVINCE_PROGRESS); + const {errorReportList, isLoadingReportList, reportList, setFilterOption, mutate} = useChart(GET_PROVINCE_PROGRESS); return ( { {type: "year", type_fa: "سال", multiple: false} ]} setFilterOption={setFilterOption} + isLoadingReportList={isLoadingReportList} + mutate={mutate} /> diff --git a/src/core/data/apiRoutes.js b/src/core/data/apiRoutes.js index f7025ee..7ca5fca 100644 --- a/src/core/data/apiRoutes.js +++ b/src/core/data/apiRoutes.js @@ -199,7 +199,7 @@ export const GET_PERMISSIONS_LIST = //role management // reports -export const GET_EXPORTS = BASE_URL + "/dashboard/exports/navgan" +export const GET_EXPORT = BASE_URL + "/dashboard/exports/navgan" export const GET_PROVINCE_PROGRESS = BASE_URL + "/dashboard/reports/navgan/province_progress" export const GET_LOAN_DISTRIBUTION = BASE_URL + "/dashboard/reports/navgan/loan_distribution" // reports diff --git a/src/core/utils/yearPeriodFinder.js b/src/core/utils/yearPeriodFinder.js new file mode 100644 index 0000000..73c01b9 --- /dev/null +++ b/src/core/utils/yearPeriodFinder.js @@ -0,0 +1,11 @@ +import moment from "jalali-moment"; + +export const periodOfYear = (year) => { + const isLeapYear = moment.jIsLeapYear(year); + const firstDayGeorgian = moment.from(`${year}/01/01`, 'fa').format('YYYY-MM-DD'); + const lastDayGeorgian = moment.from(`${year}/12/${isLeapYear ? '30' : '29'}`, 'fa').format('YYYY-MM-DD'); + return { + from: firstDayGeorgian, + to: lastDayGeorgian + } +} \ No newline at end of file diff --git a/src/lib/app/hooks/useChart.jsx b/src/lib/app/hooks/useChart.jsx index c010a03..707f21d 100644 --- a/src/lib/app/hooks/useChart.jsx +++ b/src/lib/app/hooks/useChart.jsx @@ -8,7 +8,6 @@ const useChart = (chart_url) => { const queryString = filterOption.length !== 0 ? `?${filterOption.map((option) => `${option.key}=${option.value}`).join('&')}` : ''; - console.log("check query", queryString) //swr config const fetcher = (...args) => { return requestServer(args, 'get').then(({data}) => { @@ -17,17 +16,23 @@ const useChart = (chart_url) => { }) }; - const {data, isLoading} = useSWR(`${chart_url}${queryString}`, fetcher, { + const { + data, + isValidating, + mutate + } = useSWR(filterOption.length !== 0 ? `${chart_url}${queryString}` : "", fetcher, { revalidateIfStale: false, revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateOnReconnect: false, + keepPreviousData: true }); return { reportList: data, - isLoadingReportList: isLoading, + isLoadingReportList: isValidating, errorReportList: !data, - setFilterOption: setFilterOption + setFilterOption: setFilterOption, + mutate: mutate } }; diff --git a/src/lib/app/hooks/useProvince.jsx b/src/lib/app/hooks/useProvince.jsx index d0fbda0..3443a6e 100644 --- a/src/lib/app/hooks/useProvince.jsx +++ b/src/lib/app/hooks/useProvince.jsx @@ -16,7 +16,8 @@ const useProvince = () => { const {data, isLoading} = useSWR(GET_PROVINCE_LIST, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateOnReconnect: false, + keepPreviousData: true }); return { diff --git a/src/lib/app/hooks/useRole.jsx b/src/lib/app/hooks/useRole.jsx index fc3dd18..b4c231c 100644 --- a/src/lib/app/hooks/useRole.jsx +++ b/src/lib/app/hooks/useRole.jsx @@ -16,7 +16,8 @@ const useRole = () => { const {data, isLoading} = useSWR(GET_ROLE_LIST, fetcher, { revalidateIfStale: false, revalidateOnFocus: false, - revalidateOnReconnect: false + revalidateOnReconnect: false, + keepPreviousData: true }); return {