LFFE-10 report page filter completed and problem of request solved and working on excel export

This commit is contained in:
2023-11-13 17:18:25 +03:30
parent 98d459dc38
commit f48672274d
13 changed files with 165 additions and 98 deletions

View File

@@ -478,14 +478,21 @@
"cost_of_loan_refer_to_bank": "مبلغ وام های معرفی به بانک",
"number_of_loan_refer_to_bank": "تعداد وام های معرفی به بانک",
"cost_of_payed_loan": "مبلغ وام های پرداخت شده",
"number_of_payed_loan": "تعداد وام های پرداخت شده"
"number_of_payed_loan": "تعداد وام های پرداخت شده",
"loading_fetching_reports": "درحال دریافت گزارشات",
"error_fetching_reports": "خطا در دریافت گزارشات"
},
"filters": {
"choose_province": "انتخاب استان",
"choose_year": "انتخاب سال",
"provinces": "استان های",
"province": "استان",
"year": "سال",
"years": "سال های",
"text_field_loading_provinces_list": "درحال دریافت استان ها",
"text_field_error_fetching_provinces": "خطا در دریافت استان ها"
"text_field_error_fetching_provinces": "خطا در دریافت استان ها",
"filter": "اعمال فیلتر",
"excel_export": "خروجی به اکسل",
"export": "دریافت"
}
}

View File

@@ -0,0 +1,44 @@
import {Accordion, AccordionDetails, AccordionSummary, Box, Button, Stack, Typography} from "@mui/material";
import FileUploadIcon from '@mui/icons-material/FileUpload';
import AssignmentIcon from '@mui/icons-material/Assignment';
import {useTranslations} from "next-intl";
const ExcelExport = () => {
const t = useTranslations();
return (
<Accordion elevation={0} sx={{
width: "95%",
mx: "auto",
mb: 2,
borderBottom: 2,
borderBottomColor: "divider",
backgroundColor: "#f1f1f1"
}}>
<AccordionSummary>
<Box sx={{width: "100%", display: "flex", alignItems: "center", justifyContent: "space-between"}}>
<Box sx={{display: "flex", alignItems: "center", gap: 0.5}}>
<AssignmentIcon sx={{color: "primary.main"}}/>
<Typography sx={{fontWeight: 500, color: "primary.main"}}>
{t("filters.excel_export")}
</Typography>
</Box>
<FileUploadIcon sx={{color: "primary.main"}}/>
</Box>
</AccordionSummary>
<AccordionDetails
sx={{display: "flex", justifyContent: "space-between", alignItems: "center", mx: "auto",}}>
<Stack sx={{flexDirection: {xs: "column", sm: "row"}, alignItems: "center", flexWrap: 'wrap'}}>
</Stack>
<Stack>
<Button variant="contained" sx={{backgroundColor: "success.main"}}>
{t("filters.export")}
</Button>
</Stack>
</AccordionDetails>
</Accordion>
)
};
export default ExcelExport;

View File

