Feature/report refahi

This commit is contained in:
2025-08-30 10:26:05 +00:00
committed by AmirHossein Mahmoodi
parent 17b9fa3d8c
commit 82925cb082
59 changed files with 1076 additions and 524 deletions

View File

@@ -47,6 +47,7 @@
"refahi": "رفاهی", "refahi": "رفاهی",
"navgan": "ناوگان", "navgan": "ناوگان",
"reports": "گزارشات", "reports": "گزارشات",
"reports-refahi": "گزارشات",
"refahi-province-manager": "بررسی وام های تبصره 18", "refahi-province-manager": "بررسی وام های تبصره 18",
"admin-setting": "تنظیمات ", "admin-setting": "تنظیمات ",
"machinery-expert": "کارتابل کارشناسی", "machinery-expert": "کارتابل کارشناسی",
@@ -82,6 +83,8 @@
"passenger-boss": "کارگروه", "passenger-boss": "کارگروه",
"loan-followup": "ناوگان", "loan-followup": "ناوگان",
"loan-history": "ناوگان", "loan-history": "ناوگان",
"reports": "ناوگان",
"reports-refahi": "رفاهی",
"refahi-loan-management": "رفاهی", "refahi-loan-management": "رفاهی",
"loan-history-refahi": "رفاهی", "loan-history-refahi": "رفاهی",
"refahi-province-manager": "رفاهی", "refahi-province-manager": "رفاهی",
@@ -1048,10 +1051,39 @@
"transportation_assistant": "عملکرد معاون", "transportation_assistant": "عملکرد معاون",
"province_manager": "عملکرد مدیر", "province_manager": "عملکرد مدیر",
"bank": "عملکرد بانک", "bank": "عملکرد بانک",
"bus": "گزارش عملکرد ناوگان اتوبوس",
"report_build": "گزارش عملکرد ساخت مجتمع رفاهی",
"report_repair": "گزارش عملکرد مرمت مجتمع رفاهی",
"rejected_loans": "رد درخواست",
"reserved_loans": "رزرو شده",
"sent_to_planning_council": "ارسال به شورا",
"need_user_modification": "نیاز به اصلاح کاربر",
"minibus": "گزارش عملکرد ناوگان مینی بوس",
"bank_report_bus": "گزارش عملکرد موسسه عامل (اتوبوس)",
"bank_report_minibus": "گزارش عملکرد موسسه عامل (مینی بوس)",
"bank_report_build": "گزارش عملکرد موسسه عامل (ساخت)",
"bank_report_repair": "گزارش عملکرد موسسه عامل (مرمت)",
"pending_bank_contract": "در انتظار انعقاد قرارداد",
"bank_rejected": "رد درخواست",
"bank_paid": "پرداخت شده",
"planing_council_bus": "گزارش عملکرد شورا برنامه ریزی (اتوبوس)",
"planing_council_minibus": "گزارش عملکرد شورا برنامه ریزی (مینی بوس)",
"planing_council_build": "گزارش عملکرد شورا برنامه ریزی (ساخت)",
"planing_council_repair": "گزارش عملکرد شورا برنامه ریزی (مرمت)",
"planning_council_referred": "عودت جهت اصلاح",
"planning_council_rejected": "رد درخواست",
"planning_council_confirmed": "تایید و ارسال به بانک",
"loan_progress": "درصد پیشرفت تسویه وام", "loan_progress": "درصد پیشرفت تسویه وام",
"loan_distribution": "درصد توزیع وام ها در هر مرحله", "loan_distribution": "درصد توزیع وام ها در هر مرحله",
"filter_as": "فیلتر های اعمال شده", "filter_as": "فیلتر های اعمال شده",
"loan_details": "جزئیات وام ها", "loan_details": "جزئیات وام ها",
"performance_report": "گزارش عملکرد",
"edit_required": "نیاز به اصلاح",
"send_to_shora": "ارسال به شورا",
"reserved": "رزرو شده",
"reject": "رد درخواست",
"build": "ساخت مجتمع رفاهی",
"repair": "مرمت مجتمع رفاهی",
"cost_of_loan_refer_to_bank": "مبلغ وام های معرفی به بانک", "cost_of_loan_refer_to_bank": "مبلغ وام های معرفی به بانک",
"number_of_loan_refer_to_bank": "تعداد وام های معرفی به بانک", "number_of_loan_refer_to_bank": "تعداد وام های معرفی به بانک",
"cost_of_payed_loan": "مبلغ وام های پرداخت شده", "cost_of_payed_loan": "مبلغ وام های پرداخت شده",

View File

