bring structure and working on filters
This commit is contained in:
@@ -603,6 +603,11 @@
|
||||
"loading_permissions_list": "درحال دریافت لیست دسترسی ها"
|
||||
},
|
||||
"reports": {
|
||||
"dynamic_label": "{done} از {total} انجام شده",
|
||||
"expert": "عملکرد کارشناس",
|
||||
"transportation_assistant": "عملکرد معاون",
|
||||
"province_manager": "عملکرد مدیر",
|
||||
"bank": "عملکرد بانک",
|
||||
"loan_progress": "درصد پیشرفت تسویه وام",
|
||||
"loan_distribution": "درصد توزیع وام ها در هر مرحله",
|
||||
"filter_as": "فیلتر های اعمال شده",
|
||||
|
||||
@@ -12,8 +12,8 @@ import FileUploadIcon from '@mui/icons-material/FileUpload';
|
||||
import AssignmentIcon from '@mui/icons-material/Assignment';
|
||||
import BackupIcon from '@mui/icons-material/Backup';
|
||||
import {useTranslations} from "next-intl";
|
||||
import ProvinceFilter from "@/components/dashboard/reports/Filter/ProvinceFilter";
|
||||
import YearFilter from "@/components/dashboard/reports/Filter/YearFilter";
|
||||
import ProvinceFilter from "@/components/dashboard/old-reports/Filter/ProvinceFilter";
|
||||
import YearFilter from "@/components/dashboard/old-reports/Filter/YearFilter";
|
||||
import {useState} from "react";
|
||||
import {useFormik} from "formik";
|
||||
import {GET_EXPORT} from "@/core/data/apiRoutes";
|
||||
111
src/components/dashboard/old-reports/Filter/ProvinceFilter.jsx
Normal file
111
src/components/dashboard/old-reports/Filter/ProvinceFilter.jsx
Normal file
@@ -0,0 +1,111 @@
|
||||
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},
|
||||
{name: "تبریز", id: 2},
|
||||
{name: "مشهد", id: 3}
|
||||
]
|
||||
|
||||
const ProvinceFilter = ({formik, multiple, setFilteredText}) => {
|
||||
const {errorProvinceList, isLoadingProvinceList, provinceList} = useProvince();
|
||||
const t = useTranslations();
|
||||
const provinceChange = (event) => {
|
||||
const {target: {value}} = event;
|
||||
formik.handleChange(event);
|
||||
formik.setFieldValue('province_id', value);
|
||||
if (Array.isArray(value)) {
|
||||
const province_names = []
|
||||
value.map((item) => {
|
||||
province_names.push(provinceList.find(province => province.id === item).name)
|
||||
})
|
||||
setFilteredText(prevState => ({
|
||||
...prevState,
|
||||
province: province_names.length !== 0 ? `| ${t("filters.provinces")}: ${province_names.join(' , ')}` : ""
|
||||
}));
|
||||
} else {
|
||||
const selectedProvince = provinceList.find(province => province.id === value);
|
||||
setFilteredText(prevState => ({
|
||||
...prevState,
|
||||
province: `| ${t("filters.province")}: ${selectedProvince.name}`
|
||||
}));
|
||||
}
|
||||
};
|
||||
const needAdorment = multiple
|
||||
? formik.values.province_id.length === 0 :
|
||||
formik.values.province_id === "";
|
||||
return (
|
||||
<FormControl sx={{width: 300, mx: 1, my: {xs: 1, lg: 0}}}>
|
||||
<InputLabel size="small">{t("filters.choose_province")}</InputLabel>
|
||||
<Select
|
||||
name="province_id"
|
||||
multiple={multiple}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
value={formik.values.province_id}
|
||||
onChange={provinceChange}
|
||||
onBlur={formik.handleBlur("province_id")}
|
||||
input={<OutlinedInput label={t("filters.choose_province")}/>}
|
||||
disabled={isLoadingProvinceList || errorProvinceList}
|
||||
renderValue={multiple ? (selected) => (
|
||||
<Box sx={{
|
||||
display: 'flex', gap: 0.7, overflow: "hidden",
|
||||
width: '100%', pr: 4
|
||||
}}>
|
||||
{selected.map((value) => {
|
||||
const selectedProvince = provinceList.find(province => province.id === value);
|
||||
return (
|
||||
<Typography key={value} variant="button">
|
||||
{selectedProvince ? selectedProvince.name : ''}
|
||||
</Typography>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
) : undefined}
|
||||
endAdornment={
|
||||
!needAdorment ?
|
||||
<InputAdornment position="end" sx={{mr: 2}}>
|
||||
<IconButton
|
||||
onClick={() => multiple
|
||||
? formik.setFieldValue('province_id', [])
|
||||
: formik.setFieldValue('province_id', "")
|
||||
}
|
||||
size="small">
|
||||
<ClearIcon/>
|
||||
</IconButton>
|
||||
</InputAdornment> : null
|
||||
}
|
||||
>
|
||||
{isLoadingProvinceList ? (
|
||||
<MenuItem>
|
||||
{t("filters.text_field_loading_provinces_list")}
|
||||
</MenuItem>
|
||||
) : errorProvinceList ? (
|
||||
<MenuItem>
|
||||
{t("filters.text_field_error_fetching_provinces")}
|
||||
</MenuItem>
|
||||
) : (
|
||||
provinceList.map((item) => (
|
||||
<MenuItem key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
))
|
||||
)}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)
|
||||
};
|
||||
|
||||
export default ProvinceFilter;
|
||||
66
src/components/dashboard/old-reports/Filter/YearFilter.jsx
Normal file
66
src/components/dashboard/old-reports/Filter/YearFilter.jsx
Normal file
@@ -0,0 +1,66 @@
|
||||
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 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,
|
||||
year: `| ${t("filters.years")}: ${value.join(' , ')}`
|
||||
}));
|
||||
} else {
|
||||
setFilteredText(prevState => ({
|
||||
...prevState,
|
||||
year: `| ${t("filters.year")}: ${value}`
|
||||
}));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<FormControl sx={{width: 300, mx: 1, my: {xs: 1, lg: 0}}}>
|
||||
<InputLabel size="small">{t("filters.choose_year")}</InputLabel>
|
||||
<Select
|
||||
name="date"
|
||||
multiple={multiple}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
value={formik.values.date}
|
||||
onChange={yearChange}
|
||||
onBlur={formik.handleBlur("date")}
|
||||
input={<OutlinedInput label={t("filters.choose_year")}/>}
|
||||
renderValue={multiple ? (selected) => (
|
||||
<Box sx={{display: 'flex', flexWrap: 'wrap', gap: 0.5}}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={value}
|
||||
sx={{color: "#fff", backgroundColor: "primary.dark"}}/>
|
||||
))}
|
||||
</Box>
|
||||
) : undefined}
|
||||
>
|
||||
{
|
||||
shamsiYearList.map((year) => (
|
||||
<MenuItem
|
||||
key={year}
|
||||
value={year}
|
||||
>
|
||||
{year}
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)
|
||||
};
|
||||
|
||||
export default YearFilter;
|
||||
144
src/components/dashboard/old-reports/Filter/index.jsx
Normal file
144
src/components/dashboard/old-reports/Filter/index.jsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
Stack,
|
||||
Tooltip,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
import {useFormik} from "formik";
|
||||
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 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, 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({
|
||||
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,
|
||||
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);
|
||||
setFilterOption(fields)
|
||||
},
|
||||
});
|
||||
|
||||
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 (
|
||||
<Accordion elevation={0} expanded={expanded} onChange={() => {
|
||||
}} sx={{
|
||||
mb: 2,
|
||||
borderBottom: 2,
|
||||
borderBottomColor: "divider",
|
||||
backgroundColor: "#f1f1f1",
|
||||
}}>
|
||||
<AccordionSummary sx={{cursor: "unset !important"}}>
|
||||
<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 variant="caption" sx={{fontWeight: 500}} color="error">
|
||||
{filteredText.province} {filteredText.year}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Tooltip title={t("filters.update_again")} arrow>
|
||||
<IconButton onClick={() => mutate()}
|
||||
size="small">
|
||||
<CachedIcon color="primary"/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("filters.filter_btn")} arrow>
|
||||
<IconButton onClick={() => setExpanded((prevExpanded) => !prevExpanded)}
|
||||
size="small">
|
||||
<FilterAltIcon color="primary"/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{
|
||||
display: "flex",
|
||||
flexDirection: {xs: "column", sm: "row"},
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}>
|
||||
<Stack sx={{flexDirection: {xs: "column", sm: "row"}, alignItems: "center", flexWrap: 'wrap'}}>
|
||||
{filter_by_province ?
|
||||
<ProvinceFilter formik={formik} multiple={filter_by_province.multiple}
|
||||
setFilteredText={setFilteredText}/> : ""
|
||||
}
|
||||
{filter_by_year ?
|
||||
<YearFilter formik={formik} multiple={filter_by_year.multiple}
|
||||
setFilteredText={setFilteredText}/> : ""
|
||||
}
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Button onClick={formik.handleSubmit} variant="contained"
|
||||
disabled={isLoadingReportList || !formik.dirty}
|
||||
startIcon={isLoadingReportList ?
|
||||
<CircularProgress size={20} color="inherit"/> :
|
||||
<TroubleshootIcon/>}
|
||||
>{t("filters.filter")}</Button>
|
||||
</Stack>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)
|
||||
};
|
||||
|
||||
export default Filter;
|
||||
@@ -0,0 +1,46 @@
|
||||
import Chart from "@/core/components/Chart";
|
||||
import {calculateGradientColor} from "@/core/utils/gradientColorHandler";
|
||||
|
||||
const RadialBarSemiCircularChart = ({data, label}) => {
|
||||
const specialOption = {
|
||||
title: {
|
||||
text: undefined,
|
||||
},
|
||||
plotOptions: {
|
||||
radialBar: {
|
||||
startAngle: -110,
|
||||
endAngle: 110,
|
||||
track: {
|
||||
background: "#e7e7e7",
|
||||
strokeWidth: '80%',
|
||||
},
|
||||
dataLabels: {
|
||||
name: {
|
||||
fontSize: '20px',
|
||||
offsetY: 110,
|
||||
color: "#8c8c8c"
|
||||
},
|
||||
value: {
|
||||
offsetY: 50,
|
||||
fontSize: '50px',
|
||||
fontWeight: 500,
|
||||
color: "#8c8c8c",
|
||||
formatter: function (val) {
|
||||
return val + "%";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
fill: {
|
||||
colors: calculateGradientColor(data)
|
||||
},
|
||||
labels: [label],
|
||||
};
|
||||
const series = [data]
|
||||
return (
|
||||
<Chart chartId="LoanProgressBar" type="radialBar" specialOption={specialOption} series={series}/>
|
||||
)
|
||||
};
|
||||
|
||||
export default RadialBarSemiCircularChart;
|
||||
37
src/components/dashboard/old-reports/LoanDetails/index.jsx
Normal file
37
src/components/dashboard/old-reports/LoanDetails/index.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import {Grid, Paper} from "@mui/material";
|
||||
import {useTranslations} from "next-intl";
|
||||
import RadialBarSemiCircularChart from "@/components/dashboard/old-reports/LoanDetails/RadialBarSemiCircularChart";
|
||||
import RadialBarStrokedChart from "@/components/dashboard/old-reports/LoanDetails/RadialBarStrokedChart";
|
||||
|
||||
|
||||
const LoanDetails = () => {
|
||||
const t = useTranslations();
|
||||
return (
|
||||
<Paper elevation={2} sx={{width: "95%", mx: "auto", my: 2, pb: 2, background: "#f7f7f7e6"}}>
|
||||
{/*<Filter title={t("reports.loan_details")}*/}
|
||||
{/* filterItem={[*/}
|
||||
{/* {type: "province", type_fa: "استان", multiple: false},*/}
|
||||
{/* {type: "year", type_fa: "سال", multiple: false}*/}
|
||||
{/* ]}*/}
|
||||
{/* setFilterOption={setFilterOption}*/}
|
||||
{/* isLoadingReportList={isLoadingReportList}*/}
|
||||
{/*/>*/}
|
||||
<Grid container sx={{justifyContent: {xs: "center", md: "space-between"}}}>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarSemiCircularChart data={20} label={t("reports.cost_of_loan_refer_to_bank")}/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarSemiCircularChart data={60} label={t("reports.number_of_loan_refer_to_bank")}/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarSemiCircularChart data={95} label={t("reports.cost_of_payed_loan")}/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarStrokedChart data={15} label={t("reports.number_of_payed_loan")}/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
)
|
||||
};
|
||||
|
||||
export default LoanDetails;
|
||||
@@ -2,7 +2,7 @@ import {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";
|
||||
import Filter from "@/components/dashboard/old-reports/Filter";
|
||||
import useChart from "@/lib/app/hooks/useChart";
|
||||
import {GET_LOAN_DISTRIBUTION} from "@/core/data/apiRoutes";
|
||||
import LoadingHardPage from "@/core/components/LoadingHardPage";
|
||||
16
src/components/dashboard/old-reports/index.jsx
Normal file
16
src/components/dashboard/old-reports/index.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import LoanProgress from "@/components/dashboard/old-reports/LoanProgress";
|
||||
import LoanDistribution from "@/components/dashboard/old-reports/LoanDistribution";
|
||||
import ExcelExport from "@/components/dashboard/old-reports/ExcelExport";
|
||||
|
||||
const DashboardReportsComponent = (props) => {
|
||||
return (
|
||||
<>
|
||||
<ExcelExport/>
|
||||
<LoanProgress/>
|
||||
<LoanDistribution/>
|
||||
{/*<LoanDetails/>*/}
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
export default DashboardReportsComponent;
|
||||
@@ -20,13 +20,11 @@ import TroubleshootIcon from '@mui/icons-material/Troubleshoot';
|
||||
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, 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({
|
||||
@@ -36,11 +34,10 @@ const Filter = ({title, filterItem, setFilterOption, isLoadingReportList, mutate
|
||||
...(filter_by_province && {
|
||||
province_id: filter_by_province.multiple ? [] : ""
|
||||
}),
|
||||
from_date: periodOfYear(moment().format('jYYYY')).from,
|
||||
to_date: periodOfYear(moment().format('jYYYY')).to
|
||||
from_date: "",
|
||||
to_date: ""
|
||||
})
|
||||
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: stepValue, onSubmit: (values, {setSubmitting}) => {
|
||||
|
||||
@@ -8,21 +8,21 @@ const RadialBarSemiCircularChart = ({data, label}) => {
|
||||
},
|
||||
plotOptions: {
|
||||
radialBar: {
|
||||
startAngle: -110,
|
||||
endAngle: 110,
|
||||
startAngle: -90,
|
||||
endAngle: 90,
|
||||
track: {
|
||||
background: "#e7e7e7",
|
||||
strokeWidth: '80%',
|
||||
},
|
||||
dataLabels: {
|
||||
name: {
|
||||
fontSize: '20px',
|
||||
offsetY: 110,
|
||||
fontSize: '1.1rem',
|
||||
offsetY: 20,
|
||||
color: "#8c8c8c"
|
||||
},
|
||||
value: {
|
||||
offsetY: 50,
|
||||
fontSize: '50px',
|
||||
offsetY: -50,
|
||||
fontSize: '30px',
|
||||
fontWeight: 500,
|
||||
color: "#8c8c8c",
|
||||
formatter: function (val) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import {Grid, Typography} from "@mui/material";
|
||||
import {useTranslations} from "next-intl";
|
||||
import LoadingHardPage from "@/core/components/LoadingHardPage";
|
||||
import AnalyticsIcon from "@mui/icons-material/Analytics";
|
||||
import RadialBarSemiCircularChart from "@/components/dashboard/reports/LoanDetails/RadialBarSemiCircularChart";
|
||||
|
||||
const FunctionalityCharts = () => {
|
||||
const t = useTranslations();
|
||||
const DataItemsName = [
|
||||
{id: "expert", name: t("reports.expert")},
|
||||
{id: "transportation_assistant", name: t("reports.transportation_assistant")},
|
||||
{id: "province_manager", name: t("reports.province_manager")},
|
||||
{id: "bank", name: t("reports.bank")}
|
||||
]
|
||||
|
||||
return (
|
||||
<Grid container sx={{justifyContent: {xs: "center", position: "relative"}}}>
|
||||
{isLoadingReportList ? (
|
||||
<Grid item xs={12} sx={{aspectRatio: "3/0.95"}}>
|
||||
<LoadingHardPage
|
||||
icon={<AnalyticsIcon sx={{width: "inherit", height: "inherit"}}/>}
|
||||
loading={isLoadingReportList}
|
||||
label={t("reports.loading_fetching_reports")}
|
||||
width={100}
|
||||
height={100}
|
||||
sx={{position: "absolute", bgcolor: "#f7f7f7e6"}}
|
||||
/>
|
||||
</Grid>
|
||||
) : errorReportList ? (
|
||||
<Grid item xs={12}
|
||||
sx={{aspectRatio: "3/0.95", display: "flex", justifyContent: "center", alignItems: "center"}}>
|
||||
<Typography variant="h6" sx={{
|
||||
color: "#858585",
|
||||
letterSpacing: "0.1rem"
|
||||
}}>{t("reports.error_fetching_reports")}</Typography>
|
||||
</Grid>
|
||||
) : (
|
||||
<>
|
||||
{
|
||||
Object.entries(reportList).map(([entryKey, innerObject]) => {
|
||||
let percentage = innerObject.total !== 0 ? (innerObject.total - innerObject.pending) / innerObject.total * 100 : 0;
|
||||
const ItemName = DataItemsName.find(item => item.id === entryKey).name;
|
||||
return (
|
||||
<Grid key={entryKey} item xs={12} sm={10} md={6} xl={3}
|
||||
sx={{textAlign: "center"}}>
|
||||
<Typography sx={{
|
||||
fontSize: "1.3rem",
|
||||
fontWeight: "600",
|
||||
color: "#8c8c8c"
|
||||
}}>{ItemName}</Typography>
|
||||
<RadialBarSemiCircularChart data={Math.round(percentage)}
|
||||
label={t("reports.dynamic_label", {
|
||||
total: innerObject.total,
|
||||
done: innerObject.total - innerObject.pending
|
||||
})}/>
|
||||
</Grid>
|
||||
)
|
||||
})
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
)
|
||||
};
|
||||
|
||||
export default FunctionalityCharts;
|
||||
@@ -1,34 +1,93 @@
|
||||
import {Grid, Paper} from "@mui/material";
|
||||
import {Grid, Paper, Typography} from "@mui/material";
|
||||
import {useTranslations} from "next-intl";
|
||||
import Filter from "@/components/dashboard/reports/Filter";
|
||||
import useChart from "@/lib/app/hooks/useChart";
|
||||
import {GET_LOAN_EXPERTPROGRESS, GET_LOAN_REFERREDLOANS} from "@/core/data/apiRoutes";
|
||||
import LoadingHardPage from "@/core/components/LoadingHardPage";
|
||||
import AnalyticsIcon from "@mui/icons-material/Analytics";
|
||||
import RadialBarSemiCircularChart from "@/components/dashboard/reports/LoanDetails/RadialBarSemiCircularChart";
|
||||
import RadialBarStrokedChart from "@/components/dashboard/reports/LoanDetails/RadialBarStrokedChart";
|
||||
|
||||
|
||||
const LoanDetails = () => {
|
||||
const t = useTranslations();
|
||||
const DataItemsName = [
|
||||
{id: "expert", name: t("reports.expert")},
|
||||
{id: "transportation_assistant", name: t("reports.transportation_assistant")},
|
||||
{id: "province_manager", name: t("reports.province_manager")},
|
||||
{id: "bank", name: t("reports.bank")}
|
||||
]
|
||||
|
||||
const {
|
||||
errorReportList: errorReportList1,
|
||||
isLoadingReportList: isLoadingReportList1,
|
||||
reportList: reportList1,
|
||||
setFilterOption: setFilterOption1,
|
||||
mutate: mutate1
|
||||
} = useChart(GET_LOAN_EXPERTPROGRESS);
|
||||
|
||||
// Use the useChart hook for the second API route
|
||||
const {
|
||||
errorReportList: errorReportList2,
|
||||
isLoadingReportList: isLoadingReportList2,
|
||||
reportList: reportList2,
|
||||
setFilterOption: setFilterOption2,
|
||||
mutate: mutate2
|
||||
} = useChart(GET_LOAN_REFERREDLOANS);
|
||||
|
||||
return (
|
||||
<Paper elevation={2} sx={{width: "95%", mx: "auto", my: 2, pb: 2, background: "#f7f7f7e6"}}>
|
||||
{/*<Filter title={t("reports.loan_details")}*/}
|
||||
{/* filterItem={[*/}
|
||||
{/* {type: "province", type_fa: "استان", multiple: false},*/}
|
||||
{/* {type: "year", type_fa: "سال", multiple: false}*/}
|
||||
{/* ]}*/}
|
||||
{/* setFilterOption={setFilterOption}*/}
|
||||
{/* isLoadingReportList={isLoadingReportList}*/}
|
||||
{/*/>*/}
|
||||
<Grid container sx={{justifyContent: {xs: "center", md: "space-between"}}}>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarSemiCircularChart data={20} label={t("reports.cost_of_loan_refer_to_bank")}/>
|
||||
<Filter title={t("reports.loan_details")}
|
||||
filterItem={[
|
||||
{type: "province", type_fa: "استان", multiple: false},
|
||||
]}
|
||||
setFilterOption={setFilterOption}
|
||||
isLoadingReportList={isLoadingReportList}
|
||||
mutate={mutate}
|
||||
/>
|
||||
<Grid container sx={{justifyContent: {xs: "center", position: "relative"}}}>
|
||||
{isLoadingReportList1 ? (
|
||||
<Grid item xs={12} sx={{aspectRatio: "3/0.95"}}>
|
||||
<LoadingHardPage
|
||||
icon={<AnalyticsIcon sx={{width: "inherit", height: "inherit"}}/>}
|
||||
loading={isLoadingReportList1}
|
||||
label={t("reports.loading_fetching_reports")}
|
||||
width={100}
|
||||
height={100}
|
||||
sx={{position: "absolute", bgcolor: "#f7f7f7e6"}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarSemiCircularChart data={60} label={t("reports.number_of_loan_refer_to_bank")}/>
|
||||
) : errorReportList1 ? (
|
||||
<Grid item xs={12}
|
||||
sx={{aspectRatio: "3/0.95", display: "flex", justifyContent: "center", alignItems: "center"}}>
|
||||
<Typography variant="h6" sx={{
|
||||
color: "#858585",
|
||||
letterSpacing: "0.1rem"
|
||||
}}>{t("reports.error_fetching_reports")}</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarSemiCircularChart data={95} label={t("reports.cost_of_payed_loan")}/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={10} md={5} sx={{aspectRatio: "1/0.75"}}>
|
||||
<RadialBarStrokedChart data={15} label={t("reports.number_of_payed_loan")}/>
|
||||
) : (
|
||||
<>
|
||||
{
|
||||
Object.entries(reportList1).map(([entryKey, innerObject]) => {
|
||||
let percentage = innerObject.total !== 0 ? (innerObject.total - innerObject.pending) / innerObject.total * 100 : 0;
|
||||
const ItemName = DataItemsName.find(item => item.id === entryKey).name;
|
||||
return (
|
||||
<Grid key={entryKey} item xs={12} sm={10} md={6} xl={3}
|
||||
sx={{textAlign: "center"}}>
|
||||
<Typography sx={{
|
||||
fontSize: "1.3rem",
|
||||
fontWeight: "600",
|
||||
color: "#8c8c8c"
|
||||
}}>{ItemName}</Typography>
|
||||
<RadialBarSemiCircularChart data={Math.round(percentage)}
|
||||
label={t("reports.dynamic_label", {
|
||||
total: innerObject.total,
|
||||
done: innerObject.total - innerObject.pending
|
||||
})}/>
|
||||
</Grid>
|
||||
)
|
||||
})
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
</Paper>
|
||||
)
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import LoanProgress from "@/components/dashboard/reports/LoanProgress";
|
||||
import LoanDistribution from "@/components/dashboard/reports/LoanDistribution";
|
||||
import ExcelExport from "@/components/dashboard/reports/ExcelExport";
|
||||
import LoanDetails from "@/components/dashboard/reports/LoanDetails";
|
||||
|
||||
const DashboardReportsComponent = (props) => {
|
||||
return (
|
||||
<>
|
||||
<ExcelExport/>
|
||||
<LoanProgress/>
|
||||
<LoanDistribution/>
|
||||
{/*<LoanDetails/>*/}
|
||||
<LoanDetails/>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ const Chart = ({chartId, type, series, specialOption}) => {
|
||||
const options = {
|
||||
chart: {
|
||||
id: chartId,
|
||||
offsetY: -30,
|
||||
locales: [chartLang],
|
||||
defaultLocale: languageApp,
|
||||
fontFamily: languageList[0].fontFamily,
|
||||
@@ -26,7 +27,7 @@ const Chart = ({chartId, type, series, specialOption}) => {
|
||||
},
|
||||
...(specialOption ? specialOption : {})
|
||||
}
|
||||
return <ApexChart type={type} options={options} series={series} width="100%" height="100%"/>
|
||||
return <ApexChart type={type} options={options} series={series}/>
|
||||
};
|
||||
|
||||
export default Chart;
|
||||
|
||||
@@ -225,6 +225,8 @@ export const GET_PERMISSIONS_LIST =
|
||||
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"
|
||||
export const GET_LOAN_EXPERTPROGRESS = BASE_URL + "/dashboard/reports/navgan/expert_progress"
|
||||
export const GET_LOAN_REFERREDLOANS = BASE_URL + "/dashboard/reports/navgan/referred_loans"
|
||||
// reports
|
||||
|
||||
//loan history
|
||||
|
||||
@@ -20,13 +20,14 @@ const useChart = (chart_url) => {
|
||||
data,
|
||||
isValidating,
|
||||
mutate
|
||||
} = useSWR(filterOption.length !== 0 ? `${chart_url}${queryString}` : "", fetcher, {
|
||||
} = useSWR(filterOption.length !== 0 ? `${chart_url}${queryString}` : chart_url, fetcher, {
|
||||
revalidateIfStale: true,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
keepPreviousData: true
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
reportList: data,
|
||||
isLoadingReportList: isValidating,
|
||||
|
||||
20
src/pages/dashboard/old-reports/index.jsx
Normal file
20
src/pages/dashboard/old-reports/index.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import DashboardReportsComponent from "src/components/dashboard/old-reports";
|
||||
import {parse} from "next-useragent";
|
||||
|
||||
export default function Reports() {
|
||||
return (
|
||||
<DashboardReportsComponent/>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps({req, locale}) {
|
||||
const {isBot} = parse(req.headers["user-agent"]);
|
||||
return {
|
||||
props: {
|
||||
messages: (await import(`&/locales/${locale}/app.json`)).default,
|
||||
title: "Dashboard.reports",
|
||||
isBot,
|
||||
layout: {name: 'DashboardLayout', props: {permissions: ["view_reports_navgan"]}}
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import DashboardReportsComponent from "@/components/dashboard/reports";
|
||||
import DashboardReportsComponent from "src/components/dashboard/reports";
|
||||
import {parse} from "next-useragent";
|
||||
|
||||
export default function Reports() {
|
||||
|
||||
Reference in New Issue
Block a user