@@ -34,7 +34,7 @@ const ProvinceFilter = ({formik, multiple, setFilteredText}) => {
};
return (
<FormControl sx={{width: 300, mx: 1}}>
<InputLabel size="small">انتخاب استان</InputLabel>
<InputLabel size="small">{t("filters.choose_province")}</InputLabel>
<Select
name="province_id"
multiple={multiple}
@@ -43,7 +43,7 @@ const ProvinceFilter = ({formik, multiple, setFilteredText}) => {
value={formik.values.province_id}
onChange={provinceChange}
onBlur={formik.handleBlur("province_id")}
input={<OutlinedInput label="انتخاب استان"/>}
input={<OutlinedInput label={t("filters.choose_province")}/>}
disabled={isLoadingProvinceList || errorProvinceList}
renderValue={multiple ? (selected) => (
<Box sx={{

View File

@@ -5,12 +5,25 @@ import {useTranslations} from "next-intl";
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) => {
const {target: {value}} = event;
formik.handleChange(event);
formik.setFieldValue('date', value);
const {from, to} = periodOfYear(value);
formik.setFieldValue('from_date', from);
formik.setFieldValue('to_date', to);
if (Array.isArray(value)) {
setFilteredText(prevState => ({
...prevState,
@@ -25,7 +38,7 @@ const YearFilter = ({formik, multiple, setFilteredText}) => {
};
return (
<FormControl sx={{width: 300, mx: 1}}>
<InputLabel size="small">انتخاب سال</InputLabel>
<InputLabel size="small">{t("filters.choose_year")}</InputLabel>
<Select
name="date"
multiple={multiple}
@@ -34,7 +47,7 @@ const YearFilter = ({formik, multiple, setFilteredText}) => {
value={formik.values.date}
onChange={yearChange}
onBlur={formik.handleBlur("date")}
input={<OutlinedInput label="انتخاب سال"/>}
input={<OutlinedInput label={t("filters.choose_year")}/>}
renderValue={multiple ? (selected) => (
<Box sx={{display: 'flex', flexWrap: 'wrap', gap: 0.5}}>
{selected.map((value) => (
@@ -44,14 +57,16 @@ const YearFilter = ({formik, multiple, setFilteredText}) => {
</Box>
) : undefined}
>
{shamsiYearList.map((year) => (
<MenuItem
key={year}
value={year}
>
{year}
</MenuItem>
))}
{
shamsiYearList.map((year) => (
<MenuItem
key={year}
value={year}
>
{year}
</MenuItem>
))
}
</Select>
</FormControl>
)

View File

@@ -1,25 +1,14 @@
import {Accordion, AccordionDetails, AccordionSummary, Box, Button, Stack, Typography} from "@mui/material";
import {useFormik} from "formik";
import ProvinceFilter from "./ProvinceFilter";
import YearFilter from "./YearFilter";
import ProvinceFilter from "../Filter/ProvinceFilter";
import YearFilter from "../Filter/YearFilter";
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 moment from "jalali-moment";
import {useState} from "react";
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 Filter = ({title, filterItem, expanded}) => {
const Filter = ({title, filterItem, setFilterOption}) => {
const t = useTranslations();
const filter_by_province = filterItem.find((item) => item.type === "province");
const filter_by_year = filterItem.find((item) => item.type === "year");
@@ -33,8 +22,26 @@ const Filter = ({title, filterItem, expanded}) => {
}),
...(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);
console.log("fields", fields)
setFilterOption(fields)
setSubmitting(false)
},
});
return (
@@ -48,7 +55,7 @@ const Filter = ({title, filterItem, expanded}) => {
<Box sx={{width: "100%", display: "flex", alignItems: "center", justifyContent: "space-between"}}>
<Box sx={{display: "flex", alignItems: "center", gap: 0.5}}>
<EqualizerIcon sx={{color: "primary.main"}}/>
<Typography sx={{fontWeight: 500, color: "primary.main"}}>{title}</Typography>
<Typography sx={{fontWeight: 500, color: "primary.main"}}>دریافت گزارش تکمیلی</Typography>
<Typography variant="caption" sx={{fontWeight: 500}} color="error">
{filteredText.province} {filteredText.year}
</Typography>
@@ -71,7 +78,7 @@ const Filter = ({title, filterItem, expanded}) => {
<Button onClick={formik.handleSubmit} variant="contained"
disabled={formik.isSubmitting || !formik.dirty}
startIcon={<TroubleshootIcon/>}
>اعمال فیلتر</Button>
>{t("filters.filter")}</Button>
</Stack>
</AccordionDetails>

View File

@@ -5,17 +5,10 @@ const Pie = ({data}) => {
title: {
text: undefined,
},
plotOptions: {
pie: {
fill: {
colors: ['#F99417', '#363062', '#005B41', '#C70039', '#EE9322']
}
},
},
fill: {
opacity: 0.9,
},
labels: [' آیتم 1', ' آیتم 2', ' آیتم 3', ' آیتم 4', ' آیتم 5'],
labels: data.map((item) => ` ${item.state_name}`),
dataLabels: {
enabled: true,
style: {
@@ -24,7 +17,7 @@ const Pie = ({data}) => {
},
},
};
const series = [14, 23, 21, 17, 15]
const series = data.map((item) => +item.percentage)
return (
<Chart chartId="LoanDistributionPie" type="pie" specialOption={specialOption} series={series}/>
)

View File

@@ -11,15 +11,12 @@ const PolarChart = ({data}) => {
strokeWidth: 2,
strokeColor: '#e3e3e3',
},
fill: {
colors: ['#F99417', '#363062', '#005B41', '#C70039', '#EE9322']
}
},
},
fill: {
opacity: 0.9,
},
labels: [' آیتم 1', ' آیتم 2', ' آیتم 3', ' آیتم 4', ' آیتم 5'],
labels: data.map((item) => ` ${item.state_name}`),
dataLabels: {
enabled: true,
style: {
@@ -28,7 +25,7 @@ const PolarChart = ({data}) => {
},
},
};
const series = [14, 23, 21, 17, 15]
const series = data.map((item) => +item.percentage)
return (
<Chart chartId="LoanDistributionPolar" type="polarArea" specialOption={specialOption} series={series}/>
)

View File

@@ -1,60 +1,56 @@
import {Grid, Paper} from "@mui/material";
import {Box, CircularProgress, Grid, Paper, Typography} from "@mui/material";
import PolarChart from "./PolarChart";
import Pie from "./PieChart";
import {useTranslations} from "next-intl";
import Filter from "@/components/dashboard/reports/Filter";
const fakeData = [
{name: "آذربایجان شرقی", data: 0},
{name: "آذربایجان غربی", data: 33},
{name: "اردبیل", data: 90},
{name: "اصفهان", data: 30},
{name: "البرز", data: 70},
{name: "ایلام", data: 32},
{name: "بوشهر", data: 12},
{name: "تهران", data: 44},
{name: "چهارمحال و بختیاری", data: 95},
{name: "خراسان جنوبی", data: 30},
{name: "خراسان رضوی", data: 70},
{name: "خراسان شمالی", data: 10},
{name: "خوزستان", data: 28},
{name: "زنجان", data: 85},
{name: "سمنان", data: 30},
{name: "سیستان و بلوچستان", data: 70},
{name: "فارس", data: 10},
{name: "قزوین", data: 12},
{name: "قم", data: 40},
{name: "کردستان", data: 85},
{name: "کرمان", data: 30},
{name: "کرمانشاه", data: 67},
{name: "کهگیلویه و بویراحمد", data: 100},
{name: "گلستان", data: 97},
{name: "گیلان", data: 5},
{name: "لرستان", data: 27},
{name: "مازندران", data: 31},
{name: "مرکزی", data: 37},
{name: "هرمزگان", data: 46},
{name: "همدان", data: 52},
{name: "یزد", data: 60}
];
import useChart from "@/lib/app/hooks/useChart";
import {GET_LOAN_DISTRIBUTION} from "@/core/data/apiRoutes";
const LoanDistribution = () => {
const t = useTranslations();
const {errorReportList, isLoadingReportList, reportList, setFilterOption} = useChart(GET_LOAN_DISTRIBUTION);
return (
<Paper elevation={2} sx={{width: "95%", mx: "auto", my: 2, pb: 2, background: "#f7f7f7e6"}}>
<Filter title={t("reports.loan_distribution")}
filterItem={[
{type: "province", type_fa: "استان", multiple: false},
{type: "province", type_fa: "استان", multiple: true},
{type: "year", type_fa: "سال", multiple: false}
]}
setFilterOption={setFilterOption}
/>
<Grid container sx={{justifyContent: {xs: "center", md: "space-between"}}}>
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
<PolarChart data={fakeData}/>
</Grid>
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
<Pie data={fakeData}/>
</Grid>
{isLoadingReportList ? (
<Box sx={{
height: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center"
}}>
<Grid item xs={12} sx={{aspectRatio: "1/0.75"}}>
<CircularProgress sx={{mb: 1, color: "#c2c2c2"}}/>
<Typography variant="h6"
sx={{color: "#c2c2c2", letterSpacing: "0.2rem"}}
>
{t("reports.loading_fetching_reports")}
</Typography>
</Grid>
</Box>
) : errorReportList ? (
<Grid item xs={12} sx={{aspectRatio: "1/0.75"}}>
<Typography variant="caption">{t("reports.error_fetching_reports")}</Typography>
</Grid>
) : (
<>
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
<PolarChart data={reportList}/>
</Grid>
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
<Pie data={reportList}/>
</Grid>
</>
)}
</Grid>
</Paper>
)

View File

@@ -2,10 +2,9 @@ import {calculateGradientColor} from "@/core/utils/gradientColorHandler";
import Chart from "@/core/components/Chart";
const BarChart = ({data}) => {
console.log(data)
const usableArray = data.map(({province_name, percentage, ...rest}) => ({
x: province_name,
y: percentage,
y: +percentage,
...rest,
}));
let colorArray = data.map((obj) => calculateGradientColor(obj.percentage));

View File

@@ -7,19 +7,18 @@ import {GET_PROVINCE_PROGRESS} from "@/core/data/apiRoutes";
const LoanProgress = () => {
const t = useTranslations();
const {errorReportList, isLoadingReportList, reportList} = useChart(GET_PROVINCE_PROGRESS);
console.log(reportList)
const {errorReportList, isLoadingReportList, reportList, setFilterOption} = useChart(GET_PROVINCE_PROGRESS);
return (
<Paper elevation={2} sx={{width: "95%", mx: "auto", my: 2, pb: 2, background: "#f7f7f7e6"}}>
<Filter title={t("reports.loan_progress")}
filterItem={[
{type: "year", type_fa: "سال", multiple: false}
]}
setFilterOption={setFilterOption}
/>
<Grid container>
<Grid item xs={12} sx={{aspectRatio: {xs: "1/1", sm: "1/1", md: "1/0.4", xl: "1/0.3"}}}>
{1 === 1 ? (
{isLoadingReportList ? (
<Box sx={{
height: "100%",
display: "flex",
@@ -31,11 +30,11 @@ const LoanProgress = () => {
<Typography variant="h6"
sx={{color: "#c2c2c2", letterSpacing: "0.2rem"}}
>
درحال دریافت اطلاعات
{t("reports.loading_fetching_reports")}
</Typography>
</Box>
) : errorReportList ? (
<Typography variant="caption">درحال دریافت اطلاعات</Typography>
<Typography variant="caption">{t("reports.error_fetching_reports")}</Typography>
) : (
<BarChart data={reportList}/>
)}

View File

@@ -1,10 +1,12 @@
import LoanProgress from "@/components/dashboard/reports/LoanProgress";
import LoanDistribution from "@/components/dashboard/reports/LoanDistribution";
import LoanDetails from "@/components/dashboard/reports/LoanDetails";
import ExcelExport from "@/components/dashboard/reports/ExcelExport";
const DashboardReportsComponent = (props) => {
return (
<>
<ExcelExport/>
<LoanProgress/>
<LoanDistribution/>
<LoanDetails/>

View File

@@ -199,5 +199,7 @@ export const GET_PERMISSIONS_LIST =
//role management
// reports
export const GET_EXPORTS = 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

View File

@@ -1,9 +1,14 @@
import useRequest from "@/lib/app/hooks/useRequest";
import useSWR from "swr";
import {useState} from "react";
const useChart = (chart_url) => {
const requestServer = useRequest({auth: true, notification: false})
const [filterOption, setFilterOption] = useState([]);
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}) => {
@@ -12,7 +17,7 @@ const useChart = (chart_url) => {
})
};
const {data, isLoading} = useSWR(chart_url, fetcher, {
const {data, isLoading} = useSWR(`${chart_url}${queryString}`, fetcher, {
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false
@@ -21,7 +26,8 @@ const useChart = (chart_url) => {
return {
reportList: data,
isLoadingReportList: isLoading,
errorReportList: !data
errorReportList: !data,
setFilterOption: setFilterOption
}
};