@@ -47,10 +47,10 @@ function DashboardLoanHistoryComponent() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "name", id: "name",
header: t("LoanHistory.name"), header: t("LoanHistory.name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -2,7 +2,7 @@ import { Button, CircularProgress } from "@mui/material";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import useRequest from "@/lib/app/hooks/useRequest"; import useRequest from "@/lib/app/hooks/useRequest";
import { EXPORT_LOAN_HISTORY } from "@/core/data/apiRoutes"; import { EXPORT_LOAN_HISTORY_REFAHI } from "@/core/data/apiRoutes";
import moment from "jalali-moment"; import moment from "jalali-moment";
import FileSaver from "file-saver"; import FileSaver from "file-saver";
import DescriptionIcon from "@mui/icons-material/Description"; import DescriptionIcon from "@mui/icons-material/Description";
@@ -37,7 +37,7 @@ const PrintExcel = ({ table }) => {
const clickHandler = () => { const clickHandler = () => {
setLoading(true); setLoading(true);
requestServer(`${EXPORT_LOAN_HISTORY}?${filterParams}`, "get", { requestServer(`${EXPORT_LOAN_HISTORY_REFAHI}?${filterParams}`, "get", {
auth: true, auth: true,
requestOptions: { responseType: "blob" }, requestOptions: { responseType: "blob" },
}) })

View File

@@ -11,7 +11,7 @@ import {
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { GET_HISTORY_DETAIL } from "@/core/data/apiRoutes"; import { GET_HISTORY_DETAIL_REFAHI } from "@/core/data/apiRoutes";
import moment from "jalali-moment"; import moment from "jalali-moment";
import PrintHistory from "./PrintHistory"; import PrintHistory from "./PrintHistory";
import useRequest from "@/lib/app/hooks/useRequest"; import useRequest from "@/lib/app/hooks/useRequest";
@@ -32,7 +32,7 @@ const TableContent = ({ rowId }) => {
const [error, setError] = useState(false); const [error, setError] = useState(false);
useEffect(() => { useEffect(() => {
requestServer(`${GET_HISTORY_DETAIL}/${rowId}`, "get") requestServer(`${GET_HISTORY_DETAIL_REFAHI}/${rowId}`, "get")
.then((response) => { .then((response) => {
setIsLoading(false); setIsLoading(false);
setHistoryDetails(response.data.data); setHistoryDetails(response.data.data);

View File

@@ -1,11 +1,9 @@
import { Box, Stack, Typography } from "@mui/material"; import { Box, Typography } from "@mui/material";
import { useMemo } from "react"; import { useMemo } from "react";
import { GET_LOAN_HISTORY } from "@/core/data/apiRoutes"; import { GET_LOAN_HISTORY_REFAHI } from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import TableRowActions from "./TableRowActions"; import TableRowActions from "./TableRowActions";
import moment from "jalali-moment";
import DataTable from "@/core/components/DataTable"; import DataTable from "@/core/components/DataTable";
import MuiDatePicker from "@/core/components/MuiDatePicker";
import TableToolbar from "./TableToolbar"; import TableToolbar from "./TableToolbar";
function DashboardLoanHistoryComponent() { function DashboardLoanHistoryComponent() {
@@ -37,20 +35,11 @@ function DashboardLoanHistoryComponent() {
), ),
}, },
{ {
accessorFn: (row) => row.navgan_id, accessorFn: (row) =>
id: "navgan_id", row.is_legal_person === 1 ? `${row.company_name}` : `${row.first_name} ${row.last_name}`,
header: t("LoanHistory.navgan_id"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.name,
id: "name", id: "name",
header: t("LoanHistory.name"), header: t("LoanHistory.name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],
@@ -132,88 +121,6 @@ function DashboardLoanHistoryComponent() {
columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between"], columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{
accessorFn: (row) => row.statement_count,
id: "statement_count",
header: t("LoanHistory.statement_count"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: ["equals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.navgan_capacity,
id: "navgan_capacity",
header: t("LoanHistory.navgan_capacity"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.manufacture_date,
id: "manufacture_date",
header: t("LoanHistory.manufacture_date"),
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "between"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.vehicle_type,
id: "vehicle_type",
header: t("LoanHistory.vehicle_type"),
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.plate_number,
id: "plate_number",
header: t("LoanHistory.plate_number"),
enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => (
<Stack sx={{ border: 1, borderColor: "divider", borderRadius: 1 }} direction={"row"}>
<Stack sx={{ borderRight: 1, borderColor: "divider", textAlign: "center", width: "40%" }}>
{renderedCellValue.split("-")[3]}
</Stack>
<Stack direction={"row"} sx={{ width: "100%", px: 0.5 }}>
<Stack
sx={{
textAlign: "center",
width: "100%",
}}
>
{renderedCellValue.split("-")[2]}
</Stack>
<Stack
sx={{
textAlign: "center",
width: "100%",
}}
>
{renderedCellValue.split("-")[1]}
</Stack>
<Stack
sx={{
textAlign: "center",
width: "100%",
}}
>
{renderedCellValue.split("-")[0]}
</Stack>
</Stack>
</Stack>
),
},
{ {
accessorFn: (row) => row.approved_amount, accessorFn: (row) => row.approved_amount,
id: "approved_amount", id: "approved_amount",
@@ -330,18 +237,18 @@ function DashboardLoanHistoryComponent() {
return ( return (
<Box sx={{ px: 3 }}> <Box sx={{ px: 3 }}>
<DataTable <DataTable
tableUrl={GET_LOAN_HISTORY} tableUrl={GET_LOAN_HISTORY_REFAHI}
columns={columns} columns={columns}
selectableRow={false} selectableRow={false}
enableCustomToolbar={true} enableCustomToolbar={true}
CustomToolbar={TableToolbar} CustomToolbar={TableToolbar}
enableLastUpdate={true} enableLastUpdate={true}
sorting={[ // sorting={[
{ // {
id: "statement_count", // id: "statement_count",
desc: true, // desc: true,
}, // },
]} // ]}
enablePinning={true} enablePinning={true}
enableDensityToggle={false} enableDensityToggle={false}
initialState={{ density: "compact" }} //compact (small) //comfortable (medium) //spacious (large) initialState={{ density: "compact" }} //compact (small) //comfortable (medium) //spacious (large)

View File

@@ -32,20 +32,10 @@ function DashboardBankManagementComponent() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "first_name", id: "name",
header: t("BankManagement.first_name"), header: t("BankManagement.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("BankManagement.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -49,18 +49,7 @@ const LegalTableContent = ({ rowId, row }) => {
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">{row.original.name || t("UserInfoDialog.no_info")}</Typography>
{row.original.first_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack> </Stack>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -45,20 +45,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -33,20 +33,10 @@ function DashboardLoanFollowUpComponent() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "first_name", id: "name",
header: t("LoanFollowUp.first_name"), header: t("LoanFollowUp.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("LoanFollowUp.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -50,20 +50,10 @@ function DashboardNavganLoanManagementComponent() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "first_name", id: "name",
header: t("NavganLoanManagement.first_name"), header: t("NavganLoanManagement.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("NavganLoanManagement.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -49,18 +49,7 @@ const LegalTableContent = ({ rowId, row }) => {
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">{row.original.name || t("UserInfoDialog.no_info")}</Typography>
{row.original.first_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack> </Stack>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -45,20 +45,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -50,20 +50,10 @@ function DashboardNavganProvinceManagerComponent() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "first_name", id: "name",
header: t("NavganProvinceManager.first_name"), header: t("NavganProvinceManager.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("NavganProvinceManager.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -45,22 +45,11 @@ const LegalTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">{row.original.name || t("UserInfoDialog.no_info")}</Typography>
{row.original.first_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack> </Stack>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -45,20 +45,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -48,20 +48,10 @@ function DashboardPassengerOfficeComponent() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "first_name", id: "name",
header: t("PassengerBoss.first_name"), header: t("PassengerBoss.name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("PassengerBoss.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -47,20 +47,10 @@ function DashboardReserveLoan() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "first_name", id: "name",
header: t("ReserveLoan.first_name"), header: t("ReserveLoan.name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("ReserveLoan.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -49,18 +49,7 @@ const LegalTableContent = ({ rowId, row }) => {
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">{row.original.name || t("UserInfoDialog.no_info")}</Typography>
{row.original.first_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack> </Stack>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -45,20 +45,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -49,20 +49,10 @@ function DashboardTransportationAssistanceComponent() {
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>, Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) => (row.is_legal_person === 1 ? `${row.name}` : `${row.first_name} ${row.last_name}`),
id: "first_name", id: "name",
header: t("TransportationAssistance.first_name"), header: t("TransportationAssistance.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("TransportationAssistance.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -47,21 +47,12 @@ const LegalTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.company_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -48,20 +48,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -47,20 +47,11 @@ function DashboardLoanFollowUpComponent() {
), ),
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) =>
id: "first_name", row.is_legal_person === 1 ? `${row.company_name}` : `${row.first_name} ${row.last_name}`,
id: "name",
header: t("RefahiLoanFollowUp.first_name"), header: t("RefahiLoanFollowUp.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("RefahiLoanFollowUp.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -47,21 +47,12 @@ const LegalTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.company_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -48,20 +48,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -48,20 +48,11 @@ function DashboardPassengerOfficeComponent() {
), ),
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) =>
id: "first_name", row.is_legal_person === 1 ? `${row.company_name}` : `${row.first_name} ${row.last_name}`,
id: "name",
header: t("PassengerBoss.first_name"), header: t("PassengerBoss.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("PassengerBoss.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -47,20 +47,11 @@ function DashboardRefahiLoanManagementComponent() {
), ),
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) =>
id: "first_name", row.is_legal_person === 1 ? `${row.company_name}` : `${row.first_name} ${row.last_name}`,
id: "name",
header: t("RefahiLoanManagement.first_name"), header: t("RefahiLoanManagement.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("RefahiLoanManagement.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -47,21 +47,12 @@ const LegalTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.company_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -48,20 +48,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -47,20 +47,11 @@ function DashboardRefahiProvinceManagerComponent() {
), ),
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) =>
id: "first_name", row.is_legal_person === 1 ? `${row.company_name}` : `${row.first_name} ${row.last_name}`,
id: "name",
header: t("NavganProvinceManager.first_name"), header: t("NavganProvinceManager.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("NavganProvinceManager.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -47,21 +47,12 @@ const LegalTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.company_name || t("UserInfoDialog.no_info")}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>
</Grid> </Grid>

View File

@@ -48,20 +48,12 @@ const RealTableContent = ({ rowId, row }) => {
</Grid> </Grid>
</Grid> </Grid>
<Grid container spacing={2} sx={{ my: 2 }}> <Grid container spacing={2} sx={{ my: 2 }}>
<Grid item xs={12} sm={6}> <Grid item xs={12} sm={12}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}> <Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} /> <Chip label={t("UserInfoDialog.first_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} /> <Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1"> <Typography variant="body1">
{row.original.first_name || t("UserInfoDialog.no_info")} {row.original.first_name || t("UserInfoDialog.no_info")}{" "}
</Typography>
</Stack>
</Grid>
<Grid item xs={12} sm={6}>
<Stack direction="row" alignItems="center" sx={{ width: "100%" }}>
<Chip label={t("UserInfoDialog.last_name")} sx={{ mr: 1 }} />
<Divider sx={{ flexGrow: 1, mx: 1 }} />
<Typography variant="body1">
{row.original.last_name || t("UserInfoDialog.no_info")} {row.original.last_name || t("UserInfoDialog.no_info")}
</Typography> </Typography>
</Stack> </Stack>

View File

@@ -47,20 +47,11 @@ function DashboardTransportationAssistanceComponent() {
), ),
}, },
{ {
accessorFn: (row) => row.first_name, accessorFn: (row) =>
id: "first_name", row.is_legal_person === 1 ? `${row.company_name}` : `${row.first_name} ${row.last_name}`,
id: "name",
header: t("TransportationAssistance.first_name"), header: t("TransportationAssistance.first_name"),
enableColumnFilter: true, enableColumnFilter: false,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"],
Cell: ({ renderedCellValue }) => <Typography variant="body2">{renderedCellValue}</Typography>,
},
{
accessorFn: (row) => row.last_name,
id: "last_name",
header: t("TransportationAssistance.last_name"),
enableColumnFilter: true,
datatype: "text", datatype: "text",
filterFn: "contains", filterFn: "contains",
columnFilterModeOptions: ["contains", "equals", "notEquals"], columnFilterModeOptions: ["contains", "equals", "notEquals"],

View File

@@ -1,47 +0,0 @@
import { Grid, Typography } from "@mui/material";
import { useTranslations } from "next-intl";
import RadialBarSemiCircularChart from "@/components/dashboard/reports/LoanDetails/RadialBarSemiCircularChart";
const FunctionalityCharts = ({ reportList }) => {
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={{ width: "100%" }}>
{Object.entries(reportList).map(([entryKey, innerObject]) => {
let percentage =
innerObject.total !== 0 && innerObject.pending !== 64
? ((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: "primary.main",
}}
>
{ItemName}
</Typography>
<RadialBarSemiCircularChart
data={Math.round(percentage)}
formatter={(val) => `${val}%`}
label={t("reports.dynamic_label_functionality", {
total: innerObject.total.toLocaleString("en"),
done: (innerObject.total - innerObject.pending).toLocaleString("en"),
})}
/>
</Grid>
);
})}
</Grid>
);
};
export default FunctionalityCharts;

View File

@@ -11,8 +11,8 @@ import {
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import { useFormik } from "formik"; import { useFormik } from "formik";
import ProvinceFilter from "../Filter/ProvinceFilter"; import ProvinceFilter from "./ProvinceFilter";
import YearFilter from "../Filter/YearFilter"; import YearFilter from "./YearFilter";
import FilterAltIcon from "@mui/icons-material/FilterAlt"; import FilterAltIcon from "@mui/icons-material/FilterAlt";
import EqualizerIcon from "@mui/icons-material/Equalizer"; import EqualizerIcon from "@mui/icons-material/Equalizer";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";

View File

@@ -0,0 +1,36 @@
import { Grid, Typography } from "@mui/material";
import { useTranslations } from "next-intl";
import UniqDetailComponent from "./UniqDetailComponent";
const FunctionalityCharts = ({ reportList, name }) => {
const t = useTranslations();
return (
<>
<Typography sx={{ pt: 2 }} variant={"h5"} color={"primary"} textAlign={"center"}>
{name}
</Typography>
<Grid container sx={{ width: "100%", mt: 3 }} spacing={1}>
{Object.entries(reportList).map(([entryKey, innerObject]) => {
let percentage =
innerObject.total_budget !== 0
? (innerObject.total_amount / innerObject.total_budget) * 100
: 0;
const numColumns = Object.entries(reportList).length;
let columnSize = 12 / numColumns;
return (
<Grid key={entryKey} item xs={12} sm={6} md={columnSize}>
<UniqDetailComponent
title={t(`reports.${entryKey}`)}
percentage={percentage}
item={innerObject}
/>
</Grid>
);
})}
</Grid>
</>
);
};
export default FunctionalityCharts;

View File

@@ -1,5 +1,5 @@
import { Box, Grid, Typography } from "@mui/material"; import { Box, Grid, Typography } from "@mui/material";
import RadialBarSemiCircularChart from "@/components/dashboard/reports/LoanDetails/RadialBarSemiCircularChart"; import RadialBarSemiCircularChart from "./RadialBarSemiCircularChart";
import { useTranslations } from "next-intl"; // Make sure to import React if it's not already imported import { useTranslations } from "next-intl"; // Make sure to import React if it's not already imported
const ReferredCharts = ({ reportList }) => { const ReferredCharts = ({ reportList }) => {

View File

@@ -0,0 +1,51 @@
import { Box, Grid, Stack, Typography } from "@mui/material";
import RadialBarSemiCircularChart from "./RadialBarSemiCircularChart";
import { useTranslations } from "next-intl";
const UniqDetailComponent = ({ item, percentage, title }) => {
const t = useTranslations();
return (
<Stack spacing={1}>
<Box sx={{ textAlign: "center" }}>
<Typography
sx={{
fontSize: {
xs: "0.5rem", // برای اندازه صفحه موبایل
sm: "0.8rem", // برای صفحه‌های متوسط
md: "1rem", // برای صفحات بزرگتر
lg: "1.1rem", // برای صفحات بسیار بزرگ
},
fontWeight: "600",
color: "#8c8c8c",
pb: 1,
}}
>
{title}
</Typography>
<Typography
variant="caption"
sx={{
fontSize: {
xs: "0.8rem", // برای اندازه صفحه موبایل
sm: "0.9rem", // برای صفحه‌های متوسط
md: "1rem", // برای صفحات بزرگتر
},
}}
>
{t("reports.dynamic_total_budget", {
total_budget: (item.total_budget / 1000000).toLocaleString("en"),
})}
</Typography>
</Box>
<RadialBarSemiCircularChart
data={Math.round(percentage > 100 ? 100 : percentage)}
formatter={(val) => `${Math.round(percentage)}%`}
label={t("reports.dynamic_label_referred", {
count: item.count.toLocaleString("en"),
total_amount: (item.total_amount / 1000000).toLocaleString("en"),
})}
/>
</Stack>
);
};
export default UniqDetailComponent;

View File

@@ -1,12 +1,15 @@
import { Divider, Grid, Paper, Stack, Typography } from "@mui/material"; import { Divider, Grid, Paper, Stack, Typography } from "@mui/material";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import Filter from "@/components/dashboard/reports/Filter"; import Filter from "../Filter";
import useChart from "@/lib/app/hooks/useChart"; import useChart from "@/lib/app/hooks/useChart";
import { GET_LOAN_EXPERTPROGRESS, GET_LOAN_REFERREDLOANS } from "@/core/data/apiRoutes"; import {
GET_LOAN_EXPERTPROGRESS_BANK_REPORT,
GET_LOAN_EXPERTPROGRESS_CARTABLE,
GET_LOAN_EXPERTPROGRESS_PLANING_COUNCIL,
} from "@/core/data/apiRoutes";
import LoadingHardPage from "@/core/components/LoadingHardPage"; import LoadingHardPage from "@/core/components/LoadingHardPage";
import AnalyticsIcon from "@mui/icons-material/Analytics"; import AnalyticsIcon from "@mui/icons-material/Analytics";
import FunctionalityCharts from "@/components/dashboard/reports/LoanDetails/FunctionalityCharts"; import FunctionalityCharts from "./FunctionalityCharts";
import ReferredCharts from "@/components/dashboard/reports/LoanDetails/ReferredCharts";
const LoanDetails = () => { const LoanDetails = () => {
const t = useTranslations(); const t = useTranslations();
@@ -17,16 +20,21 @@ const LoanDetails = () => {
reportList: reportList1, reportList: reportList1,
setFilterOption: setFilterOption1, setFilterOption: setFilterOption1,
mutate: mutate1, mutate: mutate1,
} = useChart(GET_LOAN_EXPERTPROGRESS); } = useChart(GET_LOAN_EXPERTPROGRESS_CARTABLE);
// Use the useChart hook for the second API route
const { const {
errorReportList: errorReportList2, errorReportList: errorReportList2,
isLoadingReportList: isLoadingReportList2, isLoadingReportList: isLoadingReportList2,
reportList: reportList2, reportList: reportList2,
setFilterOption: setFilterOption2, setFilterOption: setFilterOption2,
mutate: mutate2, mutate: mutate2,
} = useChart(GET_LOAN_REFERREDLOANS); } = useChart(GET_LOAN_EXPERTPROGRESS_PLANING_COUNCIL);
const {
errorReportList: errorReportList3,
isLoadingReportList: isLoadingReportList3,
reportList: reportList3,
setFilterOption: setFilterOption3,
mutate: mutate3,
} = useChart(GET_LOAN_EXPERTPROGRESS_BANK_REPORT);
return ( return (
<Paper elevation={2} sx={{ width: "95%", mx: "auto", my: 2, pb: 2, background: "#f7f7f7e6" }}> <Paper elevation={2} sx={{ width: "95%", mx: "auto", my: 2, pb: 2, background: "#f7f7f7e6" }}>
@@ -36,26 +44,28 @@ const LoanDetails = () => {
setFilterOption={(fields) => { setFilterOption={(fields) => {
setFilterOption1(fields); setFilterOption1(fields);
setFilterOption2(fields); setFilterOption2(fields);
setFilterOption3(fields);
}} }}
isLoadingReportList={isLoadingReportList1 || isLoadingReportList2} isLoadingReportList={isLoadingReportList1 || isLoadingReportList3 || isLoadingReportList2}
mutate={() => { mutate={() => {
mutate1(); mutate1();
mutate2(); mutate2();
mutate3();
}} }}
/> />
<Grid container sx={{ justifyContent: { xs: "center", position: "relative" } }}> <Grid container sx={{ justifyContent: { xs: "center", position: "relative" } }}>
{isLoadingReportList1 || isLoadingReportList2 ? ( {isLoadingReportList1 || isLoadingReportList2 || isLoadingReportList3 ? (
<Grid item xs={12} sx={{ aspectRatio: "3/0.95" }}> <Grid item xs={12} sx={{ aspectRatio: "3/0.95" }}>
<LoadingHardPage <LoadingHardPage
icon={<AnalyticsIcon sx={{ width: "inherit", height: "inherit" }} />} icon={<AnalyticsIcon sx={{ width: "inherit", height: "inherit" }} />}
loading={isLoadingReportList1 || isLoadingReportList2} loading={isLoadingReportList1 || isLoadingReportList2 || isLoadingReportList3}
label={t("reports.loading_fetching_reports")} label={t("reports.loading_fetching_reports")}
width={100} width={100}
height={100} height={100}
sx={{ position: "absolute", bgcolor: "#f7f7f7e6" }} sx={{ position: "absolute", bgcolor: "#f7f7f7e6" }}
/> />
</Grid> </Grid>
) : errorReportList1 || errorReportList2 ? ( ) : errorReportList1 || errorReportList2 || errorReportList3 ? (
<Grid <Grid
item item
xs={12} xs={12}
@@ -73,9 +83,20 @@ const LoanDetails = () => {
</Grid> </Grid>
) : ( ) : (
<Stack sx={{ width: "100%" }}> <Stack sx={{ width: "100%" }}>
<FunctionalityCharts reportList={reportList1} /> <FunctionalityCharts reportList={reportList1.bus} name={t("reports.bus")} />
<Divider /> <Divider />
<ReferredCharts reportList={reportList2} /> <FunctionalityCharts reportList={reportList1.minibus} name={t("reports.minibus")} />
<Divider />
<FunctionalityCharts reportList={reportList2.bus} name={t("reports.planing_council_bus")} />
<Divider />
<FunctionalityCharts
reportList={reportList2.minibus}
name={t("reports.planing_council_minibus")}
/>
<Divider />
<FunctionalityCharts reportList={reportList3.bus} name={t("reports.bank_report_bus")} />
<Divider />
<FunctionalityCharts reportList={reportList3.minibus} name={t("reports.bank_report_minibus")} />
</Stack> </Stack>
)} )}
</Grid> </Grid>

View File

@@ -1,5 +1,5 @@
import LoanDetails from "@/components/dashboard/reports/LoanDetails"; import LoanDetails from "./LoanDetails";
import ExcelExport from "@/components/dashboard/reports/ExcelExport"; import ExcelExport from "./ExcelExport";
import { GET_EXCEL_EXPORT, GET_EXCEL_EXPORT_status } from "@/core/data/apiRoutes"; import { GET_EXCEL_EXPORT, GET_EXCEL_EXPORT_status } from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";

View File

@@ -0,0 +1,66 @@
import { Accordion, AccordionSummary, Box, Button, CircularProgress, Stack, Typography } from "@mui/material";
import AssignmentIcon from "@mui/icons-material/Assignment";
import BackupIcon from "@mui/icons-material/Backup";
import { useTranslations } from "next-intl";
import moment from "jalali-moment";
import FileSaver from "file-saver";
import useRequest from "@/lib/app/hooks/useRequest";
import { useState } from "react";
const ExcelExport = ({ filenameStr, url, title }) => {
const t = useTranslations();
const requestServer = useRequest();
const [loading, setLoading] = useState(false);
const clickHandler = () => {
setLoading(true);
requestServer(url, "get", {
auth: true,
requestOptions: { responseType: "blob" },
})
.then((response) => {
const filename = `${filenameStr}_${moment().format("jDD_jMM_jYYYY")}.xlsx`;
FileSaver.saveAs(response.data, filename);
})
.catch(() => {})
.finally(() => {
setLoading(false);
});
};
return (
<Stack sx={{ width: "95%", mx: "auto" }}>
<Accordion
elevation={0}
sx={{
width: "100%",
mb: 2,
borderBottom: 2,
borderBottomColor: "divider",
backgroundColor: "#f1f1f1",
}}
expanded={false}
>
<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 }}>
<AssignmentIcon sx={{ color: "primary.main" }} />
<Typography sx={{ fontWeight: 500, color: "primary.main" }}>{title}</Typography>
</Box>
<Button
onClick={clickHandler}
variant="contained"
sx={{ backgroundColor: "success.main" }}
disabled={loading}
startIcon={loading ? <CircularProgress size={20} color="inherit" /> : <BackupIcon />}
>
{t("filters.export")}
</Button>
</Box>
</AccordionSummary>
</Accordion>
</Stack>
);
};
export default ExcelExport;

View File

@@ -0,0 +1,122 @@
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.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", "");
setFilteredText((prevState) => ({
...prevState,
province: t("filters.all_provinces"),
}));
}}
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;

View File

@@ -0,0 +1,72 @@
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;

View File

@@ -0,0 +1,167 @@
import {
Accordion,
AccordionDetails,
AccordionSummary,
Box,
Button,
CircularProgress,
IconButton,
Stack,
Tooltip,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import ProvinceFilter from "./ProvinceFilter";
import YearFilter from "./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";
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(true);
const [filteredText, setFilteredText] = useState({
...(filter_by_province ? { province: t("filters.all_provinces") } : {}),
...(filter_by_year ? { year: "" } : {}),
});
const [textFilter, setTextFilter] = useState();
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: "",
to_date: "",
});
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);
textMaker();
},
});
const textMaker = () => {
setTextFilter(`| ${Object.values(filteredText).join(", ")}`);
};
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);
setTextFilter(`| ${Object.values(filteredText).join(", ")}`);
}, []);
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">
{textFilter}
</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;

View File

@@ -0,0 +1,36 @@
import { Grid, Typography } from "@mui/material";
import { useTranslations } from "next-intl";
import UniqDetailComponent from "./UniqDetailComponent";
const FunctionalityCharts = ({ reportList, name }) => {
const t = useTranslations();
return (
<>
<Typography sx={{ pt: 2 }} variant={"h5"} color={"primary"} textAlign={"center"}>
{name}
</Typography>
<Grid container sx={{ width: "100%", mt: 3 }} spacing={1}>
{Object.entries(reportList).map(([entryKey, innerObject]) => {
let percentage =
innerObject.total_budget !== 0
? (innerObject.total_amount / innerObject.total_budget) * 100
: 0;
const numColumns = Object.entries(reportList).length;
let columnSize = 12 / numColumns;
return (
<Grid key={entryKey} item xs={12} sm={6} md={columnSize}>
<UniqDetailComponent
title={t(`reports.${entryKey}`)}
percentage={percentage}
item={innerObject}
/>
</Grid>
);
})}
</Grid>
</>
);
};
export default FunctionalityCharts;

View File

@@ -0,0 +1,42 @@
import Chart from "@/core/components/Chart";
import { calculateGradientColor } from "@/core/utils/gradientColorHandler";
const RadialBarSemiCircularChart = ({ data, label, formatter }) => {
const specialOption = {
title: {
text: undefined,
},
plotOptions: {
radialBar: {
startAngle: -90,
endAngle: 90,
track: {
background: "#e7e7e7",
strokeWidth: "80%",
},
dataLabels: {
name: {
fontSize: "1rem",
offsetY: 30,
color: "#8c8c8c",
},
value: {
offsetY: -30,
fontSize: "30px",
fontWeight: 500,
color: "#8c8c8c",
formatter: formatter,
},
},
},
},
fill: {
colors: calculateGradientColor(data),
},
labels: [label],
};
const series = [data];
return <Chart chartId="LoanProgressBar" type="radialBar" specialOption={specialOption} series={series} />;
};
export default RadialBarSemiCircularChart;

View File

@@ -0,0 +1,76 @@
import { Box, Grid, Typography } from "@mui/material";
import RadialBarSemiCircularChart from "./RadialBarSemiCircularChart";
import { useTranslations } from "next-intl"; // Make sure to import React if it's not already imported
const ReferredCharts = ({ reportList }) => {
const t = useTranslations();
const DataItemsName = [
{ id: "referred_loans_to_bank", name: t("reports.referred_loans_to_bank") },
{ id: "paid_loans", name: t("reports.paid_loans") },
];
return (
<Grid container sx={{ width: "100%", mt: 3 }}>
{Object.entries(reportList).map(([entryKey, innerObject]) => {
const ItemName = DataItemsName.find((item) => item.id === entryKey).name;
return (
<Grid
key={entryKey}
item
xs={12}
xl={6}
sx={{ display: "flex", flexDirection: "column", alignItems: "center" }}
>
<Box sx={{ mb: 2 }}>
<Typography
sx={{
fontSize: "1.3rem",
fontWeight: "600",
color: "primary.main",
}}
>
{ItemName}
</Typography>
</Box>
<Grid container sx={{ width: "100%" }}>
{innerObject.map((item, index) => {
let percentage =
item.total_budget !== 0 ? (item.total_amount / item.total_budget) * 100 : 0;
return (
<Grid key={index} item xs={12} md={6} sx={{ textAlign: "center" }}>
<Box>
<Typography
sx={{
fontSize: "1.1rem",
fontWeight: "600",
color: "#8c8c8c",
}}
>
{item.vehicle_type}
</Typography>
<Typography variant="caption">
{t("reports.dynamic_total_budget", {
total_budget: (item.total_budget / 1000000).toLocaleString("en"),
})}
</Typography>
</Box>
<RadialBarSemiCircularChart
data={Math.round(percentage > 100 ? 100 : percentage)}
formatter={(val) => `${Math.round(percentage)}%`}
label={t("reports.dynamic_label_referred", {
count: item.count.toLocaleString("en"),
total_amount: (item.total_amount / 1000000).toLocaleString("en"),
})}
/>
</Grid>
);
})}
</Grid>
</Grid>
);
})}
</Grid>
);
};
export default ReferredCharts;

View File

@@ -0,0 +1,51 @@
import { Box, Grid, Stack, Typography } from "@mui/material";
import RadialBarSemiCircularChart from "./RadialBarSemiCircularChart";
import { useTranslations } from "next-intl";
const UniqDetailComponent = ({ item, percentage, title }) => {
const t = useTranslations();
return (
<Stack spacing={1}>
<Box sx={{ textAlign: "center" }}>
<Typography
sx={{
fontSize: {
xs: "0.5rem", // برای اندازه صفحه موبایل
sm: "0.8rem", // برای صفحه‌های متوسط
md: "1rem", // برای صفحات بزرگتر
lg: "1.1rem", // برای صفحات بسیار بزرگ
},
fontWeight: "600",
color: "#8c8c8c",
pb: 1,
}}
>
{title}
</Typography>
<Typography
variant="caption"
sx={{
fontSize: {
xs: "0.8rem", // برای اندازه صفحه موبایل
sm: "0.9rem", // برای صفحه‌های متوسط
md: "1rem", // برای صفحات بزرگتر
},
}}
>
{t("reports.dynamic_total_budget", {
total_budget: (item.total_budget / 1000000).toLocaleString("en"),
})}
</Typography>
</Box>
<RadialBarSemiCircularChart
data={Math.round(percentage > 100 ? 100 : percentage)}
formatter={(val) => `${Math.round(percentage)}%`}
label={t("reports.dynamic_label_referred", {
count: item.count.toLocaleString("en"),
total_amount: (item.total_amount / 1000000).toLocaleString("en"),
})}
/>
</Stack>
);
};
export default UniqDetailComponent;

View File

@@ -0,0 +1,108 @@
import { Divider, Grid, Paper, Stack, Typography } from "@mui/material";
import { useTranslations } from "next-intl";
import Filter from "../Filter";
import useChart from "@/lib/app/hooks/useChart";
import {
//GET_LOAN_EXPERTPROGRESS_CARTABLE_REFAHI , GET_LOAN_EXPERTPROGRESS_PLANING_COUNCIL_REFAHI, GET_LOAN_EXPERTPROGRESS_BANK_REPORT_REFAHI
GET_LOAN_EXPERTPROGRESS_BANK_REPORT,
GET_LOAN_EXPERTPROGRESS_CARTABLE,
GET_LOAN_EXPERTPROGRESS_PLANING_COUNCIL,
} from "@/core/data/apiRoutes";
import LoadingHardPage from "@/core/components/LoadingHardPage";
import AnalyticsIcon from "@mui/icons-material/Analytics";
import FunctionalityCharts from "./FunctionalityCharts";
const LoanDetails = () => {
const t = useTranslations();
const {
errorReportList: errorReportList1,
isLoadingReportList: isLoadingReportList1,
reportList: reportList1,
setFilterOption: setFilterOption1,
mutate: mutate1,
} = useChart(GET_LOAN_EXPERTPROGRESS_CARTABLE);
const {
errorReportList: errorReportList2,
isLoadingReportList: isLoadingReportList2,
reportList: reportList2,
setFilterOption: setFilterOption2,
mutate: mutate2,
} = useChart(GET_LOAN_EXPERTPROGRESS_PLANING_COUNCIL);
const {
errorReportList: errorReportList3,
isLoadingReportList: isLoadingReportList3,
reportList: reportList3,
setFilterOption: setFilterOption3,
mutate: mutate3,
} = useChart(GET_LOAN_EXPERTPROGRESS_BANK_REPORT);
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 }]}
setFilterOption={(fields) => {
setFilterOption1(fields);
setFilterOption2(fields);
setFilterOption3(fields);
}}
isLoadingReportList={isLoadingReportList1 || isLoadingReportList3 || isLoadingReportList2}
mutate={() => {
mutate1();
mutate2();
mutate3();
}}
/>
<Grid container sx={{ justifyContent: { xs: "center", position: "relative" } }}>
{isLoadingReportList1 || isLoadingReportList2 || isLoadingReportList3 ? (
<Grid item xs={12} sx={{ aspectRatio: "3/0.95" }}>
<LoadingHardPage
icon={<AnalyticsIcon sx={{ width: "inherit", height: "inherit" }} />}
loading={isLoadingReportList1 || isLoadingReportList2 || isLoadingReportList3}
label={t("reports.loading_fetching_reports")}
width={100}
height={100}
sx={{ position: "absolute", bgcolor: "#f7f7f7e6" }}
/>
</Grid>
) : errorReportList1 || errorReportList2 || errorReportList3 ? (
<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>
) : (
<Stack sx={{ width: "100%" }}>
<FunctionalityCharts reportList={reportList1.bus} name={t("reports.report_build")} />
<Divider />
<FunctionalityCharts reportList={reportList1.minibus} name={t("reports.report_repair")} />
<Divider />
<FunctionalityCharts reportList={reportList2.bus} name={t("reports.planing_council_build")} />
<Divider />
<FunctionalityCharts
reportList={reportList2.minibus}
name={t("reports.planing_council_repair")}
/>
<Divider />
<FunctionalityCharts reportList={reportList3.bus} name={t("reports.bank_report_build")} />
<Divider />
<FunctionalityCharts reportList={reportList3.minibus} name={t("reports.bank_report_repair")} />
</Stack>
)}
</Grid>
</Paper>
);
};
export default LoanDetails;

View File

@@ -0,0 +1,26 @@
import LoanDetails from "./LoanDetails";
import ExcelExport from "./ExcelExport";
import { GET_EXCEL_EXPORT, GET_EXCEL_EXPORT_status } from "@/core/data/apiRoutes";
import { useTranslations } from "next-intl";
const DashboardReportsComponent = (props) => {
const t = useTranslations();
return (
<>
<ExcelExport
url={GET_EXCEL_EXPORT}
title={t("filters.excel_export_reports")}
filenameStr={"گزارش عملکرد"}
/>
<ExcelExport
url={GET_EXCEL_EXPORT_status}
title={t("filters.excel_export_reports_status")}
filenameStr={"گزارش وضعیت وام های کشور"}
/>
<LoanDetails />
</>
);
};
export default DashboardReportsComponent;

View File

@@ -161,15 +161,26 @@ export const GET_EXCEL_EXPORT = BASE_URL + "/dashboard/exports/navgan/expert_pro
export const GET_EXCEL_EXPORT_status = BASE_URL + "/dashboard/exports/navgan/loans_state"; export const GET_EXCEL_EXPORT_status = BASE_URL + "/dashboard/exports/navgan/loans_state";
export const GET_PROVINCE_PROGRESS = BASE_URL + "/dashboard/reports/navgan/province_progress"; 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_DISTRIBUTION = BASE_URL + "/dashboard/reports/navgan/loan_distribution";
export const GET_LOAN_EXPERTPROGRESS = BASE_URL + "/dashboard/reports/navgan/expert_progress"; export const GET_LOAN_EXPERTPROGRESS_CARTABLE = BASE_URL + "/dashboard/reports/navgan/cartable_reports";
export const GET_LOAN_EXPERTPROGRESS_PLANING_COUNCIL = BASE_URL + "/dashboard/reports/navgan/planning_council_reports";
export const GET_LOAN_EXPERTPROGRESS_BANK_REPORT = BASE_URL + "/dashboard/reports/navgan/bank_reports";
export const GET_LOAN_EXPERTPROGRESS_CARTABLE_REFAHI = BASE_URL + "/dashboard/reports/refahi/cartable_reports";
export const GET_LOAN_EXPERTPROGRESS_PLANING_COUNCIL_REFAHI =
BASE_URL + "/dashboard/reports/refahi/planning_council_reports";
export const GET_LOAN_EXPERTPROGRESS_BANK_REPORT_REFAHI = BASE_URL + "/dashboard/reports/refahi/bank_reports";
export const GET_LOAN_REFERREDLOANS = BASE_URL + "/dashboard/reports/navgan/referred_loans"; export const GET_LOAN_REFERREDLOANS = BASE_URL + "/dashboard/reports/navgan/referred_loans";
// reports // reports
//loan history //loan history navgan
export const GET_LOAN_HISTORY = BASE_URL + "/dashboard/navgan_loans"; export const GET_LOAN_HISTORY = BASE_URL + "/dashboard/navgan_loans";
export const GET_HISTORY_DETAIL = BASE_URL + "/dashboard/navgan_loans"; export const GET_HISTORY_DETAIL = BASE_URL + "/dashboard/navgan_loans";
export const EXPORT_LOAN_HISTORY = BASE_URL + "/dashboard/navgan_loans/export"; export const EXPORT_LOAN_HISTORY = BASE_URL + "/dashboard/navgan_loans/export";
//loan history refahi
export const GET_LOAN_HISTORY_REFAHI = BASE_URL + "/dashboard/refahi_loans";
export const GET_HISTORY_DETAIL_REFAHI = BASE_URL + "/dashboard/refahi_loans";
export const EXPORT_LOAN_HISTORY_REFAHI = BASE_URL + "/dashboard/refahi_loans/export";
//admin setting //admin setting
export const GET_ADMIN_SETTING = BASE_URL + "/dashboard/settings/show"; export const GET_ADMIN_SETTING = BASE_URL + "/dashboard/settings/show";
export const ADD_ADMIN_SETTING = BASE_URL + "/dashboard/settings/store"; export const ADD_ADMIN_SETTING = BASE_URL + "/dashboard/settings/store";
@@ -225,3 +236,6 @@ export const GET_REFAHI_COMEBACK_LOAN_DETAIL = BASE_URL + "/dashboard/vezarat_re
export const GET_REFAHI_BANK_PAYMENT = BASE_URL + "/dashboard/vezarat_refahi_loan_tracking/bank_payment_loan"; export const GET_REFAHI_BANK_PAYMENT = BASE_URL + "/dashboard/vezarat_refahi_loan_tracking/bank_payment_loan";
export const GET_REFAHI_APPROVED_BANK_LOAN = BASE_URL + "/dashboard/vezarat_refahi_loan_tracking/approved_loan"; export const GET_REFAHI_APPROVED_BANK_LOAN = BASE_URL + "/dashboard/vezarat_refahi_loan_tracking/approved_loan";
//refahi loan follow up //refahi loan follow up
//refahi report
export const GET_LOAN_REFAHI_EXPERTPROGRESS = BASE_URL + "/dashboard/reports/navgan/expert_progress";

View File

@@ -4,7 +4,7 @@ import AssignmentIndIcon from "@mui/icons-material/AssignmentInd";
import DesktopWindowsIcon from "@mui/icons-material/DesktopWindows"; import DesktopWindowsIcon from "@mui/icons-material/DesktopWindows";
import GavelIcon from "@mui/icons-material/Gavel"; import GavelIcon from "@mui/icons-material/Gavel";
import BusinessCenterIcon from "@mui/icons-material/BusinessCenter"; import BusinessCenterIcon from "@mui/icons-material/BusinessCenter";
import PollIcon from '@mui/icons-material/Poll'; import PollIcon from "@mui/icons-material/Poll";
import PaidIcon from "@mui/icons-material/Paid"; import PaidIcon from "@mui/icons-material/Paid";
import ManageAccountsIcon from "@mui/icons-material/ManageAccounts"; import ManageAccountsIcon from "@mui/icons-material/ManageAccounts";
import PersonIcon from "@mui/icons-material/Person"; import PersonIcon from "@mui/icons-material/Person";
@@ -15,7 +15,8 @@ import SettingsIcon from "@mui/icons-material/Settings";
import RepartitionIcon from "@mui/icons-material/Repartition"; import RepartitionIcon from "@mui/icons-material/Repartition";
import AccountBalanceIcon from "@mui/icons-material/AccountBalance"; import AccountBalanceIcon from "@mui/icons-material/AccountBalance";
import RepeatIcon from "@mui/icons-material/Repeat"; import RepeatIcon from "@mui/icons-material/Repeat";
import DisplaySettingsIcon from '@mui/icons-material/DisplaySettings'; import DisplaySettingsIcon from "@mui/icons-material/DisplaySettings";
import BarChartIcon from "@mui/icons-material/BarChart";
const sidebarMenu = [ const sidebarMenu = [
[ [
@@ -27,15 +28,27 @@ const sidebarMenu = [
selected: false, selected: false,
permissions: ["all"], permissions: ["all"],
}, },
{ // TODO : should un-command this part after reports get fixed
key: "sidebar.reports", // {
name: "reports", // key: "sidebar.reports",
type: "page", // name: "reports",
route: "/dashboard/reports", // secondary: "secondary.reports",
icon: <AssessmentIcon sx={{ width: "inherit", height: "inherit" }} />, // type: "page",
selected: false, // route: "/dashboard/reports/navgan",
permissions: ["view_reports_navgan"], // icon: <AssessmentIcon sx={{ width: "inherit", height: "inherit" }} />,
}, // selected: false,
// permissions: ["view_reports_navgan"],
// },
// {
// key: "sidebar.reports-refahi",
// name: "reports-refahi",
// secondary: "secondary.reports-refahi",
// type: "page",
// route: "/dashboard/reports/refahi",
// icon: <BarChartIcon sx={{ width: "inherit", height: "inherit" }} />,
// selected: false,
// permissions: ["view_reports_refahi"],
// },
{ {
key: "sidebar.loan-history", key: "sidebar.loan-history",
secondary: "secondary.loan-history", secondary: "secondary.loan-history",
@@ -198,7 +211,7 @@ const sidebarMenu = [
route: "/dashboard/refahi/loan-followup", route: "/dashboard/refahi/loan-followup",
icon: <GavelIcon sx={{ width: "inherit", height: "inherit" }} />, icon: <GavelIcon sx={{ width: "inherit", height: "inherit" }} />,
selected: false, selected: false,
permissions: ["manage_vezarat_eghtesad_loans_refahi"], permissions: ["manage_vezarat_eghtesad_loans"],
}, },
{ {
key: "sidebar.refahi-loan-management", key: "sidebar.refahi-loan-management",

View File

@@ -13,7 +13,7 @@ export async function getServerSideProps({ req, locale }) {
title: "Dashboard.loan_followup", title: "Dashboard.loan_followup",
isBot, isBot,
locale, locale,
layout: { name: "DashboardLayout", props: { permissions: ["manage_vezarat_eghtesad_loans_refahi"] } }, layout: { name: "DashboardLayout", props: { permissions: ["manage_vezarat_eghtesad_loans"] } },
}, },
}; };
} }

View File

@@ -1,4 +1,4 @@
import DashboardReportsComponent from "@/components/dashboard/reports"; import DashboardReportsComponent from "@/components/dashboard/reports/navgan";
import { parse } from "next-useragent"; import { parse } from "next-useragent";
export default function Reports() { export default function Reports() {

View File

@@ -0,0 +1,18 @@
import DashboardReportsComponent from "@/components/dashboard/reports/refahi";
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_refahi"] } },
},
};
}