From 3658345821a62b4fbb4a63905d296685235dae3a Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 11 Feb 2025 07:52:38 +0000 Subject: [PATCH 01/32] Feature/excel print --- .../roadItems/operator/ExcelPrint/index.jsx | 15 ++-- .../dashboard/roadItems/operator/Toolbar.jsx | 2 +- .../{ => ItemsReports}/ExcelPrint/index.jsx | 18 +++-- .../reports/ItemsReports/ReportLists.jsx | 5 +- .../reports/ItemsReports/TableToolbar.jsx | 10 +++ .../roadItems/reports/ItemsReports/index.jsx | 14 +++- .../SubItemsReports/ExcelPrint/index.jsx | 77 +++++++++++++++++++ .../reports/SubItemsReports/SubItemsList.jsx | 5 +- .../reports/SubItemsReports/TableToolbar.jsx | 10 +++ .../reports/SubItemsReports/index.jsx | 15 +++- .../roadItems/supervisor/ExcelPrint/index.jsx | 16 ++-- .../roadItems/supervisor/Toolbar.jsx | 6 +- .../operator/Actions/Create/index.jsx | 2 +- .../roadPatrols/operator/ExcelPrint/index.jsx | 49 ++++++++---- .../Forms/CreatePatrol/CompeleteRequest.jsx | 6 +- .../Forms/CreatePatrol/PatrolDetailInfo.jsx | 13 +--- .../MachinePerformanceContent.jsx | 9 ++- .../MachinePerformanceForm/StopsInfo.jsx | 9 ++- .../roadPatrols/operator/Toolbar.jsx | 2 +- .../roadPatrols/reports/ExcelPrint/index.jsx | 69 ++++++++++------- .../roadPatrols/reports/ReportLists.jsx | 4 +- .../dashboard/roadPatrols/reports/Toolbar.jsx | 6 +- .../dashboard/roadPatrols/reports/index.jsx | 13 +++- .../supervisor/ExcelPrint/index.jsx | 50 ++++++++---- .../MachinePerformanceContent.jsx | 11 ++- .../MachinePerformanceForm/StopsInfo.jsx | 4 +- .../MachinePerformanceForm/index.jsx | 2 +- .../roadPatrols/supervisor/Toolbar.jsx | 6 +- src/core/components/DataTable/Main.js | 19 +---- .../utils/DataTableFilterDataStructure.js | 9 +++ src/core/utils/isArrayEmpty.js | 6 ++ src/core/utils/routes.js | 26 +++++-- 32 files changed, 373 insertions(+), 135 deletions(-) rename src/components/dashboard/roadItems/reports/{ => ItemsReports}/ExcelPrint/index.jsx (77%) create mode 100644 src/components/dashboard/roadItems/reports/ItemsReports/TableToolbar.jsx create mode 100644 src/components/dashboard/roadItems/reports/SubItemsReports/ExcelPrint/index.jsx create mode 100644 src/components/dashboard/roadItems/reports/SubItemsReports/TableToolbar.jsx create mode 100644 src/core/utils/DataTableFilterDataStructure.js create mode 100644 src/core/utils/isArrayEmpty.js diff --git a/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx index 4cf871f..8862da8 100644 --- a/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx @@ -7,6 +7,8 @@ import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; import { EXPORT_ROAD_ITEMS_OPERATOR_LIST } from "@/core/utils/routes"; import { useTheme } from "@emotion/react"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; const PrintExcel = ({ table, filterData }) => { const theme = useTheme(); @@ -14,8 +16,7 @@ const PrintExcel = ({ table, filterData }) => { const [loading, setLoading] = useState(false); const requestServer = useRequest({ notificationSuccess: true }); const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { - if (filter.value) { - // Check if there's an active filter + if (filter.value && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) { acc.push({ id: key, value: filter.value }); } return acc; @@ -23,10 +24,14 @@ const PrintExcel = ({ table, filterData }) => { const filterParams = useMemo(() => { const params = new URLSearchParams(); - activeFilters.length > 0 && + if (activeFilters.length > 0) { activeFilters.map((filter, index) => { - params.set(`${filter.id}`, filter.value); + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); }); + } else { + params.set("filters", JSON.stringify([])); + } return params; }, [activeFilters]); @@ -36,7 +41,7 @@ const PrintExcel = ({ table, filterData }) => { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل عملیات فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) diff --git a/src/components/dashboard/roadItems/operator/Toolbar.jsx b/src/components/dashboard/roadItems/operator/Toolbar.jsx index 4990a23..d8c6fad 100644 --- a/src/components/dashboard/roadItems/operator/Toolbar.jsx +++ b/src/components/dashboard/roadItems/operator/Toolbar.jsx @@ -6,7 +6,7 @@ import ObservedGashtCreate from "./Actions/ObservedGashtCreate"; const Toolbar = ({ table, filterData, mutate }) => { return ( - {/**/} + diff --git a/src/components/dashboard/roadItems/reports/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/ExcelPrint/index.jsx similarity index 77% rename from src/components/dashboard/roadItems/reports/ExcelPrint/index.jsx rename to src/components/dashboard/roadItems/reports/ItemsReports/ExcelPrint/index.jsx index eb86d27..0e70403 100644 --- a/src/components/dashboard/roadItems/reports/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/ExcelPrint/index.jsx @@ -5,7 +5,10 @@ import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; -import { EXPORT_ROAD_ITEMS_OPERATOR_LIST } from "@/core/utils/routes"; +import { + EXPORT_COUNTRY_ROAD_ITEM_REPORTS_PER_ITEM, + EXPORT_PROVINCE_ROAD_ITEM_REPORTS_PER_ITEM, +} from "@/core/utils/routes"; import { useTheme } from "@emotion/react"; const PrintExcel = ({ table, filterData }) => { @@ -15,8 +18,7 @@ const PrintExcel = ({ table, filterData }) => { const requestServer = useRequest({ notificationSuccess: true }); const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { if (filter.value) { - // Check if there's an active filter - acc.push({ id: key, value: filter.value }); + acc.push({ id: filter.id, value: filter.value }); } return acc; }, []); @@ -32,11 +34,17 @@ const PrintExcel = ({ table, filterData }) => { const clickHandler = () => { setLoading(true); - requestServer(`${EXPORT_ROAD_ITEMS_OPERATOR_LIST}?${filterParams}`, "get", { + const CountryOrProvince = activeFilters.find((item) => item.id === "province_id"); + const requestUrl = + CountryOrProvince.value === "-1" + ? EXPORT_COUNTRY_ROAD_ITEM_REPORTS_PER_ITEM + : EXPORT_PROVINCE_ROAD_ITEM_REPORTS_PER_ITEM; + + requestServer(`${requestUrl}?${filterParams}`, "get", { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل گزارشات فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل گزارشات فعالیت روزانه براساس ایتم تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx index 8646e22..80821f8 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx @@ -2,8 +2,9 @@ import DataTableWithAuth from "@/core/components/DataTableWithAuth"; import { Box } from "@mui/material"; import { useMemo } from "react"; +import Toolbar from "./TableToolbar"; -const ReportLists = ({ itemsList, data }) => { +const ReportLists = ({ itemsList, data, specialFilter }) => { const columns = useMemo(() => { const dynamicColumns = itemsList?.map((item) => ({ @@ -85,6 +86,8 @@ const ReportLists = ({ itemsList, data }) => { page_name={"roadItemReportByItems"} table_name={"roadItemReportByItemsList"} enablePagination={false} + TableToolbar={Toolbar} + specialFilter={specialFilter} /> ); diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/TableToolbar.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/TableToolbar.jsx new file mode 100644 index 0000000..32c4395 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/ItemsReports/TableToolbar.jsx @@ -0,0 +1,10 @@ +import PrintExcel from "./ExcelPrint"; + +const Toolbar = ({ table, filterData }) => { + return ( + <> + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx index 137ed83..1efb794 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx @@ -18,6 +18,7 @@ const ReportPage = () => { const [data, setData] = useState(null); const { itemsList, loadingItemsList, errorItemsList } = useRoadItemGetItems(); const { data: userPermissions } = usePermissions(); + const { user } = useAuth(); const requestServer = useRequest(); const hasProvincesPermission = userPermissions.includes("show-road-item-supervise-cartable"); @@ -27,11 +28,22 @@ const ReportPage = () => { date_to: moment(new Date()).format("YYYY-MM-DD"), province_id: hasProvincesPermission ? "-1" : user.province_id, }; + const defaultFilter = [ + { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]; + const [specialFilter, setSpecialFilter] = useState(defaultFilter); const onSearchSubmit = async (data) => { const params = new URLSearchParams(); params.set("from_date", `${data.from_date}`); params.set("date_to", `${data.date_to}`); + setSpecialFilter([ + { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]); if (data.province_id === "-1") { try { const response = await requestServer(`${GET_PROVINCE_ACTIVITY_PER_ITEM}?${params}`); @@ -158,7 +170,7 @@ const ReportPage = () => { خطا در دریافت اطلاعات!!! ) : ( - + )} ); diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/ExcelPrint/index.jsx new file mode 100644 index 0000000..443135b --- /dev/null +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/ExcelPrint/index.jsx @@ -0,0 +1,77 @@ +"use client"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; +import { useMemo, useState } from "react"; +import moment from "jalali-moment"; +import FileSaver from "file-saver"; +import DescriptionIcon from "@mui/icons-material/Description"; +import useRequest from "@/lib/hooks/useRequest"; +import { useTheme } from "@emotion/react"; +import { + EXPORT_COUNTRY_ROAD_ITEM_REPORTS_PER_SUB_ITEM, + EXPORT_PROVINCE_ROAD_ITEM_REPORTS_PER_SUB_ITEM, +} from "@/core/utils/routes"; + +const PrintExcel = ({ table, filterData }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [loading, setLoading] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { + if (filter.value) { + acc.push({ id: filter.id, value: filter.value }); + } + return acc; + }, []); + + const filterParams = useMemo(() => { + const params = new URLSearchParams(); + activeFilters.length > 0 && + activeFilters.map((filter, index) => { + params.set(`${filter.id}`, filter.value); + }); + return params; + }, [activeFilters]); + + const clickHandler = () => { + setLoading(true); + const CountryOrProvince = activeFilters.find((item) => item.id === "province_id"); + const requestUrl = + CountryOrProvince.value === "-1" + ? EXPORT_COUNTRY_ROAD_ITEM_REPORTS_PER_SUB_ITEM + : EXPORT_PROVINCE_ROAD_ITEM_REPORTS_PER_SUB_ITEM; + requestServer(`${requestUrl}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی کارتابل گزارشات فعالیت روزانه براساس موارد اقدام شده تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + ); +}; +export default PrintExcel; diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/SubItemsList.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/SubItemsList.jsx index 3ab9fc1..d92a366 100644 --- a/src/components/dashboard/roadItems/reports/SubItemsReports/SubItemsList.jsx +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/SubItemsList.jsx @@ -1,7 +1,8 @@ import DataTableWithAuth from "@/core/components/DataTableWithAuth"; import { Box } from "@mui/material"; +import Toolbar from "./TableToolbar"; -const SubItemsList = ({ data }) => { +const SubItemsList = ({ data, specialFilter }) => { return ( { page_name={"roadItemReportBySubItems"} table_name={"roadItemReportBySubItemsList"} enablePagination={false} + TableToolbar={Toolbar} + specialFilter={specialFilter} /> ); diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/TableToolbar.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/TableToolbar.jsx new file mode 100644 index 0000000..32c4395 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/TableToolbar.jsx @@ -0,0 +1,10 @@ +import PrintExcel from "./ExcelPrint"; + +const Toolbar = ({ table, filterData }) => { + return ( + <> + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx index 1601b48..b5c8237 100644 --- a/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx @@ -106,12 +106,25 @@ const SubItemsReports = () => { province_id: hasProvincesPermission ? "-1" : user.province_id, item: 1, }; + const defaultFilter = [ + { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${defaultValues.item}`, datatype: "numeric", id: "item", fn: "equals" }, + { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]; + const [specialFilter, setSpecialFilter] = useState(defaultFilter); const onSearchSubmit = async (data) => { const params = new URLSearchParams(); params.set("from_date", `${data.from_date}`); params.set("date_to", `${data.date_to}`); params.set("item", `${data.item}`); + setSpecialFilter([ + { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${data.item}`, datatype: "numeric", id: "item", fn: "equals" }, + { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]); if (data.province_id === "-1") { try { const response = await requestServer(`${GET_PROVINCE_ACTIVITY_PER_SUB_ITEM}?${params}`); @@ -235,7 +248,7 @@ const SubItemsReports = () => { handleSubmit={handleSubmit} onSearchSubmit={onSearchSubmit} /> - {!data ? : } + {!data ? : } ); }; diff --git a/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx index 1cc0b2c..eca9fff 100644 --- a/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx @@ -6,6 +6,8 @@ import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; import { EXPORT_ROAD_ITEMS_SUPERVISOR_LIST } from "@/core/utils/routes"; import { useTheme } from "@emotion/react"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; const PrintExcel = ({ table, filterData }) => { const [loading, setLoading] = useState(false); @@ -13,8 +15,7 @@ const PrintExcel = ({ table, filterData }) => { const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const requestServer = useRequest(); const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { - if (filter.value) { - // Check if there's an active filter + if (filter.value && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) { acc.push({ id: key, value: filter.value }); } return acc; @@ -22,20 +23,23 @@ const PrintExcel = ({ table, filterData }) => { const filterParams = useMemo(() => { const params = new URLSearchParams(); - activeFilters.length > 0 && + if (activeFilters.length > 0) { activeFilters.map((filter, index) => { - params.set(`${filter.id}`, filter.value); + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); }); + } else { + params.set("filters", JSON.stringify([])); + } return params; }, [activeFilters]); - const clickHandler = () => { setLoading(true); requestServer(`${EXPORT_ROAD_ITEMS_SUPERVISOR_LIST}?${filterParams}`, "get", { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل ارزیابی فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) diff --git a/src/components/dashboard/roadItems/supervisor/Toolbar.jsx b/src/components/dashboard/roadItems/supervisor/Toolbar.jsx index 47cb9a9..32c4395 100644 --- a/src/components/dashboard/roadItems/supervisor/Toolbar.jsx +++ b/src/components/dashboard/roadItems/supervisor/Toolbar.jsx @@ -1,6 +1,10 @@ import PrintExcel from "./ExcelPrint"; const Toolbar = ({ table, filterData }) => { - return <>{/**/}; + return ( + <> + + + ); }; export default Toolbar; diff --git a/src/components/dashboard/roadPatrols/operator/Actions/Create/index.jsx b/src/components/dashboard/roadPatrols/operator/Actions/Create/index.jsx index 5c2f233..97ce660 100644 --- a/src/components/dashboard/roadPatrols/operator/Actions/Create/index.jsx +++ b/src/components/dashboard/roadPatrols/operator/Actions/Create/index.jsx @@ -22,7 +22,7 @@ const OperatorCreate = ({ mutate }) => { ) : ( - )} diff --git a/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx index 1c635c8..a5fb3b4 100644 --- a/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx @@ -1,17 +1,21 @@ -import { Button, CircularProgress } from "@mui/material"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; import { useMemo, useState } from "react"; import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; import { EXPORT_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; +import { useTheme } from "@emotion/react"; const PrintExcel = ({ table, filterData }) => { const [loading, setLoading] = useState(false); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const requestServer = useRequest(); const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { - if (filter.value) { - // Check if there's an active filter + if (filter.value && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) { acc.push({ id: key, value: filter.value }); } return acc; @@ -19,10 +23,14 @@ const PrintExcel = ({ table, filterData }) => { const filterParams = useMemo(() => { const params = new URLSearchParams(); - activeFilters.length > 0 && + if (activeFilters.length > 0) { activeFilters.map((filter, index) => { - params.set(`${filter.id}`, filter.value); + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); }); + } else { + params.set("filters", JSON.stringify([])); + } return params; }, [activeFilters]); @@ -32,7 +40,7 @@ const PrintExcel = ({ table, filterData }) => { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل عملیات تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل عملیات گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) @@ -42,16 +50,25 @@ const PrintExcel = ({ table, filterData }) => { }; return ( - + <> + {isMobile ? ( + + + + ) : ( + + )} + ); }; diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx index e13b779..40f59da 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx @@ -92,7 +92,7 @@ const CompeleteRequest = ({ allData, handlePrev, mutate, setOpen }) => { setOtpSended(true); setDelayPerRequest(true); setValueSend("phoneNumber", data.phoneNumber); - } catch (error) { } + } catch (error) {} }; const sendData = useCallback( @@ -287,8 +287,8 @@ const CompeleteRequest = ({ allData, handlePrev, mutate, setOpen }) => { {delayPerRequest ? formatCounter(counter) : isSubmittingOtp - ? "درحال دریافت کد ..." - : "دریافت کد"} + ? "درحال دریافت کد ..." + : "دریافت کد"} diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx index 91cc822..14fd172 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetailInfo.jsx @@ -4,7 +4,7 @@ import MapLoading from "@/core/components/MapLayer/Loading"; import ShowPlak from "@/core/components/ShowPlak"; import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS"; import AddRoadIcon from "@mui/icons-material/AddRoad"; -import BadgeIcon from '@mui/icons-material/Badge'; +import BadgeIcon from "@mui/icons-material/Badge"; import DirectionsCarFilledIcon from "@mui/icons-material/DirectionsCarFilled"; import ElectricCarIcon from "@mui/icons-material/ElectricCar"; import LocalGasStationIcon from "@mui/icons-material/LocalGasStation"; @@ -12,16 +12,7 @@ import QrCode2Icon from "@mui/icons-material/QrCode2"; import QueryBuilderIcon from "@mui/icons-material/QueryBuilder"; import ShareLocationIcon from "@mui/icons-material/ShareLocation"; import WatchLaterIcon from "@mui/icons-material/WatchLater"; -import { - Box, - Card, - CardContent, - Chip, - Divider, - Fade, - Stack, - Typography -} from "@mui/material"; +import { Box, Card, CardContent, Chip, Divider, Fade, Stack, Typography } from "@mui/material"; import moment from "jalali-moment"; import dynamic from "next/dynamic"; import React, { useEffect } from "react"; diff --git a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx index c16b10d..8570f58 100644 --- a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx +++ b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx @@ -44,7 +44,7 @@ const MachinePerformanceContent = ({ machinesLists }) => { return ( <> - + { - + { return ( - + { return ( - {/**/} + ); }; diff --git a/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx index 414a9cb..192d4fb 100644 --- a/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadPatrols/reports/ExcelPrint/index.jsx @@ -1,16 +1,21 @@ -import { Button, CircularProgress } from "@mui/material"; +"use client"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; import { useMemo, useState } from "react"; import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; +import { useTheme } from "@emotion/react"; +import { EXPORT_COUNTRY_ROAD_PATROL_REPORTS, EXPORT_PROVINCE_ROAD_PATROL_REPORTS } from "@/core/utils/routes"; const PrintExcel = ({ table, filterData }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const [loading, setLoading] = useState(false); - const requestServer = useRequest(); + const requestServer = useRequest({ notificationSuccess: true }); const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { - if (filter) { - acc.push({ id: key, value: filter }); + if (filter.value) { + acc.push({ id: filter.id, value: filter.value }); } return acc; }, []); @@ -25,31 +30,43 @@ const PrintExcel = ({ table, filterData }) => { }, [activeFilters]); const clickHandler = () => { - // setLoading(true); - // requestServer(`${EXPORT_ROAD_PATROL_OPERATOR_LIST}?${filterParams}`, "get", { - // requestOptions: { responseType: "blob" }, - // }) - // .then((response) => { - // const filename = `خروجی کارتابل گزارشات گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; - // FileSaver.saveAs(response.data, filename); - // }) - // .catch(() => {}) - // .finally(() => { - // setLoading(false); - // }); + setLoading(true); + const CountryOrProvince = activeFilters.find((item) => item.id === "province_id"); + const requestUrl = + CountryOrProvince.value === "-1" ? EXPORT_COUNTRY_ROAD_PATROL_REPORTS : EXPORT_PROVINCE_ROAD_PATROL_REPORTS; + requestServer(`${requestUrl}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی کارتابل گزارشات گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); }; return ( - + <> + {isMobile ? ( + + + + ) : ( + + )} + ); }; export default PrintExcel; diff --git a/src/components/dashboard/roadPatrols/reports/ReportLists.jsx b/src/components/dashboard/roadPatrols/reports/ReportLists.jsx index 4a3d8e5..76f6cef 100644 --- a/src/components/dashboard/roadPatrols/reports/ReportLists.jsx +++ b/src/components/dashboard/roadPatrols/reports/ReportLists.jsx @@ -4,7 +4,7 @@ import { Box } from "@mui/material"; import { useMemo } from "react"; import Toolbar from "./Toolbar"; -const ReportLists = ({ data }) => { +const ReportLists = ({ data, specialFilter }) => { const columns = useMemo(() => { return [ { @@ -195,12 +195,12 @@ const ReportLists = ({ data }) => { return ( ); diff --git a/src/components/dashboard/roadPatrols/reports/Toolbar.jsx b/src/components/dashboard/roadPatrols/reports/Toolbar.jsx index 5dc6e42..7dd930f 100644 --- a/src/components/dashboard/roadPatrols/reports/Toolbar.jsx +++ b/src/components/dashboard/roadPatrols/reports/Toolbar.jsx @@ -2,6 +2,10 @@ import PrintExcel from "./ExcelPrint"; import { Box } from "@mui/material"; const Toolbar = ({ table, filterData, mutate }) => { - return {/**/}; + return ( + + + + ); }; export default Toolbar; diff --git a/src/components/dashboard/roadPatrols/reports/index.jsx b/src/components/dashboard/roadPatrols/reports/index.jsx index 12743aa..a71eb35 100644 --- a/src/components/dashboard/roadPatrols/reports/index.jsx +++ b/src/components/dashboard/roadPatrols/reports/index.jsx @@ -25,11 +25,22 @@ const ReportPage = () => { date_to: moment(new Date()).format("YYYY-MM-DD"), province_id: hasProvincesPermission ? "-1" : user.province_id, }; + const defaultFilter = [ + { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]; + const [specialFilter, setSpecialFilter] = useState(defaultFilter); const onSearchSubmit = async (data) => { const params = new URLSearchParams(); params.set("from_date", `${data.from_date}`); params.set("date_to", `${data.date_to}`); + setSpecialFilter([ + { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]); if (data.province_id === "-1") { try { const response = await requestServer(`${GET_PROVINCE_ACTIVITY_REPORT}?${params}`); @@ -115,7 +126,7 @@ const ReportPage = () => { handleSubmit={handleSubmit} onSearchSubmit={onSearchSubmit} /> - {!data ? : } + {!data ? : } ); }; diff --git a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx index e5ba95a..ba176bb 100644 --- a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx @@ -1,17 +1,21 @@ -import { Button, CircularProgress } from "@mui/material"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; import { useMemo, useState } from "react"; import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; import { EXPORT_ROAD_PATROL_SUPERVISOR_LIST } from "@/core/utils/routes"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; +import { useTheme } from "@emotion/react"; const PrintExcel = ({ table, filterData }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const [loading, setLoading] = useState(false); const requestServer = useRequest(); const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { - if (filter.value) { - // Check if there's an active filter + if (filter.value && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) { acc.push({ id: key, value: filter.value }); } return acc; @@ -19,10 +23,14 @@ const PrintExcel = ({ table, filterData }) => { const filterParams = useMemo(() => { const params = new URLSearchParams(); - activeFilters.length > 0 && + if (activeFilters.length > 0) { activeFilters.map((filter, index) => { - params.set(`${filter.id}`, filter.value); + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); }); + } else { + params.set("filters", JSON.stringify([])); + } return params; }, [activeFilters]); @@ -32,7 +40,7 @@ const PrintExcel = ({ table, filterData }) => { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل ارزیابی تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل ارزیابی گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) @@ -42,17 +50,25 @@ const PrintExcel = ({ table, filterData }) => { }; return ( - + <> + {isMobile ? ( + + + + ) : ( + + )} + ); }; diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx index 93c0874..8570f58 100644 --- a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx @@ -44,7 +44,7 @@ const MachinePerformanceContent = ({ machinesLists }) => { return ( <> - + { - + { ); }; -export default MachinePerformanceContent; \ No newline at end of file +export default MachinePerformanceContent; diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx index 0415b7c..24b45cc 100644 --- a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/StopsInfo.jsx @@ -5,7 +5,7 @@ import LocalGasStationIcon from "@mui/icons-material/LocalGasStation"; import QrCode2Icon from "@mui/icons-material/QrCode2"; import AddRoadIcon from "@mui/icons-material/AddRoad"; import DirectionsCarFilledIcon from "@mui/icons-material/DirectionsCarFilled"; -import BadgeIcon from '@mui/icons-material/Badge'; +import BadgeIcon from "@mui/icons-material/Badge"; import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS"; import ShowPlak from "@/core/components/ShowPlak"; @@ -162,4 +162,4 @@ const StopsInfo = ({ selectedPoint, staticData }) => { ); }; -export default StopsInfo; \ No newline at end of file +export default StopsInfo; diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx index 3eb7b87..7e2902f 100644 --- a/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/index.jsx @@ -48,4 +48,4 @@ const MachinePerformanceForm = ({ row }) => { ); }; -export default MachinePerformanceForm; \ No newline at end of file +export default MachinePerformanceForm; diff --git a/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx b/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx index 47cb9a9..32c4395 100644 --- a/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/Toolbar.jsx @@ -1,6 +1,10 @@ import PrintExcel from "./ExcelPrint"; const Toolbar = ({ table, filterData }) => { - return <>{/**/}; + return ( + <> + + + ); }; export default Toolbar; diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js index eb11e54..e9a3a37 100644 --- a/src/core/components/DataTable/Main.js +++ b/src/core/components/DataTable/Main.js @@ -7,6 +7,8 @@ import { useCallback, useMemo, useState } from "react"; import useRequest from "@/lib/hooks/useRequest"; import useSWR from "swr"; import { flattenObjectOfObjects } from "@/core/utils/flattenObjectOfObjects"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; const DataTable_Main = (props) => { const request = useRequest(); @@ -37,26 +39,13 @@ const DataTable_Main = (props) => { const params = new URLSearchParams(); params.set("start", `${pagination.pageIndex * pagination.pageSize}`); params.set("size", pagination.pageSize); - const isValueEmpty = (value) => { - if (Array.isArray(value)) { - return value.length === 0 || value.every((v) => v === ""); - } - return value === "" || value === null || value === undefined; - }; if (specialFilter) { const filteredSpecialFilterData = Object.values(specialFilter).filter( - (filter) => !isValueEmpty(filter.value) + (filter) => !isArrayEmpty(filter.value) ); params.set("filters", JSON.stringify(filteredSpecialFilterData || [])); } else { - const filteredFilterData = Object.values(filterData) - .filter((filter) => !isValueEmpty(filter.value)) - .map(({ filterMode, id, ...rest }) => ({ - ...rest, - id: id.replace(/__/g, "."), - fn: filterMode, - })); - + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); } params.set("sorting", JSON.stringify(sortData)); diff --git a/src/core/utils/DataTableFilterDataStructure.js b/src/core/utils/DataTableFilterDataStructure.js new file mode 100644 index 0000000..c88bdfc --- /dev/null +++ b/src/core/utils/DataTableFilterDataStructure.js @@ -0,0 +1,9 @@ +export default function DataTableFilterDataStructure(filterData, isValueEmpty) { + return Object.values(filterData) + .filter((filter) => !isValueEmpty(filter.value)) + .map(({ filterMode, id, ...rest }) => ({ + ...rest, + id: id.replace(/__/g, "."), + fn: filterMode, + })); +} diff --git a/src/core/utils/isArrayEmpty.js b/src/core/utils/isArrayEmpty.js new file mode 100644 index 0000000..81624fb --- /dev/null +++ b/src/core/utils/isArrayEmpty.js @@ -0,0 +1,6 @@ +export default function isArrayEmpty(value) { + if (Array.isArray(value)) { + return value.length === 0 || value.every((v) => v === ""); + } + return value === "" || value === null || value === undefined; +} diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index a217a6e..2e5a8f8 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -30,23 +30,33 @@ export const UPDATE_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/update"; //road patrol export const GET_ROAD_PATROL_OPERATOR_LIST = api + "/api/v3/road_patrols/operator_index"; -export const EXPORT_ROAD_PATROL_OPERATOR_LIST = api + "/v2/road_patrols/operator/cartable/report"; +export const EXPORT_ROAD_PATROL_OPERATOR_LIST = api + "/api/v3/road_patrols/operator_report"; export const GET_ROAD_PATROL_SUPERVISOR_LIST = api + "/api/v3/road_patrols/supervisor_index"; -export const EXPORT_ROAD_PATROL_SUPERVISOR_LIST = api + "/v2/road_patrols/supervisor/cartable/report"; +export const EXPORT_ROAD_PATROL_SUPERVISOR_LIST = api + "/api/v3/road_patrols/supervisor_report"; +export const EXPORT_PROVINCE_ROAD_PATROL_REPORTS = api + "/api/v3/road_patrol_reports/province_activity_excel"; +export const EXPORT_COUNTRY_ROAD_PATROL_REPORTS = api + "/api/v3/road_patrol_reports/country_activity_excel"; export const DELETE_ROAD_PATROL_SUPERVISOR = api + "/api/v3/road_patrols/delete"; export const GET_ROAD_PATROL_OPERATOR_REPORT = api + "/v2/road_patrols/operator/report"; export const GET_ROAD_PATROL_SUPERVISOR_REPORT = api + "/v2/road_patrols/supervisor/report"; export const CREATE_PATROL = api + "/api/v3/road_patrols/store"; export const GET_FMS_DATA = api + "/api/v3/fms_vehicle/get_activity"; export const GET_OTP_TOKEN = api + "/v2/get_otp_token"; -export const GET_PROVINCE_ACTIVITY_REPORT = api + "/api/v3/road_patrol_reports/province_activity"; -export const GET_CITY_ACTIVITY_REPORT = api + "/api/v3/road_patrol_reports/city_activity"; +export const GET_PROVINCE_ACTIVITY_REPORT = api + "/api/v3/road_patrol_reports/country_activity"; +export const GET_CITY_ACTIVITY_REPORT = api + "/api/v3/road_patrol_reports/province_activity"; // road items export const GET_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_index"; export const EXPORT_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_report"; export const GET_ROAD_ITEMS_OPERATOR_LIST = api + "/api/v3/road_items/operator_index"; export const EXPORT_ROAD_ITEMS_OPERATOR_LIST = api + "/api/v3/road_items/operator_report"; +export const EXPORT_PROVINCE_ROAD_ITEM_REPORTS_PER_ITEM = + api + "/api/v3/road_item_reports/province_activity_excel_per_item"; +export const EXPORT_COUNTRY_ROAD_ITEM_REPORTS_PER_ITEM = + api + "/api/v3/road_item_reports/country_activity_excel_per_item"; +export const EXPORT_PROVINCE_ROAD_ITEM_REPORTS_PER_SUB_ITEM = + api + "/api/v3/road_item_reports/province_activity_excel_per_sub_item"; +export const EXPORT_COUNTRY_ROAD_ITEM_REPORTS_PER_SUB_ITEM = + api + "/api/v3/road_item_reports/country_activity_excel_per_sub_item"; export const GET_ROAD_ITEMS_ITEM = api + "/v2/items"; export const GET_ROAD_ITEMS_SUB_ITEM = api + "/v2/sub_items"; export const GET_EDARAT_LISTS = api + "/public/contents/edarate_shahri_by_province"; @@ -59,10 +69,10 @@ export const RESTORE_BY_SUPERVISOR = api + "/api/v3/road_items/restore"; export const GET_CAR_LIST_SEARCH = api + "/api/v3/cmms_machines/search"; export const GET_RAHDARANS_LIST_SEARCH = api + "/api/v3/rahdaran/search"; export const GET_OBSERVED_GASHT_LIST = api + "/api/v3/observed_items/filter"; -export const GET_PROVINCE_ACTIVITY_PER_ITEM = api + "/api/v3/road_item_reports/province_activity_per_item"; -export const GET_CITY_ACTIVITY_PER_ITEM = api + "/api/v3/road_item_reports/city_activity_per_item"; -export const GET_PROVINCE_ACTIVITY_PER_SUB_ITEM = api + "/api/v3/road_item_reports/province_activity_per_sub_item"; -export const GET_CITY_ACTIVITY_PER_SUB_ITEM = api + "/api/v3/road_item_reports/city_activity_per_sub_item"; +export const GET_PROVINCE_ACTIVITY_PER_ITEM = api + "/api/v3/road_item_reports/country_activity_per_item"; +export const GET_CITY_ACTIVITY_PER_ITEM = api + "/api/v3/road_item_reports/province_activity_per_item"; +export const GET_PROVINCE_ACTIVITY_PER_SUB_ITEM = api + "/api/v3/road_item_reports/country_activity_per_sub_item"; +export const GET_CITY_ACTIVITY_PER_SUB_ITEM = api + "/api/v3/road_item_reports/province_activity_per_sub_item"; // activity code log export const ACTIVITY_LOG = api + "/api/v3/profile/add_activity"; From d3a301c22dcb2acc161206c0e08035e1e70cadf8 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 11 Feb 2025 09:56:58 +0000 Subject: [PATCH 02/32] Feature/amiriis parakandegi --- package.json | 1 + .../dashboard/road-items/reports/map/page.js | 6 + src/assets/scss/global.scss | 1 + src/assets/scss/map-styles.scss | 32 +--- .../RowActions/MapModify/PolygonForDesign.jsx | 2 - .../roadItems/operator/OperatorList.jsx | 4 +- .../RowActions/DescriptionForm/index.jsx | 4 +- .../reports/Map/ClusterSwitch/index.jsx | 25 ++++ .../Filter/Drawer/ActivityDateTimeFilter.jsx | 32 ++++ .../Map/Filter/Drawer/EdarehShahriFilter.jsx | 65 ++++++++ .../reports/Map/Filter/Drawer/ItemFilter.jsx | 47 ++++++ .../Map/Filter/Drawer/ProvinceFilter.jsx | 47 ++++++ .../Map/Filter/Drawer/StatusFilter.jsx | 35 +++++ .../Map/Filter/Drawer/SubItemFilter.jsx | 65 ++++++++ .../reports/Map/Filter/Drawer/index.jsx | 141 ++++++++++++++++++ .../roadItems/reports/Map/Filter/index.jsx | 44 ++++++ .../roadItems/reports/Map/Legend/index.jsx | 48 ++++++ .../DialogInfoItem/ContentInfoItem.jsx | 104 +++++++++++++ .../Map/PointsOnMap/DialogInfoItem/index.jsx | 51 +++++++ .../reports/Map/PointsOnMap/index.jsx | 71 +++++++++ .../dashboard/roadItems/reports/Map/index.jsx | 127 ++++++++++++++++ .../roadItems/supervisor/SupervisorList.jsx | 2 +- .../DataTable/filter/FilterBody.jsx | 27 +--- .../components/DataTable/filter/index.jsx | 22 +-- src/core/components/LoadingHardPage.jsx | 3 - src/core/components/MapInfoOneMarker.jsx | 4 +- src/core/components/MapInfoTwoMarker.jsx | 8 +- src/core/components/ScrollBox.jsx | 19 +++ src/core/utils/pageMenu.js | 2 +- src/core/utils/routes.js | 2 + src/lib/hooks/useRoadItemGetISubtems.js | 4 +- 31 files changed, 970 insertions(+), 75 deletions(-) create mode 100644 src/app/(withAuth)/dashboard/road-items/reports/map/page.js create mode 100644 src/components/dashboard/roadItems/reports/Map/ClusterSwitch/index.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ActivityDateTimeFilter.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/Drawer/EdarehShahriFilter.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ItemFilter.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ProvinceFilter.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/Drawer/StatusFilter.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/Drawer/SubItemFilter.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Filter/index.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/Legend/index.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/index.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/PointsOnMap/index.jsx create mode 100644 src/components/dashboard/roadItems/reports/Map/index.jsx create mode 100644 src/core/components/ScrollBox.jsx diff --git a/package.json b/package.json index c5b2139..f5c7205 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "react-hook-form": "^7.52.1", "react-leaflet": "^4.2.1", "react-leaflet-draw": "^0.20.4", + "react-leaflet-markercluster": "^4.2.1", "react-toastify": "^10.0.5", "sass": "^1.75.0", "sharp": "^0.33.4", diff --git a/src/app/(withAuth)/dashboard/road-items/reports/map/page.js b/src/app/(withAuth)/dashboard/road-items/reports/map/page.js new file mode 100644 index 0000000..00f000d --- /dev/null +++ b/src/app/(withAuth)/dashboard/road-items/reports/map/page.js @@ -0,0 +1,6 @@ +import RoadItemsReportMap from "@/components/dashboard/roadItems/reports/Map"; + +const Page = () => { + return ; +}; +export default Page; diff --git a/src/assets/scss/global.scss b/src/assets/scss/global.scss index 7b73e7e..1fc9f82 100644 --- a/src/assets/scss/global.scss +++ b/src/assets/scss/global.scss @@ -1,6 +1,7 @@ @import "fontiran"; @import "leaflet/dist/leaflet.css"; @import "react-toastify/dist/ReactToastify.css"; +@import "./map-styles.scss"; .filter-toast { box-shadow: diff --git a/src/assets/scss/map-styles.scss b/src/assets/scss/map-styles.scss index 44e6651..595f664 100644 --- a/src/assets/scss/map-styles.scss +++ b/src/assets/scss/map-styles.scss @@ -1,27 +1,9 @@ -.leaflet-draw-tooltip { - background-color: #4f4f4f; - font-family: IRANSans; +.custom-marker-cluster { + font-family: IRANSansFaNum; + background-color: rgba(51, 181, 229, 0.6); color: #fff; - font-size: 10px; - padding: 2px 8px; - margin-right: 15px; - border-radius: 4px; - box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); -} - -.leaflet-draw-tooltip:before { - content: ""; - position: absolute; - bottom: 100%; - left: 103%; - transform: rotate(90deg); - margin-left: -6px; - border-width: 6px; - border-style: solid; - border-color: transparent transparent #333 transparent; -} - -.leaflet-draw-tooltip-subtext { - font-size: 12px; - color: #ffeb3b; + border-radius: 50%; + text-align: center; + font-size: 14px; + line-height: 40px; } diff --git a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx index cd75f8d..4e1bb43 100644 --- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx +++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/MapModify/PolygonForDesign.jsx @@ -1,7 +1,5 @@ "use client"; -import "leaflet-draw/dist/leaflet.draw.css"; -import "@/assets/scss/map-styles.scss"; import { FeatureGroup, useMap } from "react-leaflet"; import { EditControl } from "react-leaflet-draw"; import { useEffect, useRef, useState } from "react"; diff --git a/src/components/dashboard/roadItems/operator/OperatorList.jsx b/src/components/dashboard/roadItems/operator/OperatorList.jsx index 52913cc..33b37c6 100644 --- a/src/components/dashboard/roadItems/operator/OperatorList.jsx +++ b/src/components/dashboard/roadItems/operator/OperatorList.jsx @@ -300,7 +300,7 @@ const OperatorList = () => { }, { accessorKey: "status", - header: "وضعیت", // Status + header: "وضعیت نظارت", id: "status", enableColumnFilter: true, datatype: "numeric", @@ -318,7 +318,7 @@ const OperatorList = () => { }, { accessorKey: "supervisor_description", - header: "توضیحات کارشناس", // Description + header: "توضیحات ناظر", id: "supervisor_description", enableColumnFilter: false, enableSorting: false, diff --git a/src/components/dashboard/roadItems/operator/RowActions/DescriptionForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/DescriptionForm/index.jsx index 481e2d6..91b135a 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/DescriptionForm/index.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/DescriptionForm/index.jsx @@ -8,7 +8,7 @@ const DescriptionForm = ({ description }) => { return ( <> - + setOpenOfficerDescriptionDialog(true)}> @@ -30,7 +30,7 @@ const DescriptionForm = ({ description }) => { > - توضیحات کارشناس + توضیحات ناظر setOpenOfficerDescriptionDialog(false)} size="small"> diff --git a/src/components/dashboard/roadItems/reports/Map/ClusterSwitch/index.jsx b/src/components/dashboard/roadItems/reports/Map/ClusterSwitch/index.jsx new file mode 100644 index 0000000..e8fac87 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/ClusterSwitch/index.jsx @@ -0,0 +1,25 @@ +import DoneIcon from "@mui/icons-material/Done"; +import { Chip, Tooltip, Zoom } from "@mui/material"; + +const ClusterSwitch = ({ isCluster, setCluster, disabled, max }) => { + return ( + + + setCluster((c) => !c)} + sx={{ borderRadius: 1 }} + icon={ + + + + } + /> + + + ); +}; +export default ClusterSwitch; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ActivityDateTimeFilter.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ActivityDateTimeFilter.jsx new file mode 100644 index 0000000..cab7ddc --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ActivityDateTimeFilter.jsx @@ -0,0 +1,32 @@ +import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker"; +import { Box } from "@mui/material"; + +const ActivityDateTimeFilter = ({ value, onChange, error }) => { + return ( + + { + onChange([formattedDate, value[1]]); + }} + maxDate={value[1]} + placeholder={`از تاریخ`} + /> + { + onChange([value[0], formattedDate]); + }} + minDate={value[0]} + placeholder={`تا تاریخ`} + helperText={error ? error.message : null} + error={Boolean(error)} + /> + + ); +}; +export default ActivityDateTimeFilter; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/EdarehShahriFilter.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/EdarehShahriFilter.jsx new file mode 100644 index 0000000..eb08e27 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/EdarehShahriFilter.jsx @@ -0,0 +1,65 @@ +"use client"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; +import { useEffect, useMemo, useState } from "react"; +import { useWatch } from "react-hook-form"; + +const EdarehShahriFilter = ({ control, value, onChange }) => { + const dependencyField = useWatch({ control, name: "province_id" }); + return ; +}; + +const EdarehShahriController = ({ value, onChange, dependencyField }) => { + const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(dependencyField); + const [prevDependency, setPrevDependency] = useState(dependencyField); + + const columnSelectOption = useMemo(() => { + if (dependencyField === "") { + return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; + } + if (loadingEdaratList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorEdaratList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل ادارات" }, + ...edaratList.map((edare) => ({ + value: edare.id, + label: edare.name_fa, + })), + ]; + }, [edaratList, loadingEdaratList, errorEdaratList]); + + useEffect(() => { + if (prevDependency === dependencyField) return; + onChange(""); + setPrevDependency(dependencyField); + }, [dependencyField]); + + return ( + + + اداره + + + + ); +}; +export default EdarehShahriFilter; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ItemFilter.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ItemFilter.jsx new file mode 100644 index 0000000..de27d11 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ItemFilter.jsx @@ -0,0 +1,47 @@ +import useRoadItemGetItems from "@/lib/hooks/useRoadItemGetItems"; +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; +import { useMemo } from "react"; + +const ItemFilter = ({ value, onChange }) => { + const { itemsList, loadingItemsList, errorItemsList } = useRoadItemGetItems(); + const columnSelectOption = useMemo(() => { + if (loadingItemsList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorItemsList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "همه آیتم ها" }, + ...itemsList.map((item) => ({ + value: item.id, + label: item.name, + })), + ]; + }, [itemsList, loadingItemsList, errorItemsList]); + + return ( + + + آیتم فعالیت + + + + ); +}; +export default ItemFilter; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ProvinceFilter.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ProvinceFilter.jsx new file mode 100644 index 0000000..4de3f9e --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/ProvinceFilter.jsx @@ -0,0 +1,47 @@ +import useProvinces from "@/lib/hooks/useProvince"; +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; +import { useMemo } from "react"; + +const ProvinceFilter = ({ value, onChange }) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const columnSelectOption = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + + return ( + + + استان + + + + ); +}; +export default ProvinceFilter; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/StatusFilter.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/StatusFilter.jsx new file mode 100644 index 0000000..dd52ea5 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/StatusFilter.jsx @@ -0,0 +1,35 @@ +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; + +const StatusFilter = ({ value, onChange }) => { + const statusOptions = [ + { value: "", label: "همه وضعیت ها" }, + { value: 0, label: "درحال بررسی" }, + { value: 1, label: "تایید" }, + { value: 2, label: "عدم تایید" }, + ]; + + return ( + + + وضعیت نظارت + + + + ); +}; +export default StatusFilter; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/SubItemFilter.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/SubItemFilter.jsx new file mode 100644 index 0000000..e9f973a --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/SubItemFilter.jsx @@ -0,0 +1,65 @@ +"use client"; +import useRoadItemGetSubItems from "@/lib/hooks/useRoadItemGetISubtems"; +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; +import { useEffect, useMemo, useState } from "react"; +import { useWatch } from "react-hook-form"; + +const SubItemFilter = ({ control, value, onChange }) => { + const dependencyField = useWatch({ control, name: "item_id" }); + return ; +}; + +const SubItemController = ({ value, onChange, dependencyField }) => { + const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetSubItems(dependencyField); + const [prevDependency, setPrevDependency] = useState(dependencyField); + + const columnSelectOption = useMemo(() => { + if (dependencyField === "") { + return [{ value: "empty", label: "ابتدا آیتم فعالیت را انتخاب کنید" }]; + } + if (loadingSubItemsList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorSubItemsList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "همه اقدام ها" }, + ...subItemsList.map((item) => ({ + value: item.sub_item, + label: item.name, + })), + ]; + }, [subItemsList, loadingSubItemsList, errorSubItemsList]); + + useEffect(() => { + if (prevDependency === dependencyField) return; + onChange(""); + setPrevDependency(dependencyField); + }, [dependencyField]); + + return ( + + + اقدام انجام شده + + + + ); +}; +export default SubItemFilter; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx new file mode 100644 index 0000000..9700220 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx @@ -0,0 +1,141 @@ +import ScrollBox from "@/core/components/ScrollBox"; +import { Box, Button, IconButton, Typography } from "@mui/material"; +import CancelIcon from "@mui/icons-material/Cancel"; +import FilterAltIcon from "@mui/icons-material/FilterAlt"; +import { Controller, useForm } from "react-hook-form"; +import ProvinceFilter from "./ProvinceFilter"; +import EdarehShahriFilter from "./EdarehShahriFilter"; +import ItemFilter from "./ItemFilter"; +import SubItemFilter from "./SubItemFilter"; +import StatusFilter from "./StatusFilter"; +import ActivityDateTimeFilter from "./ActivityDateTimeFilter"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as Yup from "yup"; + +const validationSchema = Yup.object({ + activity_date_time: Yup.array() + .of(Yup.string().nullable()) + .test({ + test(value, ctx) { + const [start, end] = value || ["", ""]; + if ((start && !end) || (!start && end)) { + return ctx.createError({ message: "این بخش را تکمیل نمایید" }); + } + return true; + }, + }), +}); + +const headerSx = { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + px: 2, + py: 1, + backgroundColor: "#155175", + boxShadow: "rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px", + maxWidth: "450px", +}; + +const headerTitleSx = { display: "flex", alignItems: "center" }; +const headerIconSx = { color: "#fff", mr: 1 }; +const iconButtonSx = { color: "#fff" }; + +const formContainerSx = { px: 2, py: 3 }; +const footerSx = { display: "flex", justifyContent: "center", alignItems: "center", pb: 2 }; + +const submitButtonSx = { + px: 8, + boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px", + backgroundColor: "primary2", + ":hover": { backgroundColor: "primary2" }, +}; + +const FilterDrawer = ({ defaultValues, setFilterData, closeDrawer }) => { + const { + control, + handleSubmit, + formState: { isDirty }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onBlur", + }); + + const onSubmit = (data) => { + setFilterData(data); + closeDrawer(); + }; + + return ( + <> + + + + + فیلتر + + + + + + + + +
+ + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + + + + + +
+
+ + ); +}; +export default FilterDrawer; diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/index.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/index.jsx new file mode 100644 index 0000000..c12a511 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Filter/index.jsx @@ -0,0 +1,44 @@ +"use client"; +import { Box, Button, Drawer } from "@mui/material"; +import FilterListIcon from "@mui/icons-material/FilterList"; +import { useCallback, useState } from "react"; +import FilterDrawer from "./Drawer"; + +const drawerSx = { + overflowY: "hidden", + display: "flex", + flexDirection: "column", + height: "100%", + zIndex: "1300", +}; + +const boxSx = { + width: { xs: 300, sm: 450 }, +}; + +const Filter = ({ filterData, setFilterData }) => { + const [open, setOpen] = useState(false); + + const openDrawer = useCallback(() => setOpen(true), []); + const closeDrawer = useCallback(() => setOpen(false), []); + + return ( + <> + + + + {open && ( + + )} + + + + ); +}; +export default Filter; diff --git a/src/components/dashboard/roadItems/reports/Map/Legend/index.jsx b/src/components/dashboard/roadItems/reports/Map/Legend/index.jsx new file mode 100644 index 0000000..20707aa --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/Legend/index.jsx @@ -0,0 +1,48 @@ +import { Box, Stack, Typography } from "@mui/material"; + +const Legend = () => { + return ( + + + theme.palette.primary.main, + background: (theme) => theme.palette.primary.light, + }} + /> + در حال بررسی + + + theme.palette.success.main, + background: (theme) => theme.palette.success.light, + }} + /> + تایید شده + + + theme.palette.warning.main, + background: (theme) => theme.palette.warning.light, + }} + /> + عدم تایید + + + ); +}; +export default Legend; diff --git a/src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx b/src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx new file mode 100644 index 0000000..a10a1aa --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx @@ -0,0 +1,104 @@ +import { Box, Chip, Divider, Stack, Typography } from "@mui/material"; +import moment from "jalali-moment"; +import Image from "next/image"; + +const ContentInfoItem = ({ data }) => { + return ( + + + + + {data.id !== "" && data.id} + + + + + + {data.province_fa !== "" && data.province_fa} + + + + + + + {data.edarat_name !== "" && data.edarat_name} + + + + + + + {data.item_fa !== "" && data.item_fa} + + + + + + + {data.sub_item_fa !== "" && data.sub_item_fa} + + + + + + + + {(data.sub_item_data / 1).toLocaleString()} + + + {`(${data.unit_fa})`} + + + + + + + + {data.activity_date_time && + moment(data.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD")} + + + + + + + {data.created_at && moment(data.created_at).locale("fa").format("HH:mm | yyyy/MM/DD")} + + + + + + + {data.status_fa !== "" && data.status_fa} + + + + + + + before_image + + + + + + before_image + + + + + ); +}; +export default ContentInfoItem; diff --git a/src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/index.jsx b/src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/index.jsx new file mode 100644 index 0000000..a3c9b23 --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/PointsOnMap/DialogInfoItem/index.jsx @@ -0,0 +1,51 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_ROAD_ITEM } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { Button, DialogActions, DialogContent, DialogTitle } from "@mui/material"; +import { useEffect, useState } from "react"; +import ContentInfoItem from "./ContentInfoItem"; + +const DialogInfoItem = ({ selectedId, closeDialog }) => { + const request = useRequest(); + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + + useEffect(() => { + const controller = new AbortController(); + + const fetchItem = async () => { + setLoading(true); + setData(null); + try { + const response = await request(`${GET_ROAD_ITEM}/${selectedId}`, "get", { + signal: controller.signal, + }); + setData(response.data.data); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchItem(); + + return () => { + controller.abort(); + }; + }, [selectedId]); + + return ( + <> + جزئیات فعالیت + + {loading ? : data && } + + + + + + ); +}; +export default DialogInfoItem; diff --git a/src/components/dashboard/roadItems/reports/Map/PointsOnMap/index.jsx b/src/components/dashboard/roadItems/reports/Map/PointsOnMap/index.jsx new file mode 100644 index 0000000..8599f3f --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/PointsOnMap/index.jsx @@ -0,0 +1,71 @@ +import { Dialog, useTheme } from "@mui/material"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { CircleMarker, useMap } from "react-leaflet"; +import MarkerClusterGroup from "react-leaflet-markercluster"; +import DialogInfoItem from "./DialogInfoItem"; + +const PointsOnMap = ({ data, isCluster }) => { + const map = useMap(); + const theme = useTheme(); + const [selectedId, setSelectedId] = useState(null); + const [open, setOpen] = useState(false); + + const openDialog = useCallback(() => setOpen(true), []); + const closeDialog = useCallback(() => setOpen(false), []); + + const getMarkerColor = (status) => { + if (status === 0) return theme.palette.info.main; + if (status === 1) return theme.palette.success.main; + return theme.palette.warning.main; + }; + + const markers = useMemo(() => { + return data.map(({ i, l, g, status }) => ( + { + openDialog(); + setSelectedId(i); + }, + }} + key={i} + center={L.latLng(l, g)} + radius={8} + color={getMarkerColor(status)} + /> + )); + }, [data, theme]); + + useEffect(() => { + if (data.length === 0) return; + const bounds = L.latLngBounds(data.map(({ l, g }) => L.latLng(l, g))); + map.flyToBounds(bounds, { duration: 0.7 }); + }, [data]); + + const createClusterCustomIcon = (cluster) => { + const count = cluster.getChildCount(); + return L.divIcon({ + html: `
${count}
`, + className: "custom-marker-cluster", + iconSize: L.point(40, 40, true), + }); + }; + + if (data.length === 0) return null; + + return ( + <> + {isCluster ? ( + + {markers} + + ) : ( + markers + )} + + {open && } + + + ); +}; +export default PointsOnMap; diff --git a/src/components/dashboard/roadItems/reports/Map/index.jsx b/src/components/dashboard/roadItems/reports/Map/index.jsx new file mode 100644 index 0000000..1daacfb --- /dev/null +++ b/src/components/dashboard/roadItems/reports/Map/index.jsx @@ -0,0 +1,127 @@ +"use client"; +import LoadingHardPage from "@/core/components/LoadingHardPage"; +import MapLoading from "@/core/components/MapLayer/Loading"; +import { GET_ROAD_ITEMS_REPORT_MAP } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { Box, Stack } from "@mui/material"; +import dynamic from "next/dynamic"; +import { useCallback, useEffect, useState } from "react"; +import Filter from "./Filter"; +import PointsOnMap from "./PointsOnMap"; +import moment from "jalali-moment"; +import ClusterSwitch from "./ClusterSwitch"; +import Legend from "./Legend"; +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const defaultValues = { + province_id: "", + edarat_id: "", + item_id: "", + sub_item_id: "", + status: "", + activity_date_time: [moment().locale("en").format("YYYY-MM-DD"), moment().locale("en").format("YYYY-MM-DD")], +}; + +const MAX_POINTS = 1000; + +const containerStyles = { + width: "100%", + height: "100%", + p: 1, + border: "1px dashed", + borderColor: "divider", + borderRadius: 1, +}; + +const controlBarStyles = { + p: 1, + background: "#fff", +}; + +const RoadItemsReportMap = () => { + const requestServer = useRequest(); + const [filterData, setFilterData] = useState(defaultValues); + const [isCluster, setCluster] = useState(false); + const [data, setData] = useState([]); + const [isLoading, setLoading] = useState(true); + + const buildQueryParams = useCallback(() => { + const params = new URLSearchParams(); + const { province_id, edarat_id, item_id, sub_item_id, activity_date_time, status } = filterData; + + if (province_id) params.set("province_id", province_id); + if (edarat_id) params.set("edarat_id", edarat_id); + if (item_id) params.set("item_id", item_id); + if (sub_item_id) params.set("sub_item_id", sub_item_id); + if (activity_date_time[0]) { + params.set("date_from", activity_date_time[0]); + params.set("date_to", activity_date_time[1]); + } + if (status) params.set("status", status); + + return params.toString(); + }, [filterData]); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const query = buildQueryParams(); + const response = await requestServer(`${GET_ROAD_ITEMS_REPORT_MAP}?${query}`); + const fetchedData = response?.data?.data || []; + + if (fetchedData.length > MAX_POINTS) { + setCluster(true); + } + setData(fetchedData); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [filterData, buildQueryParams]); + + return ( + + + + + MAX_POINTS} + max={MAX_POINTS} + /> + + + + + + + + + + + {isLoading && ( + + + + )} + + + + + ); +}; +export default RoadItemsReportMap; diff --git a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx index 84a48c9..f9a7d78 100644 --- a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx @@ -404,7 +404,7 @@ const SupervisorList = () => { }, { accessorKey: "status", - header: "وضعیت", // Status + header: "وضعیت نظارت", id: "status", enableColumnFilter: true, datatype: "numeric", diff --git a/src/core/components/DataTable/filter/FilterBody.jsx b/src/core/components/DataTable/filter/FilterBody.jsx index ab12820..461eb01 100644 --- a/src/core/components/DataTable/filter/FilterBody.jsx +++ b/src/core/components/DataTable/filter/FilterBody.jsx @@ -1,32 +1,15 @@ "use client"; -import FilterBodyField from "@/core/components/DataTable/filter/FilterBodyField"; +import FilterBodyControllerWithDependency from "@/core/components/DataTable/filter/FilterBodyControllerWithDependency"; import FilterHeader from "@/core/components/DataTable/filter/FilterHeader"; import useDataTable from "@/lib/hooks/useDataTable"; import { yupResolver } from "@hookform/resolvers/yup"; -import { Box, Button, Drawer, styled } from "@mui/material"; +import { Box, Button } from "@mui/material"; import { useEffect, useState } from "react"; -import { Controller, useForm, useWatch } from "react-hook-form"; +import { useForm } from "react-hook-form"; import * as Yup from "yup"; +import ScrollBox from "../../ScrollBox"; import FilterBodyController from "./FilterBodyController"; -import FilterBodyControllerWithDependency from "@/core/components/DataTable/filter/FilterBodyControllerWithDependency"; - -const ScrollBox = styled(Box)({ - flexGrow: 1, - overflowY: "scroll", - maxWidth: "450px", - "::-webkit-scrollbar": { - width: "5px", - }, - "::-webkit-scrollbar-track": { - boxShadow: "inset 0 0 5px #fff", - borderRadius: "5px", - }, - "::-webkit-scrollbar-thumb": { - background: "#155175", - borderRadius: "0px", - }, -}); function FilterBody({ columns, drawerState, setDrawerState }) { const { filterData, setFilterData } = useDataTable(); @@ -89,7 +72,7 @@ function FilterBody({ columns, drawerState, setDrawerState }) { {Object.keys(filterData).length > 0 && (
- + {columns.map((column) => column.enableColumnFilter ? ( column.dependencyId ? ( diff --git a/src/core/components/DataTable/filter/index.jsx b/src/core/components/DataTable/filter/index.jsx index b922bc4..5885cb4 100644 --- a/src/core/components/DataTable/filter/index.jsx +++ b/src/core/components/DataTable/filter/index.jsx @@ -2,7 +2,7 @@ import { useState } from "react"; import FilterButton from "@/core/components/DataTable/filter/FilterButton"; import FilterBody from "@/core/components/DataTable/filter/FilterBody"; -import { Drawer } from "@mui/material"; +import { Box, Drawer } from "@mui/material"; function FilterColumn({ columns, user_id, page_name, table_name }) { const [open, setOpen] = useState(false); @@ -15,14 +15,18 @@ function FilterColumn({ columns, user_id, page_name, table_name }) { onClose={() => setOpen(false)} sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%", zIndex: "1300" }} > - + + {open && ( + + )} + ); diff --git a/src/core/components/LoadingHardPage.jsx b/src/core/components/LoadingHardPage.jsx index 89e5f17..e0ff917 100644 --- a/src/core/components/LoadingHardPage.jsx +++ b/src/core/components/LoadingHardPage.jsx @@ -6,15 +6,12 @@ import SvgError from "@/core/components/svgs/SvgError"; const LoadingImage = styled(Box)({ "@keyframes load": { "0%": { - // opacity: 0, transform: "scale(1)", }, "50%": { - // opacity: 1, transform: "scale(.5)", }, "100%": { - // opacity: 0, transform: "scale(1)", }, }, diff --git a/src/core/components/MapInfoOneMarker.jsx b/src/core/components/MapInfoOneMarker.jsx index b42ced9..b9574c1 100644 --- a/src/core/components/MapInfoOneMarker.jsx +++ b/src/core/components/MapInfoOneMarker.jsx @@ -38,9 +38,9 @@ const createCustomIcon = (size, iconUrl, labelText, color) => { const MAX_ZOOM_FOR_MARKER = 13; const MapInteraction = ({ setValue, startLat, startLng }) => { - const [isMarkerLocked, setIsMarkerLocked] = useState(!!(startLat && startLng)); // وضعیت قفل مارکر + const [isMarkerLocked, setIsMarkerLocked] = useState(!!(startLat && startLng)); const [enableSend, setEnableSend] = useState(false); - const [startIconColor, setStartIconColor] = useState(startLat ? "#1CAC66" : "#003d4f"); // رنگ آیکن شروع + const [startIconColor, setStartIconColor] = useState(startLat ? "#1CAC66" : "#003d4f"); const [markerPosition, setMarkerPosition] = useState( startLat && startLng ? { lat: startLat, lng: startLng } : null ); diff --git a/src/core/components/MapInfoTwoMarker.jsx b/src/core/components/MapInfoTwoMarker.jsx index 44745b1..9b114d3 100644 --- a/src/core/components/MapInfoTwoMarker.jsx +++ b/src/core/components/MapInfoTwoMarker.jsx @@ -37,13 +37,13 @@ const createCustomIcon = (size, iconUrl, labelText, color) => { const MAX_ZOOM_FOR_MARKER = 13; const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => { - const [isStartLocked, setIsStartLocked] = useState(!!(startLat && startLng)); // وضعیت قفل نقطه آغاز - const [isEndLocked, setIsEndLocked] = useState(!!(endLat && endLng)); // وضعیت قفل نقطه پایان + const [isStartLocked, setIsStartLocked] = useState(!!(startLat && startLng)); + const [isEndLocked, setIsEndLocked] = useState(!!(endLat && endLng)); const [startPosition, setStartPosition] = useState(startLat && startLng ? { lat: startLat, lng: startLng } : null); const [endPosition, setEndPosition] = useState(endLat && endLng ? { lat: endLat, lng: endLng } : null); const [enableSend, setEnableSend] = useState(false); - const [startIconColor, setStartIconColor] = useState(startLat ? "#1CAC66" : "#003d4f"); // رنگ آیکن شروع - const [endIconColor, setEndIconColor] = useState(endLat ? "#D13131" : "#003d4f"); // رنگ آیکن پایان + const [startIconColor, setStartIconColor] = useState(startLat ? "#1CAC66" : "#003d4f"); + const [endIconColor, setEndIconColor] = useState(endLat ? "#D13131" : "#003d4f"); const startRef = useRef(); const endRef = useRef(); diff --git a/src/core/components/ScrollBox.jsx b/src/core/components/ScrollBox.jsx new file mode 100644 index 0000000..11f8404 --- /dev/null +++ b/src/core/components/ScrollBox.jsx @@ -0,0 +1,19 @@ +import { Box, styled } from "@mui/material"; + +const ScrollBox = styled(Box)({ + flexGrow: 1, + overflowY: "auto", + maxWidth: "450px", + "::-webkit-scrollbar": { + width: "5px", + }, + "::-webkit-scrollbar-track": { + boxShadow: "inset 0 0 5px #fff", + borderRadius: "5px", + }, + "::-webkit-scrollbar-thumb": { + background: "#155175", + borderRadius: "0px", + }, +}); +export default ScrollBox; diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index f312523..fa593bb 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -137,7 +137,7 @@ export const pageMenu = [ id: "roadItemManagmentMap", label: "پراکندگی بر روی نقشه", type: "page", - route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=road_items", + route: "/dashboard/road-items/reports/map", icon: , permissions: [ "show-road-item-supervise-cartable", diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 2e5a8f8..94ce0d9 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -69,6 +69,8 @@ export const RESTORE_BY_SUPERVISOR = api + "/api/v3/road_items/restore"; export const GET_CAR_LIST_SEARCH = api + "/api/v3/cmms_machines/search"; export const GET_RAHDARANS_LIST_SEARCH = api + "/api/v3/rahdaran/search"; export const GET_OBSERVED_GASHT_LIST = api + "/api/v3/observed_items/filter"; +export const GET_ROAD_ITEMS_REPORT_MAP = api + "/api/v3/road_item_reports/activities_on_map"; +export const GET_ROAD_ITEM = api + "/api/v3/road_item_reports/show_on_map"; export const GET_PROVINCE_ACTIVITY_PER_ITEM = api + "/api/v3/road_item_reports/country_activity_per_item"; export const GET_CITY_ACTIVITY_PER_ITEM = api + "/api/v3/road_item_reports/province_activity_per_item"; export const GET_PROVINCE_ACTIVITY_PER_SUB_ITEM = api + "/api/v3/road_item_reports/country_activity_per_sub_item"; diff --git a/src/lib/hooks/useRoadItemGetISubtems.js b/src/lib/hooks/useRoadItemGetISubtems.js index b0e3264..368f72d 100644 --- a/src/lib/hooks/useRoadItemGetISubtems.js +++ b/src/lib/hooks/useRoadItemGetISubtems.js @@ -30,7 +30,7 @@ function reducer(state, action) { } } -const useRoadItemGetItems = (id) => { +const useRoadItemGetSubItems = (id) => { const requestServer = useRequest({ notificationShow: false }); const [state, dispatch] = useReducer(reducer, initialState); useEffect(() => { @@ -59,4 +59,4 @@ const useRoadItemGetItems = (id) => { }; }; -export default useRoadItemGetItems; +export default useRoadItemGetSubItems; From 2639ef3e1b1c7d3f7db65da129906dedd6f32a6f Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 15 Feb 2025 11:30:26 +0000 Subject: [PATCH 03/32] add observed item in road potrol --- .../Create/Forms/CreateFormContent.jsx | 1 - .../Forms/CreatePatrol/ActionInfo.jsx | 8 +- .../Forms/CreatePatrol/ActionsDurigPatrol.jsx | 54 +++-- .../Forms/CreatePatrol/CompeleteRequest.jsx | 41 ++-- .../CreateFormContentObservedItem.jsx | 203 ++++++++++++++++++ .../CreatePatrol/GetObservedItemInfo.jsx | 55 +++++ .../CreatePatrol/ModalActionsDuringPatrol.jsx | 2 +- .../CreatePatrol/ModalObservedItemPatrol.jsx | 16 ++ .../Forms/CreatePatrol/ObservedInfo.jsx | 37 ++++ .../PreviousStatesInfoInObservedItem.jsx | 43 ++++ 10 files changed, 431 insertions(+), 29 deletions(-) create mode 100644 src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx create mode 100644 src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/GetObservedItemInfo.jsx create mode 100644 src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalObservedItemPatrol.jsx create mode 100644 src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ObservedInfo.jsx create mode 100644 src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PreviousStatesInfoInObservedItem.jsx diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx index cfe6c09..62bc6a8 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx @@ -249,7 +249,6 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => { diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionInfo.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionInfo.jsx index ae935b3..8ad27a7 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionInfo.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionInfo.jsx @@ -2,7 +2,7 @@ import DeleteIcon from "@mui/icons-material/Delete"; import HandymanIcon from "@mui/icons-material/Handyman"; -import { Box, Card, CardActions, CardHeader, IconButton, Typography } from "@mui/material"; +import { Box, Card, CardActions, CardHeader, Chip, IconButton, Typography } from "@mui/material"; import EditActionDuringPatrol from "./EditActionDuringPatrol"; const ActionInfo = ({ action, setActionsList, index, deleteAction }) => { @@ -17,7 +17,11 @@ const ActionInfo = ({ action, setActionsList, index, deleteAction }) => { > } - title={{action.data.item_name}} + title={ + + {action.data.item_name} + + } subheader={action.data.sub_item_name} /> diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx index 304391a..570c78f 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx @@ -6,12 +6,20 @@ import { Box, Button, Chip, DialogActions, DialogContent, Divider, Grid, Stack, import { useState } from "react"; import ActionInfo from "./ActionInfo"; import ModalActionsDuringPatrol from "./ModalActionsDuringPatrol"; +import ModalObservedItemPatrol from "./ModalObservedItemPatrol"; +import ObservedInfo from "./ObservedInfo"; const ActionsDuringPatrol = ({ allData, setAllData, handlePrev, setTabState }) => { const [actionsList, setActionsList] = useState(allData.observed_items); - const [open, setOpen] = useState(false); - const handleOpen = () => { - setOpen(true); + const [openAction, setOpenAction] = useState(false); + const [openObserved, setOpenObseved] = useState(false); + const handleOpenAction = () => { + setOpenAction(true); + }; + + + const handleOpenObserved = () => { + setOpenObseved(true); }; const deleteAction = (indexToRemove) => { @@ -28,17 +36,30 @@ const ActionsDuringPatrol = ({ allData, setAllData, handlePrev, setTabState }) = - - + + + @@ -56,12 +77,21 @@ const ActionsDuringPatrol = ({ allData, setAllData, handlePrev, setTabState }) = > {actionsList.map((action, index) => ( - deleteAction(index)} - /> + {action.instant_action == 1 ? ( + deleteAction(index)} + /> + ) : ( + deleteAction(index)} + /> + )} ))} diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx index 40f59da..0fa5919 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx @@ -109,19 +109,34 @@ const CompeleteRequest = ({ allData, handlePrev, mutate, setOpen }) => { formData.append(`stop_points[${index}][startDT]`, stop_point.startDT); }); allData.observed_items.forEach((observed_item, index) => { - formData.append(`observed_items[${index}][instant_action]`, 1); - formData.append(`observed_items[${index}][local_name]`, ""); - formData.append(`observed_items[${index}][start_point]`, observed_item.result.start_point); - formData.append(`observed_items[${index}][end_point]`, observed_item.result.end_point); - formData.append(`observed_items[${index}][amount]`, observed_item.result.amount); - formData.append(`observed_items[${index}][before_image]`, observed_item.result.before_image); - formData.append(`observed_items[${index}][after_image]`, observed_item.result.after_image); - formData.append(`observed_items[${index}][item_id]`, observed_item.result.item_id); - formData.append(`observed_items[${index}][sub_item_id]`, observed_item.result.sub_item_id); - formData.append(`observed_items[${index}][road_item_machines_id][]`, allData.roadPatrolMachinesId.id); - allData.road_patrol_rahdaran_id.forEach((rahdar, i) => - formData.append(`observed_items[${index}][road_item_rahdaran_id][]`, rahdar.id) - ); + if (observed_item.instant_action == 1) { + formData.append(`observed_items[${index}][instant_action]`, observed_item.instant_action); + formData.append(`observed_items[${index}][local_name]`, ""); + formData.append(`observed_items[${index}][start_point]`, observed_item.result.start_point); + if (observed_item.result.end_point) + formData.append(`observed_items[${index}][end_point]`, observed_item.result.end_point); + formData.append(`observed_items[${index}][amount]`, observed_item.result.amount); + formData.append(`observed_items[${index}][before_image]`, observed_item.result.before_image); + formData.append(`observed_items[${index}][after_image]`, observed_item.result.after_image); + formData.append(`observed_items[${index}][item_id]`, observed_item.result.item_id); + formData.append(`observed_items[${index}][sub_item_id]`, observed_item.result.sub_item_id); + formData.append( + `observed_items[${index}][road_item_machines_id][]`, + allData.roadPatrolMachinesId.id + ); + allData.road_patrol_rahdaran_id.forEach((rahdar, i) => + formData.append(`observed_items[${index}][road_item_rahdaran_id][]`, rahdar.id) + ); + } else { + formData.append(`observed_items[${index}][instant_action]`, observed_item.instant_action); + formData.append(`observed_items[${index}][local_name]`, observed_item.result.local_name); + formData.append(`observed_items[${index}][priority]`, observed_item.result.priority); + formData.append(`observed_items[${index}][start_point]`, observed_item.result.start_point); + if (observed_item.result.end_point) + formData.append(`observed_items[${index}][end_point]`, observed_item.result.end_point); + formData.append(`observed_items[${index}][item_id]`, observed_item.result.item_id); + formData.append(`observed_items[${index}][sub_item_id]`, observed_item.result.sub_item_id); + } }); formData.append("start_time", allData.start_time); formData.append("end_time", allData.end_time); diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx new file mode 100644 index 0000000..63b0ec9 --- /dev/null +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx @@ -0,0 +1,203 @@ +import GetItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm"; +import GetSubItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm"; +import StyledForm from "@/core/components/StyledForm"; +import { useTheme } from "@emotion/react"; +import { yupResolver } from "@hookform/resolvers/yup"; +import BeenhereIcon from "@mui/icons-material/Beenhere"; +import ExitToAppIcon from "@mui/icons-material/ExitToApp"; +import FileCopyIcon from "@mui/icons-material/FileCopy"; +import InfoIcon from "@mui/icons-material/Info"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; +import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; +import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { mixed, number, object, string } from "yup"; +import GetObservedItemInfo from "./GetObservedItemInfo"; + +function TabPanel(props) { + const { children, value, index } = props; + return ( + + ); +} + +const validationSchema = object({ + item_id: number().required("نوع آیتم را مشخص کنید!"), + sub_item_id: number().required("اقدام انجام شده را مشخص کنید!"), + local_name: string().required("نام محلی را مشخص کنید!"), + priority: string().required("اولویت را مشخص کنید!"), + start_point: mixed() + .test("start-point-required", "لطفاً نقطه شروع را مشخص کنید!", function (value) { + return !!value; // چک می‌کند که مقدار موجود است + }) + .required("لطفاً نقطه شروع را مشخص کنید!"), + end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) { + const { subItemsList } = this.options.context; + const needsEndPoint = subItemsList?.needs_end_point === 1; + if (needsEndPoint) { + return !!value; // چک می‌کند که مقدار موجود است + } + return true; + }), +}); + +const CreateFormContentObservedItem = ({ setOpen, onSubmit }) => { + const defaultValues = { + item_id: null, + sub_item_id: null, + local_name: "", + priority: "1", + start_point: "", + end_point: "", + }; + + const [tabState, setTabState] = useState(0); + const [itemsList, setItemsList] = useState(); + const [subItemsList, setSubItemsList] = useState([]); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + + const handleClose = () => { + setOpen(false); + }; + + const handleChangeTab = (event, newValue) => { + setTabState(newValue); + }; + + const handlePrev = () => { + if (tabState === 2) { + const fieldsToReset = ["local_name", "priority", "start_point", "end_point"]; + fieldsToReset.forEach((field) => resetField(field)); + } + if (tabState === 0) { + handleClose(); + } else { + setTabState(tabState - 1); + } + }; + const { + control, + watch, + getValues, + register, + handleSubmit, + setValue, + resetField, + formState: { errors, isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onBlur", + context: { subItemsList }, + }); + const onSubmitBase = async (data) => { + let result = { ...data }; + + if (result.end_point === "") { + delete result.end_point; + delete data.end_point; + } else { + result.end_point = `${result.end_point.lat},${result.end_point.lng}`; + } + + if (result.start_point === "") { + delete result.start_point; + delete data.start_point; + } else { + result.start_point = `${result.start_point.lat},${result.start_point.lng}`; + } + + await onSubmit({ + result, + data: { + ...data, + item_name: itemsList.name, + sub_item_name: subItemsList.name, + unit_fa: subItemsList.unit, + }, + }); + }; + + return ( + + + } label="انتخاب آیتم" /> + } label="انتخاب اقدام مشاهده شده" /> + } label="اطلاعات فعالیت" /> + + + + + + + + + + + + + + + + + {tabState === 2 && ( + + )} + + + ); +}; +export default CreateFormContentObservedItem; diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/GetObservedItemInfo.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/GetObservedItemInfo.jsx new file mode 100644 index 0000000..63a1953 --- /dev/null +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/GetObservedItemInfo.jsx @@ -0,0 +1,55 @@ +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select, Stack, TextField } from "@mui/material"; +import PreviousStatesInfoInObservedItem from "./PreviousStatesInfoInObservedItem"; + +const GetObservedItemInfo = ({ register, itemsList, subItemsList, control, setValue, errors, watch, getValues }) => { + return ( + + + + + + + + + + + اولویت + + + + + + {subItemsList?.needs_end_point === 1 ? ( + + ) : ( + + )} + + + ); +}; +export default GetObservedItemInfo; diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx index 073fd9e..97be471 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalActionsDuringPatrol.jsx @@ -3,7 +3,7 @@ import { Dialog } from "@mui/material"; const ModalActionsDuringPatrol = ({ open, setOpen, setActionsList }) => { const HandleSubmit = async (data) => { - setActionsList((prev) => [...prev, data]); + setActionsList((prev) => [...prev, { ...data, instant_action: 1 }]); setOpen(false); }; diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalObservedItemPatrol.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalObservedItemPatrol.jsx new file mode 100644 index 0000000..9a6c0fc --- /dev/null +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ModalObservedItemPatrol.jsx @@ -0,0 +1,16 @@ +import { Dialog } from "@mui/material"; +import CreateFormContentObservedItem from "./CreateFormContentObservedItem"; + +const ModalObservedItemPatrol = ({ open, setOpen, setActionsList }) => { + const HandleSubmit = async (data) => { + setActionsList((prev) => [...prev, { ...data, instant_action: 0 }]); + setOpen(false); + }; + + return ( + + + + ); +}; +export default ModalObservedItemPatrol; diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ObservedInfo.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ObservedInfo.jsx new file mode 100644 index 0000000..16b9953 --- /dev/null +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ObservedInfo.jsx @@ -0,0 +1,37 @@ +"use client"; + +import DeleteIcon from "@mui/icons-material/Delete"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import { Box, Card, CardActions, CardHeader, Chip, IconButton, Typography } from "@mui/material"; + +const ObservedInfo = ({ action, deleteAction }) => { + return ( + + } + title={ + + {action.data.item_name} + + } + subheader={action.data.sub_item_name} + /> + + + + + + + + + ); +}; + +export default ObservedInfo; diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PreviousStatesInfoInObservedItem.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PreviousStatesInfoInObservedItem.jsx new file mode 100644 index 0000000..7476631 --- /dev/null +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PreviousStatesInfoInObservedItem.jsx @@ -0,0 +1,43 @@ +import { Box, Chip, Divider, Grid, Typography } from "@mui/material"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; +import FileCopyIcon from "@mui/icons-material/FileCopy"; + +const PreviousStatesInfoInObservedItem = ({ itemsList, subItemsList }) => { + return ( + + + + } + label={ + + آیتم انتخاب ‌شده + + } + /> + + + + {itemsList.name || "هیچ آیتمی انتخاب نشده است"} + + + + + } + label={ + + اقدام مشاهده شده + + } + /> + + + + {subItemsList?.name || "هیچ اقدامی انتخاب نشده است"} + + + + ); +}; +export default PreviousStatesInfoInObservedItem; From 32adc047c8b92d2db88f62e6d98049957132d2c9 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Mon, 17 Feb 2025 07:45:56 +0000 Subject: [PATCH 04/32] Resolve #86c24uh2g "Bugfix/bug modal machine performance " --- .../operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx | 1 - .../operator/RowActions/MachinePerformanceForm/index.jsx | 8 ++++++-- .../RowActions/MachinePerformanceForm/index.jsx | 8 ++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx index 570c78f..8028103 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/ActionsDurigPatrol.jsx @@ -17,7 +17,6 @@ const ActionsDuringPatrol = ({ allData, setAllData, handlePrev, setTabState }) = setOpenAction(true); }; - const handleOpenObserved = () => { setOpenObseved(true); }; diff --git a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx index 7e2902f..8269166 100644 --- a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx +++ b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx @@ -1,4 +1,4 @@ -import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip, Typography } from "@mui/material"; import DirectionsCarIcon from "@mui/icons-material/DirectionsCar"; import MachinePerformanceContent from "./MachinePerformanceContent"; import { useState } from "react"; @@ -33,7 +33,11 @@ const MachinePerformanceForm = ({ row }) => { maxWidth={"md"} > عملکرد خودرو - + {row.original?.stop_points ? ( + + ) : ( + عملکردی در سامانه ثبت نشده است. + )} + + + + ); +}; +export default CreateFormContent; diff --git a/src/components/dashboard/receipt/damages/Actions/Create/Form/index.jsx b/src/components/dashboard/receipt/damages/Actions/Create/Form/index.jsx new file mode 100644 index 0000000..91aa0fb --- /dev/null +++ b/src/components/dashboard/receipt/damages/Actions/Create/Form/index.jsx @@ -0,0 +1,11 @@ +import { Dialog } from "@mui/material"; +import CreateFormContent from "./CreateFormContent"; + +const CreateForm = ({ open, setOpen, mutate }) => { + return ( + + {open && } + + ); +}; +export default CreateForm; diff --git a/src/components/dashboard/receipt/damages/Actions/Create/index.jsx b/src/components/dashboard/receipt/damages/Actions/Create/index.jsx new file mode 100644 index 0000000..a720b22 --- /dev/null +++ b/src/components/dashboard/receipt/damages/Actions/Create/index.jsx @@ -0,0 +1,36 @@ +import { AddCircle } from "@mui/icons-material"; +import { Button, IconButton, useMediaQuery, useTheme } from "@mui/material"; +import CreateForm from "./Form"; +import { useState } from "react"; + +const CreateDamage = ({ mutate }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + + ); +}; +export default CreateDamage; diff --git a/src/components/dashboard/receipt/damages/DamagesList.jsx b/src/components/dashboard/receipt/damages/DamagesList.jsx new file mode 100644 index 0000000..e93d216 --- /dev/null +++ b/src/components/dashboard/receipt/damages/DamagesList.jsx @@ -0,0 +1,84 @@ +"use client"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { GET_RECEIPT_DAMAGE_ITEMS_LIST } from "@/core/utils/routes"; +import { Box } from "@mui/material"; +import { useMemo } from "react"; +import Toolbar from "./Toolbar"; +import RowActions from "./RowActions"; + +const DamagesList = () => { + const columns = useMemo( + () => [ + { + accessorKey: "id", + header: "کد یکتا", + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "title", + header: "عنوان", + id: "title", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "unit", + header: "واحد", + id: "unit", + enableColumnFilter: false, + grow: false, + size: 100, + }, + { + accessorKey: "base_price", + header: "فی (ریال)", + id: "base_price", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ renderedCellValue }) => (renderedCellValue / 1).toLocaleString(), + }, + { + accessorKey: "status", + header: "وضعیت", + id: "status", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ renderedCellValue }) => (renderedCellValue == 1 ? "فعال" : "غیرفعال"), + }, + ], + [] + ); + + return ( + <> + + + + + ); +}; +export default DamagesList; diff --git a/src/components/dashboard/receipt/damages/RowActions/Activity/index.jsx b/src/components/dashboard/receipt/damages/RowActions/Activity/index.jsx new file mode 100644 index 0000000..4f64584 --- /dev/null +++ b/src/components/dashboard/receipt/damages/RowActions/Activity/index.jsx @@ -0,0 +1,31 @@ +import { ATIVITY_RECEIPT_DAMAGE_ITEMS } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { Switch } from "@mui/material"; +import { useState } from "react"; + +const Activity = ({ rowId, mutate, status }) => { + const [disabled, setDisabled] = useState(false) + const [checked, setChecked] = useState(status == 1); + const requestServer = useRequest(); + + const handleSwitch = (event) => { + const newChecked = event.target.checked; + setChecked(newChecked); + setDisabled(true) + + requestServer(`${ATIVITY_RECEIPT_DAMAGE_ITEMS}/${rowId}`, "post") + .then(() => { + mutate(); + }) + .catch(() => { + setChecked(!newChecked); + }) + .finally(() => { + setDisabled(false) + }) + }; + + return ; +}; + +export default Activity; diff --git a/src/components/dashboard/receipt/damages/RowActions/Edit/Form/index.jsx b/src/components/dashboard/receipt/damages/RowActions/Edit/Form/index.jsx new file mode 100644 index 0000000..30965fe --- /dev/null +++ b/src/components/dashboard/receipt/damages/RowActions/Edit/Form/index.jsx @@ -0,0 +1,145 @@ +import NumberField from "@/core/components/NumberField"; +import StyledForm from "@/core/components/StyledForm"; +import { UPDATE_RECEIPT_DAMAGE_ITEMS } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Beenhere, ExitToApp } from "@mui/icons-material"; +import { Button, DialogActions, DialogContent, DialogTitle, Stack, TextField } from "@mui/material"; +import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog"; +import { Controller, useForm } from "react-hook-form"; +import { object, string } from "yup"; + +const validationSchema = object({ + title: string().required("وارد کردن عنوان الزامیست!"), + unit: string().required("وارد کردن واحد الزامیست!"), + base_price: string().required("وارد کردن فی الزامیست!"), +}); + +const EditFormContent = ({ row, rowId, mutate, setOpen }) => { + const requestServer = useRequest({ notificationSuccess: true }); + const defaultValues = { + title: row.original.title, + unit: row.original.unit, + base_price: row.original.base_price, + }; + + const { + control, + handleSubmit, + formState: { isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onBlur", + }); + + const onSubmit = async (data) => { + const formData = new FormData(); + formData.append("title", data.title); + formData.append("unit", data.unit); + formData.append("base_price", data.base_price); + + try { + await requestServer(`${UPDATE_RECEIPT_DAMAGE_ITEMS}/${rowId}`, "post", { + data: formData, + }); + mutate(); + setOpen(false); + } catch (error) {} + }; + + return ( + <> + + ویرایش آیتم + + + + + ( + + )} + /> + ( + + )} + /> + ( + { + if (!isNaN(event.target.value)) { + onChange(event.target.value); + } else { + onChange(value); + } + }} + helperText={error ? error.message : null} + variant="outlined" + fullWidth + /> + )} + /> + + + + + + + + + ); +}; +export default EditFormContent; diff --git a/src/components/dashboard/receipt/damages/RowActions/Edit/index.jsx b/src/components/dashboard/receipt/damages/RowActions/Edit/index.jsx new file mode 100644 index 0000000..5fad290 --- /dev/null +++ b/src/components/dashboard/receipt/damages/RowActions/Edit/index.jsx @@ -0,0 +1,30 @@ +import BorderColorIcon from "@mui/icons-material/BorderColor"; +import { Dialog, IconButton, Tooltip } from "@mui/material"; +import EditFormContent from "./Form"; +import { useState } from "react"; + +const Edit = ({ row, mutate, rowId }) => { + const [openEditDialog, setOpenEditDialog] = useState(false); + + return ( + <> + + { + setOpenEditDialog(true); + }} + > + + + + + {openEditDialog && ( + + )} + + + ); +}; +export default Edit; diff --git a/src/components/dashboard/receipt/damages/RowActions/index.jsx b/src/components/dashboard/receipt/damages/RowActions/index.jsx new file mode 100644 index 0000000..18aa6ea --- /dev/null +++ b/src/components/dashboard/receipt/damages/RowActions/index.jsx @@ -0,0 +1,13 @@ +import { Box } from "@mui/material"; +import Activity from "./Activity"; +import Edit from "./Edit"; + +const RowActions = ({ row, mutate }) => { + return ( + + + + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/receipt/damages/Toolbar.jsx b/src/components/dashboard/receipt/damages/Toolbar.jsx new file mode 100644 index 0000000..b639318 --- /dev/null +++ b/src/components/dashboard/receipt/damages/Toolbar.jsx @@ -0,0 +1,10 @@ +import CreateDamage from "./Actions/Create"; + +const Toolbar = ({ mutate }) => { + return ( + <> + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/receipt/damages/index.jsx b/src/components/dashboard/receipt/damages/index.jsx new file mode 100644 index 0000000..ac37d7d --- /dev/null +++ b/src/components/dashboard/receipt/damages/index.jsx @@ -0,0 +1,13 @@ +import PageTitle from "@/core/components/PageTitle"; +import { Stack } from "@mui/material"; +import DamagesList from "./DamagesList"; + +const DamagesPage = () => { + return ( + + + + + ); +}; +export default DamagesPage; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx index c9921bf..0a215b5 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx @@ -4,7 +4,7 @@ import CreateFormContent from "@/components/dashboard/roadItems/operator/Actions import { CREATE_ROAD_ITEMS } from "@/core/utils/routes"; import useRequest from "@/lib/hooks/useRequest"; -const OperatorCreateForm = ({ open, setOpen, mutate, rowId }) => { +const OperatorCreateForm = ({ open, setOpen, mutate }) => { const requestServer = useRequest({ notificationSuccess: true }); const HandleSubmit = async ({ result }) => { const formData = new FormData(); diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx index c8c8375..596cf9a 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx @@ -1,8 +1,7 @@ -import React, { useState } from "react"; -import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; import DeleteContent from "./DeleteContent"; -import useRequest from "@/lib/hooks/useRequest"; const DeleteForm = ({ rowId, mutate }) => { const [openDeleteDialog, setOpenDeleteDialog] = useState(false); diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index fa593bb..6a778b3 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -473,6 +473,14 @@ export const pageMenu = [ }, ], }, + { + id: "damageItems", + label: "آیتم خسارات", + type: "page", + route: "/dashboard/receipt/damages", + icon: , + permissions: ["manage-damage"], + }, { id: "receiptManagmentReport", label: "گزارش ها", diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 94ce0d9..42e2f87 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -76,5 +76,11 @@ export const GET_CITY_ACTIVITY_PER_ITEM = api + "/api/v3/road_item_reports/provi export const GET_PROVINCE_ACTIVITY_PER_SUB_ITEM = api + "/api/v3/road_item_reports/country_activity_per_sub_item"; export const GET_CITY_ACTIVITY_PER_SUB_ITEM = api + "/api/v3/road_item_reports/province_activity_per_sub_item"; +// damage items +export const GET_RECEIPT_DAMAGE_ITEMS_LIST = api + "/api/v3/damages"; +export const CREATE_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages"; +export const UPDATE_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages"; +export const ATIVITY_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages/activate"; + // activity code log export const ACTIVITY_LOG = api + "/api/v3/profile/add_activity"; From 4ddc82b0ad60bcc9940b17c2d0afcf9b8a55b6d2 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Wed, 19 Feb 2025 07:25:45 +0000 Subject: [PATCH 07/32] Resolve #86c24uue0 "Feature/extract images cars roads api " --- .../roadItems/operator/OperatorList.jsx | 44 +++---- .../RowActions/EditForm/EditController.jsx | 118 +++++++++++++----- .../RowActions/EditForm/EditFormContent.jsx | 5 +- .../operator/RowActions/EditForm/index.jsx | 28 +++-- .../RowActions/ImageForm/ImagesContent.jsx | 43 +++++++ .../operator/RowActions/ImageForm/index.jsx | 15 +-- .../MachinesCodeForm/MachinesCodeContent.jsx | 58 ++++++--- .../RowActions/MachinesCodeForm/index.jsx | 4 +- .../RahdaranForm/RahdaranContent.jsx | 58 ++++++--- .../RowActions/RahdaranForm/index.jsx | 4 +- .../RowActions/ImageForm/ImagesContent.jsx | 43 +++++++ .../supervisor/RowActions/ImageForm/index.jsx | 15 +-- .../MachinesCodeForm/MachinesCodeContent.jsx | 58 ++++++--- .../RowActions/MachinesCodeForm/index.jsx | 4 +- .../RahdaranForm/RahdaranContent.jsx | 58 ++++++--- .../RowActions/RahdaranForm/index.jsx | 4 +- .../roadItems/supervisor/SupervisorList.jsx | 71 ++++------- .../roadPatrols/supervisor/SupervisorList.jsx | 2 +- src/core/utils/routes.js | 3 + 19 files changed, 419 insertions(+), 216 deletions(-) create mode 100644 src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImagesContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImagesContent.jsx diff --git a/src/components/dashboard/roadItems/operator/OperatorList.jsx b/src/components/dashboard/roadItems/operator/OperatorList.jsx index 33b37c6..793b1e6 100644 --- a/src/components/dashboard/roadItems/operator/OperatorList.jsx +++ b/src/components/dashboard/roadItems/operator/OperatorList.jsx @@ -156,9 +156,8 @@ const OperatorList = () => { }, }, { - accessorKey: "files", - header: "تصاویر", // Images - id: "files", + header: "تصاویر", + id: "images", enableColumnFilter: false, enableSorting: false, datatype: "array", @@ -173,23 +172,15 @@ const OperatorList = () => { }, }, }, - Cell: ({ renderedCellValue }) => { - if (renderedCellValue.length > 0) { - return ( - - - - ); - } + Cell: ({ renderedCellValue, row }) => { return ( - - بدون تصویر - + + + ); }, }, { - accessorKey: "location", header: "موقعیت", // Location id: "location", enableColumnFilter: false, @@ -220,8 +211,7 @@ const OperatorList = () => { }, }, { - accessorKey: "cmms_machines", - header: "خودرو ها", // Car ID + header: "خودرو ها", id: "cmmsMachines__machine_code", enableColumnFilter: true, datatype: "text", @@ -239,19 +229,15 @@ const OperatorList = () => { }, }, }, - Cell: ({ renderedCellValue }) => { - if (renderedCellValue) { - return ( - - - - ); - } - return خودرویی وجود ندارد; + Cell: ({ renderedCellValue, row }) => { + return ( + + + + ); }, }, { - accessorKey: "rahdaran", header: "راهداران", id: "rahdaran__code", enableColumnFilter: true, @@ -270,10 +256,10 @@ const OperatorList = () => { }, }, }, - Cell: ({ renderedCellValue }) => { + Cell: ({ renderedCellValue, row }) => { return ( - + ); }, diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx index 164fc70..bb235a1 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx @@ -1,19 +1,77 @@ -import { Dialog, DialogTitle } from "@mui/material"; +import { GET_CMMS_MACHINE_ROAD_ITEM, UPDATE_ROAD_ITEMS } from "@/core/utils/routes"; +import { Dialog, DialogTitle, Typography } from "@mui/material"; import EditFormContent from "./EditFormContent"; -import { UPDATE_ROAD_ITEMS } from "@/core/utils/routes"; import useRequest from "@/lib/hooks/useRequest"; +import { useEffect, useState } from "react"; +import DialogLoading from "@/core/components/DialogLoading"; const EditController = ({ rowId, mutate, setOpenEditDialog, openEditDialog, row }) => { - const requestServer = useRequest({ notificationSuccess: true }); + const requestServer = useRequest(); + const [loadingImages, setLoadingImages] = useState(true); + const [images, setImages] = useState(null); + const [loadingCmmsMachine, setLoadingCmmsMachine] = useState(true); + const [cmmsMachine, setCmmsMachine] = useState(null); + const [loadingRahdaran, setLoadingRahdaran] = useState(true); + const [rahdaran, setRahdaran] = useState(null); + + useEffect(() => { + const fetchData = async () => { + try { + setLoadingImages(true); + const response = await requestServer(`${GET_IMAGES_ROAD_ITEM}/${rowId}`); + setImages(response.data); + } catch (error) { + } finally { + setLoadingImages(false); + } + }; + + fetchData(); + }, [rowId]); + + useEffect(() => { + const fetchData = async () => { + try { + setLoadingCmmsMachine(true); + const response = await requestServer(`${GET_CMMS_MACHINE_ROAD_ITEM}/${rowId}`); + setCmmsMachine(response.data); + } catch (error) { + } finally { + setLoadingCmmsMachine(false); + } + }; + + fetchData(); + }, [rowId]); + + useEffect(() => { + const fetchData = async () => { + try { + setLoadingRahdaran(true); + const response = await requestServer(`${GET_RAHDARAN_ROAD_ITEM}/${rowId}`); + setRahdaran(response.data); + } catch (error) { + } finally { + setLoadingRahdaran(false); + } + }; + + fetchData(); + }, [rowId]); const HandleSubmit = async ({ result }) => { const formData = new FormData(); for (const [key, value] of Object.entries(result)) { formData.append(key, value); } - await requestServer(`${UPDATE_ROAD_ITEMS}/${rowId}`, "post", { - data: formData, - }) + await requestServer( + `${UPDATE_ROAD_ITEMS}/${rowId}`, + "post", + { + data: formData, + }, + { notificationSuccess: true } + ) .then((response) => { mutate(); setOpenEditDialog(false); @@ -21,34 +79,28 @@ const EditController = ({ rowId, mutate, setOpenEditDialog, openEditDialog, row .catch((error) => {}); }; - return ( - + ) : images && cmmsMachine && rahdaran ? ( + - ویرایش اطلاعات - - + /> + ) : ( + + تمامی اطلاعات یافت نشد! + ); }; export default EditController; diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx index d8b8dc3..c49d50d 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx @@ -1,7 +1,6 @@ -import { LinearProgress, Typography } from "@mui/material"; -import React from "react"; -import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems"; import EditFormCreate from "@/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate"; +import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems"; +import { LinearProgress, Typography } from "@mui/material"; const EditFormContent = ({ setOpenEditDialog, onSubmit, defaultData, is_gasht }) => { const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetISubtems(defaultData?.item_id); diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx index 9302918..2cc378d 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx @@ -19,13 +19,27 @@ const EditForm = ({ row, mutate, rowId }) => { - + + ویرایش اطلاعات + {openEditDialog && ( + + )} + ); }; diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImagesContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImagesContent.jsx new file mode 100644 index 0000000..6414e24 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImagesContent.jsx @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; +import ImageFormContent from "./ImageFormContent"; +import DialogLoading from "@/core/components/DialogLoading"; +import useRequest from "@/lib/hooks/useRequest"; +import { GET_IMAGES_ROAD_ITEM } from "@/core/utils/routes"; +import { Typography } from "@mui/material"; + +const ImagesContent = ({ rowId }) => { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + const request = useRequest(); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const response = await request(`${GET_IMAGES_ROAD_ITEM}/${rowId}`); + setData(response.data.data); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [rowId]); + + return ( + <> + {loading ? ( + + ) : data ? ( + <> + + + + ) : ( + تصویری در سامانه یافت نشد + )} + + ); +}; +export default ImagesContent; diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx index 130f443..08e9cd9 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx @@ -1,9 +1,9 @@ -import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Paper, Tooltip } from "@mui/material"; -import { useState } from "react"; import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; -import ImageFormContent from "./ImageFormContent"; +import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ImagesContent from "./ImagesContent"; -const ImageDialog = ({ images }) => { +const ImageDialog = ({ rowId }) => { const [openImageDialog, setOpenImageDialog] = useState(false); return ( @@ -26,12 +26,7 @@ const ImageDialog = ({ images }) => { scroll="paper" > تصاویر - - - - - - + {openImageDialog && } + {tabState !== 2 ? ( + + ) : ( + + )} + + + ); +}; +export default CreateFormContent; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageAmount.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageAmount.jsx new file mode 100644 index 0000000..b2c7b90 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageAmount.jsx @@ -0,0 +1,55 @@ +import { Controller, useWatch } from "react-hook-form"; +import { Grid } from "@mui/material"; +import NumberField from "@/core/components/NumberField"; +import { useEffect } from "react"; + +const DamageAmount = ({ control, damageItemList, setValue }) => { + const itemDamage = useWatch({ control, name: "items_damge_id" }); + const itemValue = useWatch({ control, name: "items_damge_value" }); + const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage); + const isDisabled = itemDamageId?.base_price ? itemDamageId?.base_price !== 0 : false; + const calculatedValue = itemValue * (itemDamageId?.base_price ? itemDamageId?.base_price : 0); + useEffect(() => { + if (isDisabled) { + setValue("items_damge_amount", calculatedValue); + } + }, [itemValue, itemDamageId, isDisabled, setValue]); + return ( + + { + return ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + if (!isDisabled) { + field.onChange(inputValue); + } + }} + label={`هزینه خسارت (ریال)`} + fullWidth + size="small" + disabled={isDisabled || itemDamage === null} + error={error} + helperText={error?.message} + /> + ); + }} + /> + + ); +}; +export default DamageAmount; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageInfo.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageInfo.jsx new file mode 100644 index 0000000..4b3457b --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageInfo.jsx @@ -0,0 +1,273 @@ +import { Controller } from "react-hook-form"; +import { Grid, Stack, TextField } from "@mui/material"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import MuiTimePicker from "@/core/components/MuiTimePicker"; +import SelectBox from "@/core/components/SelectBox"; +import useProvinces from "@/lib/hooks/useProvince"; +import PlateNumber from "@/core/components/PlateNumber"; +import ImageUpload from "./ImageUpload"; +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import CityController from "@/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CityController"; + +const DamageInfo = ({ control, setValue, errors }) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + + return ( + + + + { + return ( + field.onChange(value || [])} + helperText={error ? error.message : null} + /> + ); + }} + name={"accident_date"} + /> + + + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + name={"accident_time"} + /> + + + { + return ( + + ); + }} + /> + + + + + { + return ( + + ); + }} + /> + + + + + + ( + + )} + /> + + + + + ( + + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + field.onChange(inputValue); + }} + label="کد ملی" + placeholder={"کد ملی را وارد کنید"} + fullWidth + size="small" + error={error} + helperText={error?.message} + /> + )} + /> + + + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + field.onChange(inputValue); + }} + label="شماره تماس" + placeholder={"شماره تماس را وارد کنید"} + fullWidth + size="small" + error={error} + helperText={error?.message} + /> + )} + /> + + + + + + + + ( + + )} + /> + + + ( + + )} + /> + + + + + + + ); +}; +export default DamageInfo; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItem.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItem.jsx new file mode 100644 index 0000000..3a5de73 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItem.jsx @@ -0,0 +1,136 @@ +import { Autocomplete, Box, Button, Chip, Divider, Fade, Grid, Stack, TextField, Typography } from "@mui/material"; +import { Controller, useController, useForm } from "react-hook-form"; +import LinearProgress from "@mui/material/LinearProgress"; +import { useEffect, useState } from "react"; +import DamageItemInfo from "./DamageItemInfo"; +import { number, object } from "yup"; +import useDamageItemList from "@/lib/hooks/useDamageItemList"; +import DamagePrice from "./DamagePrice"; +import { yupResolver } from "@hookform/resolvers/yup"; + +const schema = object({ + items_damge_id: number().required("لطفا نوع خسارت را وارد کنید!!!"), + items_damge_value: number().required("لطفا میزان خسارت را وارد کنید!!!"), + items_damge_amount: number().required("لطفا هزینه خسارت را وارد کنید!!!"), +}); +const defaultValues = { + items_damge_id: null, + items_damge_value: null, + items_damge_amount: null, +}; +const DamageItem = ({ baseOnChange, getValues }) => { + const [selectedDamageItemList, setSelectedDamageItemList] = useState([]); + const { damageItemList, loadingDamageItemList, errorDamageItemList } = useDamageItemList(); + const { control, handleSubmit, reset, setValue } = useForm({ + defaultValues, + resolver: yupResolver(schema), + mode: "onBlur", + }); + const deleteDamageItem = (id) => { + setSelectedDamageItemList((prev) => prev.filter((DamageItem) => DamageItem.items_damge_id !== id)); + }; + useEffect(() => { + baseOnChange(selectedDamageItemList); + }, [selectedDamageItemList]); + const handleCreateDamage = (data) => { + setSelectedDamageItemList((prev) => { + const isDuplicate = prev.some((item) => item.items_damge_id === data.items_damge_id); + if (!isDuplicate) { + return [...prev, data]; + } else { + return prev; + } + }); + reset(); + }; + + return ( + <> + + {loadingDamageItemList ? ( + + + + ) : ( + <> + + ( + option.title} + isOptionEqualToValue={(option, value) => option.id === value.id} + loading={loadingDamageItemList} + renderInput={(params) => ( + + )} + onChange={(_, value) => field.onChange(value?.id)} + value={damageItemList.find((item) => item.id === field.value) || null} + /> + )} + /> + + + + + + + + + + + )} + + + + + {selectedDamageItemList.length === 0 && ( + + + ابتدا اطلاعات بالا را تکمیل نمایید + + + )} + + + {selectedDamageItemList.length !== 0 && + selectedDamageItemList.map((selectedDamageItem, index) => ( + + + + ))} + + + + ); +}; +export default DamageItem; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItemInfo.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItemInfo.jsx new file mode 100644 index 0000000..7af4ff4 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItemInfo.jsx @@ -0,0 +1,43 @@ +import { Box, Card, IconButton, Stack, Typography } from "@mui/material"; +import DeleteIcon from "@mui/icons-material/Delete"; +import React from "react"; +import MinorCrashIcon from "@mui/icons-material/MinorCrash"; + +const DamageItemInfo = ({ deleteDamageItem, selectedDamageItem, damageItemList }) => { + const itemDamageId = damageItemList.find((item) => item.id == selectedDamageItem.items_damge_id); + return ( + + + + + + {itemDamageId.title} + + + میزان خسارت ({itemDamageId.unit}): {selectedDamageItem.items_damge_value} + + + هزینه خسارت (ریال): {selectedDamageItem.items_damge_amount} + + + + + deleteDamageItem(selectedDamageItem.items_damge_id)}> + + + + + ); +}; +export default DamageItemInfo; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamagePrice.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamagePrice.jsx new file mode 100644 index 0000000..02fe21a --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamagePrice.jsx @@ -0,0 +1,48 @@ +import { Controller, useWatch } from "react-hook-form"; +import { Grid, TextField } from "@mui/material"; +import DamageAmount from "./DamageAmount"; + +const DamagePrice = ({ damageItemList, control, setValue }) => { + const itemDamage = useWatch({ control, name: "items_damge_id" }); + const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage); + + return ( + <> + + { + return ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + field.onChange(inputValue); + }} + label={`میزان خسارت ${itemDamageId?.unit ? `(${itemDamageId?.unit})` : ""}`} + fullWidth + size="small" + error={error} + helperText={error?.message} + /> + ); + }} + /> + + + + ); +}; +export default DamagePrice; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageReport.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageReport.jsx new file mode 100644 index 0000000..a649b14 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageReport.jsx @@ -0,0 +1,53 @@ +import { FormControlLabel, RadioGroup, Stack } from "@mui/material"; +import { Controller } from "react-hook-form"; +import { useState } from "react"; +import PoliceFileReport from "./PoliceFileReport"; +import Radio from "@mui/material/Radio"; + +const DamageReport = ({ control, setValue }) => { + const [selectedOption, setSelectedOption] = useState("report_base"); + + const handleOptionChange = (e) => { + setSelectedOption(e.target.value); + if (e.target.value === "report_base") { + setValue("police_file_checkbox", ""); + setValue("report_base", "1"); + } + }; + + return ( + <> + + + } + /> + } + label="کروکی یا نامه پلیس راه (معرفی فرد به اداره)" + /> + } + /> + } + label="بازدید عوامل و کارشناسان راهداری" + /> + + + {selectedOption === "police_file_checkbox" && } + + ); +}; +export default DamageReport; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/ImageUpload.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/ImageUpload.jsx new file mode 100644 index 0000000..107d640 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/ImageUpload.jsx @@ -0,0 +1,77 @@ +import { FormControl, FormHelperText } from "@mui/material"; +import UploadSystem from "@/core/components/UploadSystem"; +import React, { useEffect, useState } from "react"; + +const ImageUpload = ({ name, value, onChange, error, beforeImage = null, title }) => { + const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null); + const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null); + const [beforeFileName, setBeforeFileName] = useState(null); + const [showBeforeImage, setShowBeforeImage] = useState(!beforeImage); + + useEffect(() => { + if (value) { + setShowBeforeImage(false); + if (typeof value === "string") { + setBeforeImg(value); + setBeforeFileType("image/"); + } else if (value instanceof File) { + setBeforeImg(URL.createObjectURL(value)); + setBeforeFileType(value.type); + } + } + }, [value]); + + const handleFileChange = (event) => { + const uploadedFile = event.target?.files?.[0]; + if (uploadedFile) { + const fileType = event.target?.files?.[0].type; + const fileName = event.target?.files?.[0].name; + setBeforeImg(URL.createObjectURL(uploadedFile)); + setBeforeFileType(fileType); + setBeforeFileName(fileName); + onChange(uploadedFile); + setShowBeforeImage(false); + } + }; + + return ( + + + {title} + + + {error ? error.message : null} + + ); +}; +export default ImageUpload; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/PoliceFileReport.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/PoliceFileReport.jsx new file mode 100644 index 0000000..9a93566 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/PoliceFileReport.jsx @@ -0,0 +1,65 @@ +import { Controller } from "react-hook-form"; +import { Grid, Stack, TextField } from "@mui/material"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import ImageUpload from "./ImageUpload"; + +const PoliceFileReport = ({ control }) => { + return ( + + + + + ( + + )} + /> + + + { + return ( + field.onChange(value || [])} + helperText={error ? error.message : null} + /> + ); + }} + name={"police_file_date"} + /> + + + + ( + + )} + /> + + ); +}; +export default PoliceFileReport; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/index.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/index.jsx new file mode 100644 index 0000000..86c05f8 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/index.jsx @@ -0,0 +1,12 @@ +"use client"; +import { Dialog } from "@mui/material"; +import CreateFormContent from "@/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CreateFormContent"; + +const OperatorCreateForm = ({ open, setOpen, mutate }) => { + return ( + + + + ); +}; +export default OperatorCreateForm; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/index.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/index.jsx new file mode 100644 index 0000000..5816c12 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/index.jsx @@ -0,0 +1,38 @@ +"use client"; +import { Button, IconButton, useMediaQuery } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import { useState } from "react"; +import { AddCircle } from "@mui/icons-material"; +import OperatorCreateForm from "./Forms"; + +const OperatorCreate = ({ mutate }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + + ); +}; +export default OperatorCreate; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/OperatorList.jsx b/src/components/dashboard/technicalBuildingDamage/operator/OperatorList.jsx new file mode 100644 index 0000000..04955b3 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/OperatorList.jsx @@ -0,0 +1,356 @@ +"use client"; +import { useEffect, useMemo, useState } from "react"; +import { Box, Stack, Typography } from "@mui/material"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import Toolbar from "./Toolbar"; +import { GET_TECHNICAL_DAMAGE_OPERATOR_LIST } from "@/core/utils/routes"; +import moment from "jalali-moment"; +import RowActions from "./RowActions"; +import useProvinces from "@/lib/hooks/useProvince"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import ImageDialog from "./RowActions/ImageDialog"; +import LocationForm from "./RowActions/LocationForm"; +import ShowPlate from "./RowActions/ShowPlate"; + +const OperatorList = () => { + const statusOptions = [ + { value: 0, label: "بدون اقدام" }, + { value: 1, label: "صدور نامه بیمه و کارشناسی داغی" }, + { value: 2, label: "فیش ها ثبت شده است" }, + { value: 3, label: "فاکتور صادر شده است" }, + { value: 4, label: "فاکتور پرداخت شده است" }, + { value: 5, label: "نامه پلیس راه صادر شده است" }, + ]; + const columns = useMemo(() => { + return [ + { + accessorKey: "id", + header: "کد یکتا", // Unique Code + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "province_id", + header: "استان", + id: "province_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 130, + ColumnSelectComponent: (props) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const getColumnSelectOptions = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.province_fa}, + }, + { + accessorKey: "city_id", + header: "شهرستان", // Office + id: "city_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + dependencyId: "province_id", + grow: false, + size: 120, + ColumnSelectComponent: (props) => { + const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( + props.dependencyFieldValue.value + ); + const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value); + + const getColumnSelectOptions = useMemo(() => { + if (props.dependencyFieldValue.value === "") { + return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; + } + if (loadingEdaratList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorEdaratList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "شهر" }, + ...edaratList.map((edare) => ({ + value: edare.id, + label: edare.name_fa, + })), + ]; + }, [edaratList, loadingEdaratList, errorEdaratList]); + useEffect(() => { + if (prevDependency === props.dependencyFieldValue?.value) return; + props.handleChange({ ...props.filterParameters, value: "" }); + setPrevDependency(props.dependencyFieldValue?.value); + }, [props.dependencyFieldValue?.value]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.city_fa}, + }, + { + accessorKey: "axis_name", + header: "نام محور", + id: "axis_name", + enableColumnFilter: true, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "accident_type", + header: "نوع تصادف", // Value + id: "accident_type", + enableColumnFilter: true, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + Cell: ({ row }) => <>{row.original.accident_type_fa}, + }, + { + accessorFn: (row) => { + const formattedDate = moment(row.accident_date, "YYYY-MM-DD"); // تنظیم فرمت صحیح تاریخ + const formattedTime = row.accident_time || "00:00:00"; // در صورت خالی بودن مقدار، مقدار پیش‌فرض داده شود + return moment(`${formattedDate.format("YYYY-MM-DD")}T${formattedTime}`) + .locale("fa") + .format("HH:mm | YYYY/MM/DD"); + }, + header: "تاریخ تصادف", + id: "accident_date", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorKey: "files", + header: "تصاویر", + id: "files", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + if (row.original.damage_picture1) { + const imagesList = [row.original.damage_picture1, row.original.damage_picture2]; + return ( + + + + ); + } + return ( + + بدون تصویر + + ); + }, + }, + { + accessorKey: "location", + header: "موقعیت", + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + return ( + + + + ); + }, + }, + { + accessorKey: "sum", + header: "مبلغ کل خسارت (ریال)", + id: "sum", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + sortDescFirst: true, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.sum / 1).toLocaleString()} + + ), + }, + { + accessorKey: "plaque", + header: "پلاک", + id: "plaque", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: true, + grow: false, + size: 100, + Cell: ({ renderedCellValue }) => , + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "created_at", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorKey: "status", + header: "اخرین وضعیت", + id: "status", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + columnSelectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + Cell: ({ row }) => { + return {row.original.status_fa}; + }, + }, + { + header: "اطلاعات واریز", + id: "items", + enableColumnFilter: false, + enableSorting: false, + grow: false, + size: 50, + columns: [ + { + header: "مبلغ فیش (ریال)", + id: "final_amount", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.final_amount / 1).toLocaleString()} + + ), + }, + { + header: "مبلغ بیمه (ریال)", + id: "deposit_insurance_amount", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.deposit_insurance_amount / 1).toLocaleString()} + + ), + }, + { + header: "مبلغ داغی (ریال)", + id: "deposit_daghi_amount", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.deposit_daghi_amount / 1).toLocaleString()} + + ), + }, + ], + }, + ]; + }, []); + + return ( + <> + + + + + ); +}; +export default OperatorList; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/ImageFormContent.jsx new file mode 100644 index 0000000..9aaa92e --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/ImageFormContent.jsx @@ -0,0 +1,44 @@ +import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; +import Image from "next/image"; + +const ImageFormContent = ({ image, title }) => { + const theme = useTheme(); + return ( + + + {title} + + + Image + + + ); +}; +export default ImageFormContent; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/index.jsx b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/index.jsx new file mode 100644 index 0000000..6e442ca --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/index.jsx @@ -0,0 +1,44 @@ +import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Paper, Tooltip } from "@mui/material"; +import { useState } from "react"; +import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; +import ImageFormContent from "./ImageFormContent"; + +const ImageDialog = ({ images }) => { + const [openImageDialog, setOpenImageDialog] = useState(false); + + return ( + <> + + setOpenImageDialog(true)}> + + + + setOpenImageDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"sm"} + scroll="paper" + > + تصاویر + + + + + + + + + + + + ); +}; +export default ImageDialog; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/LocationFormContent.jsx new file mode 100644 index 0000000..14b3db3 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/LocationFormContent.jsx @@ -0,0 +1,35 @@ +"use client"; +import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material"; +import React from "react"; +import dynamic from "next/dynamic"; +import MapLoading from "@/core/components/MapLayer/Loading"; +import ShowLocationMarker from "@/core/components/ShowLocationMarker"; +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_lat, end_lng }) => { + return ( + <> + + + + + + + + + + + + ); +}; +export default LocationFormContent; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/index.jsx b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/index.jsx new file mode 100644 index 0000000..f578ca8 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/index.jsx @@ -0,0 +1,38 @@ +import React, { useState } from "react"; +import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; +import ExploreIcon from "@mui/icons-material/Explore"; +import LocationFormContent from "./LocationFormContent"; +const LocationForm = ({ start_lat, start_lng, end_lat, end_lng }) => { + const [openLocationDialog, setOpenLocationDialog] = useState(false); + + return ( + <> + + setOpenLocationDialog(true)}> + + + + setOpenLocationDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"md"} + > + موقعیت + + + + ); +}; +export default LocationForm; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ShowPlate/index.jsx b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ShowPlate/index.jsx new file mode 100644 index 0000000..90d3ad0 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ShowPlate/index.jsx @@ -0,0 +1,38 @@ +import { Stack } from "@mui/material"; + +const ShowPlate = ({ plate }) => { + return ( + + + {plate.split("-")[3]} + + + + {plate.split("-")[2]} + + + {plate.split("-")[1]} + + + {plate.split("-")[0]} + + + + ); +}; +export default ShowPlate; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/index.jsx b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/index.jsx new file mode 100644 index 0000000..c9e69ef --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/RowActions/index.jsx @@ -0,0 +1,6 @@ +import { Box } from "@mui/material"; + +const RowActions = ({ row }) => { + return ; +}; +export default RowActions; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Toolbar.jsx b/src/components/dashboard/technicalBuildingDamage/operator/Toolbar.jsx new file mode 100644 index 0000000..b774945 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/Toolbar.jsx @@ -0,0 +1,11 @@ +import { Box } from "@mui/material"; +import OperatorCreate from "./Actions/create"; + +const Toolbar = ({ table, filterData, mutate }) => { + return ( + + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/index.jsx b/src/components/dashboard/technicalBuildingDamage/operator/index.jsx new file mode 100644 index 0000000..4936664 --- /dev/null +++ b/src/components/dashboard/technicalBuildingDamage/operator/index.jsx @@ -0,0 +1,14 @@ +"use client"; +import PageTitle from "@/core/components/PageTitle"; +import { Stack } from "@mui/material"; +import OperatorList from "./OperatorList"; + +const OperatorPage = () => { + return ( + + + + + ); +}; +export default OperatorPage; diff --git a/src/core/components/MapInfoOneMarker.jsx b/src/core/components/MapInfoOneMarker.jsx index b9574c1..a68d4bf 100644 --- a/src/core/components/MapInfoOneMarker.jsx +++ b/src/core/components/MapInfoOneMarker.jsx @@ -37,7 +37,7 @@ const createCustomIcon = (size, iconUrl, labelText, color) => { }; const MAX_ZOOM_FOR_MARKER = 13; -const MapInteraction = ({ setValue, startLat, startLng }) => { +const MapInteraction = ({ setValue, startLat, startLng, title }) => { const [isMarkerLocked, setIsMarkerLocked] = useState(!!(startLat && startLng)); const [enableSend, setEnableSend] = useState(false); const [startIconColor, setStartIconColor] = useState(startLat ? "#1CAC66" : "#003d4f"); @@ -109,7 +109,7 @@ const MapInteraction = ({ setValue, startLat, startLng }) => { > - برای ثبت محل فعالیت، لطفاً نقشه را بیشتر زوم کنید. + {title} @@ -131,7 +131,12 @@ const MapInteraction = ({ setValue, startLat, startLng }) => { ); }; -const MapInfoOneMarker = ({ setValue, errors, StartPoint = null }) => { +const MapInfoOneMarker = ({ + setValue, + errors, + StartPoint = null, + title = "برای ثبت محل فعالیت، لطفاً نقشه را بیشتر زوم کنید.", +}) => { return ( @@ -140,6 +145,7 @@ const MapInfoOneMarker = ({ setValue, errors, StartPoint = null }) => { setValue={setValue} startLat={StartPoint ? StartPoint.lat : null} startLng={StartPoint ? StartPoint.lng : null} + title={title} /> diff --git a/src/core/components/MuiDatePicker.jsx b/src/core/components/MuiDatePicker.jsx index 0b5d8b1..f931188 100644 --- a/src/core/components/MuiDatePicker.jsx +++ b/src/core/components/MuiDatePicker.jsx @@ -8,67 +8,69 @@ import moment from "jalali-moment"; function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error, label }) { return ( - - - { - const date = new Date(value); - const formattedDate = moment(date).locale("en").format("YYYY-MM-DD"); - setFieldValue(name, formattedDate); - }} - minDate={minDate ? new Date(minDate) : null} - maxDate={maxDate ? new Date(maxDate) : null} - slotProps={{ - textField: { - error: error, - size: "small", - placeholder: placeholder, - InputLabelProps: { - shrink: true, + <> + + + { + const date = new Date(value); + const formattedDate = moment(date).locale("en").format("YYYY-MM-DD"); + setFieldValue(name, formattedDate); + }} + minDate={minDate ? new Date(minDate) : null} + maxDate={maxDate ? new Date(maxDate) : null} + slotProps={{ + textField: { + error: error, + size: "small", + placeholder: placeholder, + InputLabelProps: { + shrink: true, + }, + InputProps: { + endAdornment: ( + + { + event.stopPropagation(); + setFieldValue(name, ""); + }} + sx={{ + color: "#bfbfbf", + "&:hover": { + backgroundColor: "rgba(189, 189, 189, 0.1)", + color: "#363434", + }, + }} + > + + + + ), + }, }, - InputProps: { - endAdornment: ( - - { - event.stopPropagation(); - setFieldValue(name, ""); - }} - sx={{ - color: "#bfbfbf", - "&:hover": { - backgroundColor: "rgba(189, 189, 189, 0.1)", - color: "#363434", - }, - }} - > - - - - ), - }, - }, - }} - /> - {helperText ? ( - - {helperText} - - ) : null} - - + }} + /> + + + {helperText && ( + + {helperText} + + )} + ); } diff --git a/src/core/components/MuiTimePicker.jsx b/src/core/components/MuiTimePicker.jsx index b3336d4..a8fc46c 100644 --- a/src/core/components/MuiTimePicker.jsx +++ b/src/core/components/MuiTimePicker.jsx @@ -13,66 +13,68 @@ function MuiTimePicker({ value, setFieldValue, name, minTime, maxTime, helperTex const parsedMaxTime = maxTime || null; return ( - - - { - setFieldValue(name, newValue); - }} - minTime={parsedMinTime} - maxTime={parsedMaxTime} - slotProps={{ - textField: { - placeholder: placeholder, - size: "small", - error: error, - InputLabelProps: { - shrink: true, + <> + + + { + setFieldValue(name, newValue); + }} + minTime={parsedMinTime} + maxTime={parsedMaxTime} + slotProps={{ + textField: { + placeholder: placeholder, + size: "small", + error: error, + InputLabelProps: { + shrink: true, + }, + InputProps: { + endAdornment: ( + + { + event.stopPropagation(); + setFieldValue(name, ""); + }} + sx={{ + color: "#bfbfbf", + "&:hover": { + backgroundColor: "rgba(189, 189, 189, 0.1)", + color: "#363434", + }, + }} + > + + + + ), + }, }, - InputProps: { - endAdornment: ( - - { - event.stopPropagation(); - setFieldValue(name, ""); - }} - sx={{ - color: "#bfbfbf", - "&:hover": { - backgroundColor: "rgba(189, 189, 189, 0.1)", - color: "#363434", - }, - }} - > - - - - ), - }, - }, - }} - /> - {helperText ? ( - - {helperText} - - ) : null} - - + }} + /> + + + {helperText && ( + + {helperText} + + )} + ); } diff --git a/src/core/components/PlateNumber.jsx b/src/core/components/PlateNumber.jsx new file mode 100644 index 0000000..7b396d5 --- /dev/null +++ b/src/core/components/PlateNumber.jsx @@ -0,0 +1,197 @@ +import { Box, Button, Drawer, FormHelperText, Stack, TextField } from "@mui/material"; +import AccessibleIcon from "@mui/icons-material/Accessible"; +import { Controller, useFormState, useWatch } from "react-hook-form"; +import { useEffect, useState } from "react"; + +const plate_words = [ + { id: 1, value: "الف", name: "الف" }, + { id: 2, value: "ب", name: "ب" }, + { id: 3, value: "پ", name: "پ" }, + { id: 4, value: "ت", name: "ت" }, + { id: 5, value: "ث", name: "ث" }, + { id: 6, value: "ج", name: "ج" }, + { id: 7, value: "د", name: "د" }, + { id: 8, value: "ز", name: "ز" }, + { id: 9, value: "س", name: "س" }, + { id: 10, value: "ش", name: "ش" }, + { id: 11, value: "ص", name: "ص" }, + { id: 12, value: "ط", name: "ط" }, + { id: 13, value: "ع", name: "ع" }, + { id: 14, value: "ف", name: "ف" }, + { id: 15, value: "ق", name: "ق" }, + { id: 16, value: "ک", name: "گ" }, + { id: 17, value: "ل", name: "ل" }, + { id: 18, value: "م", name: "م" }, + { id: 19, value: "ن", name: "ن" }, + { id: 20, value: "و", name: "و" }, + { id: 21, value: "ه", name: "ه" }, + { id: 22, value: "ی", name: "ی" }, + { id: 23, value: "*", name: }, +]; + +const PlateNumber = ({ control }) => { + const platePart2 = useWatch({ control, name: "plate_part2" }); + const [plateDrawer, setPlateDrawer] = useState(false); + const { errors } = useFormState({ control }); + const plateErrors = ["plate_part1", "plate_part2", "plate_part3", "plate_part4"].some((part) => errors[part]); + + return ( + <> + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + inputValue.length <= 2 ? field.onChange(inputValue) : null; + }} + size="small" + placeholder="xx" + sx={{ flexGrow: 2, "& fieldset": { border: "none" } }} + /> + )} + /> + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + inputValue.length <= 3 ? field.onChange(inputValue) : null; + }} + size="small" + placeholder="xxx" + sx={{ + flexGrow: 3, + borderLeft: 1, + borderRadius: 0, + borderColor: "divider", + "& fieldset": { border: "none" }, + }} + /> + )} + /> + + + + setPlateDrawer(false)} + > + + {plate_words.map((item) => ( + ( + + )} + /> + ))} + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + inputValue.length <= 2 ? field.onChange(inputValue) : null; + }} + /> + )} + /> + + + + + + + {plateErrors && ( + + پلاک نامعتبر است!!! + + )} + + ); +}; +export default PlateNumber; diff --git a/src/core/components/SelectBox.jsx b/src/core/components/SelectBox.jsx new file mode 100644 index 0000000..e6e8552 --- /dev/null +++ b/src/core/components/SelectBox.jsx @@ -0,0 +1,49 @@ +import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material"; + +function SelectBox({ + name, + value, + onChange, + onBlur, + selectors, + label, + error, + schema, + disabled, + helperText, + isLoading, + errorEcured, + variant = "outlined", +}) { + return ( + + {label} + + {helperText || error?.message} + + ); +} +export default SelectBox; diff --git a/src/core/components/ShowPlak.jsx b/src/core/components/ShowPlak.jsx index 5d51455..6e02934 100644 --- a/src/core/components/ShowPlak.jsx +++ b/src/core/components/ShowPlak.jsx @@ -14,7 +14,6 @@ const ShowPlak = ({ plak_number }) => { }; }; const plakParts = processPlak(plak_number); - return ( value === true); + const flattenObject = (obj) => { + let result = {}; + for (const key in obj) { + if (typeof obj[key] === "object" && obj[key] !== null) { + Object.assign(result, flattenObject(obj[key])); // باز کردن اشیای تودرتو + } else { + result[key] = obj[key]; + } + } + return result; + }; + // تبدیل آبجکت به آبجکت فلت شده + const flatHideData = flattenObject(hideData); + + const CheckHideValues = Object.values(flatHideData).every((value) => value === true); hasAnyConflict = !CheckHideValues || JSON.stringify(sortData) !== JSON.stringify(initialSort) || !CheckFilterValues; diff --git a/src/lib/hooks/useDamageItemList.js b/src/lib/hooks/useDamageItemList.js new file mode 100644 index 0000000..a353627 --- /dev/null +++ b/src/lib/hooks/useDamageItemList.js @@ -0,0 +1,29 @@ +import { useEffect, useState } from "react"; +import { GET_DAMAGE_ITEM_LIST, GET_PROVINCE_LISTS } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; + +const useDamageItemList = () => { + const requestServer = useRequest(); + const [damageItemList, setDamageItemList] = useState([]); + const [loadingDamageItemList, setLoadingDamageItemList] = useState(true); + const [errorDamageItemList, setErrorDamageItemList] = useState(null); + + useEffect(() => { + const fetchCities = async () => { + try { + const response = await requestServer(`${GET_DAMAGE_ITEM_LIST}`); + setDamageItemList(response.data.data); + setLoadingDamageItemList(false); + } catch (e) { + setErrorDamageItemList(e); + setLoadingDamageItemList(false); + } + }; + + fetchCities(); + }, []); + + return { damageItemList, loadingDamageItemList, errorDamageItemList }; +}; + +export default useDamageItemList; diff --git a/src/lib/hooks/useProvince.js b/src/lib/hooks/useProvince.js index f6b98b8..a36df7d 100644 --- a/src/lib/hooks/useProvince.js +++ b/src/lib/hooks/useProvince.js @@ -15,7 +15,6 @@ const useProvinces = () => { setProvinces(response.data.data); setLoadingProvinces(false); } catch (e) { - console.error("Error fetching provinces:", e); setErrorProvinces(e); setLoadingProvinces(false); } From baaf967f1cb81f9547a44f678da238db682cd3f0 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 23 Feb 2025 13:22:24 +0000 Subject: [PATCH 09/32] Feature/amiriis receipt report --- .../dashboard/receipt/report/page.js | 6 + .../receipt/report/ExcelPrint/index.jsx | 72 ++++++ .../dashboard/receipt/report/ReportLists.jsx | 242 ++++++++++++++++++ .../report/Search/FromDateController.jsx | 28 ++ .../report/Search/SearchReportField.jsx | 45 ++++ .../receipt/report/Search/SelectProvince.jsx | 47 ++++ .../report/Search/ToDateController.jsx | 30 +++ .../dashboard/receipt/report/Search/index.jsx | 13 + .../dashboard/receipt/report/Toolbar.jsx | 11 + .../dashboard/receipt/report/index.jsx | 135 ++++++++++ .../Search/FromDateController.jsx | 1 + .../ItemsReports/Search/ToDateController.jsx | 1 + .../roadItems/reports/ItemsReports/index.jsx | 17 +- .../Search/FromDateController.jsx | 1 + .../Search/ToDateController.jsx | 3 +- .../reports/SubItemsReports/index.jsx | 7 +- .../roadItems/supervisor/RowActions/index.jsx | 4 +- .../roadItems/supervisor/SupervisorList.jsx | 10 +- .../reports/Search/FromDateController.jsx | 1 + .../reports/Search/ToDateController.jsx | 1 + .../dashboard/roadPatrols/reports/index.jsx | 11 +- .../supervisor/RowActions/index.jsx | 4 +- .../roadPatrols/supervisor/SupervisorList.jsx | 6 +- src/core/utils/pageMenu.js | 4 +- src/core/utils/routes.js | 7 + 25 files changed, 676 insertions(+), 31 deletions(-) create mode 100644 src/app/(withAuth)/dashboard/receipt/report/page.js create mode 100644 src/components/dashboard/receipt/report/ExcelPrint/index.jsx create mode 100644 src/components/dashboard/receipt/report/ReportLists.jsx create mode 100644 src/components/dashboard/receipt/report/Search/FromDateController.jsx create mode 100644 src/components/dashboard/receipt/report/Search/SearchReportField.jsx create mode 100644 src/components/dashboard/receipt/report/Search/SelectProvince.jsx create mode 100644 src/components/dashboard/receipt/report/Search/ToDateController.jsx create mode 100644 src/components/dashboard/receipt/report/Search/index.jsx create mode 100644 src/components/dashboard/receipt/report/Toolbar.jsx create mode 100644 src/components/dashboard/receipt/report/index.jsx diff --git a/src/app/(withAuth)/dashboard/receipt/report/page.js b/src/app/(withAuth)/dashboard/receipt/report/page.js new file mode 100644 index 0000000..acc26be --- /dev/null +++ b/src/app/(withAuth)/dashboard/receipt/report/page.js @@ -0,0 +1,6 @@ +import ReceiptReportPage from "@/components/dashboard/receipt/report" + +const Page = () => { + return +} +export default Page \ No newline at end of file diff --git a/src/components/dashboard/receipt/report/ExcelPrint/index.jsx b/src/components/dashboard/receipt/report/ExcelPrint/index.jsx new file mode 100644 index 0000000..79ea64f --- /dev/null +++ b/src/components/dashboard/receipt/report/ExcelPrint/index.jsx @@ -0,0 +1,72 @@ +"use client"; +import { EXPORT_COUNTRY_RECEIPT_REPORT, EXPORT_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { useTheme } from "@emotion/react"; +import DescriptionIcon from "@mui/icons-material/Description"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; +import FileSaver from "file-saver"; +import moment from "jalali-moment"; +import { useMemo, useState } from "react"; + +const PrintExcel = ({ table, filterData }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [loading, setLoading] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { + if (filter.value) { + acc.push({ id: filter.id, value: filter.value }); + } + return acc; + }, []); + + const filterParams = useMemo(() => { + const params = new URLSearchParams(); + activeFilters.length > 0 && + activeFilters.map((filter, index) => { + params.set(`${filter.id}`, filter.value); + }); + return params; + }, [activeFilters]); + + const clickHandler = () => { + setLoading(true); + const CountryOrProvince = activeFilters.find((item) => item.id === "province_id"); + const requestUrl = + CountryOrProvince.value === "-1" ? EXPORT_COUNTRY_RECEIPT_REPORT : EXPORT_PROVINCE_RECEIPT_REPORT; + requestServer(`${requestUrl}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی کارتابل گزارشات خسارات وارده بر ابنیه فنی تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => { }) + .finally(() => { + setLoading(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + ); +}; +export default PrintExcel; diff --git a/src/components/dashboard/receipt/report/ReportLists.jsx b/src/components/dashboard/receipt/report/ReportLists.jsx new file mode 100644 index 0000000..4386ac6 --- /dev/null +++ b/src/components/dashboard/receipt/report/ReportLists.jsx @@ -0,0 +1,242 @@ +"use client"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { Box } from "@mui/material"; +import { useMemo } from "react"; +import Toolbar from "./Toolbar"; + +const ReportLists = ({ data, specialFilter }) => { + const columns = useMemo(() => { + return [ + { + accessorKey: "edare_name", + header: "اداره کل", + id: "edare_name", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + }, + { + accessorKey: "tedade", + header: "تعداد کل", + id: "tedade", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + header: "تعداد نحوه شناسایی خسارت", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + columns: [ + { + accessorKey: "policeCount", + header: "گزارش پلیس راه", + id: "policeCount", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "gashtCount", + header: "گزارش گشت راهداری", + id: "gashtCount", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + ] + }, + { + accessorKey: "kesartAll", + header: "مبلغ کل خسارات ثبت شده (ریال)", + id: "kesartAll", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "vosolAll", + header: "کل مبلغ وصول شده (ریال)", + id: "vosolAll", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + header: "مبلغ وصول شده به تفکیک نوع پرداخت (ریال)", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + columns: [ + { + accessorKey: "fish", + header: "فیش", + id: "fish", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "bime", + header: "بیمه", + id: "bime", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "daghi", + header: "داغی", + id: "daghi", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + ] + }, + ]; + }, []); + + return ( + + + + ); +}; + +export default ReportLists; diff --git a/src/components/dashboard/receipt/report/Search/FromDateController.jsx b/src/components/dashboard/receipt/report/Search/FromDateController.jsx new file mode 100644 index 0000000..b18b320 --- /dev/null +++ b/src/components/dashboard/receipt/report/Search/FromDateController.jsx @@ -0,0 +1,28 @@ +import { Controller } from "react-hook-form"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import React from "react"; +import moment from "jalali-moment"; + +const FromDateController = ({ control }) => { + return ( + ( + { + const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); + onChange(formattedDate); // Update the field value in react-hook-form + }} + helperText={error ? error.message : null} + /> + )} + /> + ); +}; +export default FromDateController; diff --git a/src/components/dashboard/receipt/report/Search/SearchReportField.jsx b/src/components/dashboard/receipt/report/Search/SearchReportField.jsx new file mode 100644 index 0000000..ab8442c --- /dev/null +++ b/src/components/dashboard/receipt/report/Search/SearchReportField.jsx @@ -0,0 +1,45 @@ +import { Button, Grid, LinearProgress, Typography } from "@mui/material"; +import SearchIcon from "@mui/icons-material/Search"; +import React from "react"; +import FromDateController from "./FromDateController"; +import ToDateController from "./ToDateController"; +import SelectProvince from "./SelectProvince"; +import { Controller } from "react-hook-form"; + +const SearchReportField = ({ control, hasProvincesPermission }) => { + return ( + + + + + + + + {hasProvincesPermission && ( + + + + )} + + ( + + )} + /> + + + ); +}; + +export default SearchReportField; diff --git a/src/components/dashboard/receipt/report/Search/SelectProvince.jsx b/src/components/dashboard/receipt/report/Search/SelectProvince.jsx new file mode 100644 index 0000000..b1d8a39 --- /dev/null +++ b/src/components/dashboard/receipt/report/Search/SelectProvince.jsx @@ -0,0 +1,47 @@ +import { Controller } from "react-hook-form"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import React from "react"; +import { FormControl, FormHelperText, InputLabel, LinearProgress, MenuItem, Select, Typography } from "@mui/material"; +import useProvinces from "@/lib/hooks/useProvince"; + +const SelectProvince = ({ control }) => { + const { provinces, loadingProvinces, errorProvinces } = useProvinces(); + return ( + ( + + استان + + {error && {error.message}} + + )} + /> + ); +}; +export default SelectProvince; diff --git a/src/components/dashboard/receipt/report/Search/ToDateController.jsx b/src/components/dashboard/receipt/report/Search/ToDateController.jsx new file mode 100644 index 0000000..6f8b0d8 --- /dev/null +++ b/src/components/dashboard/receipt/report/Search/ToDateController.jsx @@ -0,0 +1,30 @@ +import { Controller, useWatch } from "react-hook-form"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import React from "react"; +import moment from "jalali-moment"; + +const ToDateController = ({ control }) => { + const minDate = useWatch({ control, name: "from_date" }); + return ( + ( + { + const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); + onChange(formattedDate); // Update the field value in react-hook-form + }} + helperText={error ? error.message : null} + /> + )} + /> + ); +}; +export default ToDateController; diff --git a/src/components/dashboard/receipt/report/Search/index.jsx b/src/components/dashboard/receipt/report/Search/index.jsx new file mode 100644 index 0000000..f29d302 --- /dev/null +++ b/src/components/dashboard/receipt/report/Search/index.jsx @@ -0,0 +1,13 @@ +import StyledForm from "@/core/components/StyledForm"; +import SearchReportField from "./SearchReportField"; + +const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => { + return ( + <> + + + + + ); +}; +export default SearchReportList; diff --git a/src/components/dashboard/receipt/report/Toolbar.jsx b/src/components/dashboard/receipt/report/Toolbar.jsx new file mode 100644 index 0000000..7dd930f --- /dev/null +++ b/src/components/dashboard/receipt/report/Toolbar.jsx @@ -0,0 +1,11 @@ +import PrintExcel from "./ExcelPrint"; +import { Box } from "@mui/material"; + +const Toolbar = ({ table, filterData, mutate }) => { + return ( + + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/receipt/report/index.jsx b/src/components/dashboard/receipt/report/index.jsx new file mode 100644 index 0000000..3efedfe --- /dev/null +++ b/src/components/dashboard/receipt/report/index.jsx @@ -0,0 +1,135 @@ +'use client' +import PageTitle from "@/core/components/PageTitle"; +import { GET_CITY_RECEIPT_REPORT, GET_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes"; +import { useAuth } from "@/lib/contexts/auth"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { LinearProgress, Stack } from "@mui/material"; +import moment from "jalali-moment"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import * as yup from "yup"; +import SearchReportList from "./Search"; +import ReportLists from "./ReportLists"; + +const ReceiptReportPage = () => { + const [data, setData] = useState([]); + const { data: userPermissions } = usePermissions(); + const { user } = useAuth(); + const requestServer = useRequest(); + const hasProvincesPermission = userPermissions?.includes("show-receipt"); + + const defaultValues = { + from_date: moment(new Date()).format("YYYY-MM-DD"), + date_to: moment(new Date()).format("YYYY-MM-DD"), + province_id: hasProvincesPermission ? "-1" : user.province_id, + }; + const defaultFilter = [ + { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]; + const [specialFilter, setSpecialFilter] = useState(defaultFilter); + + const onSearchSubmit = async (data) => { + const params = new URLSearchParams(); + params.set("from_date", `${data.from_date}`); + params.set("date_to", `${data.date_to}`); + setSpecialFilter([ + { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]); + if (data.province_id === "-1") { + try { + const response = await requestServer(`${GET_PROVINCE_RECEIPT_REPORT}?${params}`); + const result = response.data.data; + const nationalReportForItem = result.activities.find((report) => report.province_id === -1); + const nationalReport = { + edare_name: "کل کشور", + ...nationalReportForItem, + }; + + setData({ + data: [ + nationalReport, + ...result.provinces.map((province) => { + const filteredReports = result.activities.find( + (report) => report.province_id === province.id + ); + return { + ...filteredReports, + edare_name: province.name_fa, + }; + }), + ], + filters: data, + }); + } catch (e) { } + } else { + try { + params.set("province_id", `${data.province_id}`); + const response = await requestServer(`${GET_CITY_RECEIPT_REPORT}?${params}`); + const result = response.data.data; + const nationalReportForItem = result.activities.find((report) => report.edare_id === "-1"); + const nationalReport = { + ...nationalReportForItem, + edare_name: "کل استان", + }; + setData({ + data: [ + nationalReport, + ...result.edarateShahri.map((edare) => { + const filteredReports = result.activities.find((report) => report.edare_id == edare.id); + return { + edare_name: edare.name_fa, + ...filteredReports, + }; + }), + ], + filters: data, + }); + } catch (e) { } + } + }; + + useEffect(() => { + if (!userPermissions) return + onSearchSubmit(defaultValues); + }, [userPermissions]); + + const validationSchema = yup.object().shape({ + from_date: yup + .string() + .required("تاریخ شروع الزامی است!") + .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!") + .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), + date_to: yup + .string() + .required("تاریخ پایان الزامی است!") + .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!") + .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), + province_id: yup.string().nullable(), + }); + + const { control, handleSubmit } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onBlur", + }); + + return ( + + + + {!data ? : } + + ); +} +export default ReceiptReportPage \ No newline at end of file diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/Search/FromDateController.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/Search/FromDateController.jsx index 2cfa8af..0764aa8 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/Search/FromDateController.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/Search/FromDateController.jsx @@ -13,6 +13,7 @@ const FromDateController = ({ control }) => { name="from_date" error={!!error} value={value} + label={'تاریخ فعالیت (از تاریخ)'} placeholder={"از تاریخ"} setFieldValue={(name, newValue) => { const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/Search/ToDateController.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/Search/ToDateController.jsx index 003ac20..90b4bdd 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/Search/ToDateController.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/Search/ToDateController.jsx @@ -15,6 +15,7 @@ const ToDateController = ({ control }) => { minDate={minDate} error={!!error} value={value} + label={'تاریخ فعالیت (تا تاریخ)'} placeholder={"تا تاریخ"} setFieldValue={(name, newValue) => { const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx index 1efb794..528be1a 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx @@ -21,7 +21,7 @@ const ReportPage = () => { const { user } = useAuth(); const requestServer = useRequest(); - const hasProvincesPermission = userPermissions.includes("show-road-item-supervise-cartable"); + const hasProvincesPermission = userPermissions?.includes("show-road-item-supervise-cartable"); const defaultValues = { from_date: moment(new Date()).format("YYYY-MM-DD"), @@ -52,8 +52,8 @@ const ReportPage = () => { edare_name: "کل کشور", sum: result.activities.length ? result.activities - .filter((report) => report.p === -1) - .reduce((acc, report) => acc + (report.s || 0), 0) + .filter((report) => report.p === -1) + .reduce((acc, report) => acc + (report.s || 0), 0) : 0, }; itemsList.forEach((item) => { @@ -82,7 +82,7 @@ const ReportPage = () => { ], filters: data, }); - } catch (e) {} + } catch (e) { } } else { try { params.set("province_id", `${data.province_id}`); @@ -92,8 +92,8 @@ const ReportPage = () => { edare_name: "کل استان", sum: result.activities.length ? result.activities - .filter((report) => report.c === -1) - .reduce((acc, report) => acc + (report.s || 0), 0) + .filter((report) => report.c === -1) + .reduce((acc, report) => acc + (report.s || 0), 0) : 0, }; @@ -126,14 +126,15 @@ const ReportPage = () => { ], filters: data, }); - } catch (e) {} + } catch (e) { } } }; useEffect(() => { + if (!userPermissions) return if (itemsList.length === 0) return; onSearchSubmit(defaultValues); - }, [itemsList]); + }, [itemsList, userPermissions]); const validationSchema = yup.object().shape({ from_date: yup diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx index 2cfa8af..0764aa8 100644 --- a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/FromDateController.jsx @@ -13,6 +13,7 @@ const FromDateController = ({ control }) => { name="from_date" error={!!error} value={value} + label={'تاریخ فعالیت (از تاریخ)'} placeholder={"از تاریخ"} setFieldValue={(name, newValue) => { const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/ToDateController.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/ToDateController.jsx index 003ac20..3cb1952 100644 --- a/src/components/dashboard/roadItems/reports/SubItemsReports/Search/ToDateController.jsx +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/Search/ToDateController.jsx @@ -15,10 +15,11 @@ const ToDateController = ({ control }) => { minDate={minDate} error={!!error} value={value} + label={'تاریخ فعالیت (تا تاریخ)'} placeholder={"تا تاریخ"} setFieldValue={(name, newValue) => { const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); - onChange(formattedDate); // Update the field value in react-hook-form + onChange(formattedDate); }} helperText={error ? error.message : null} /> diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx index b5c8237..a2bd859 100644 --- a/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx @@ -98,7 +98,7 @@ const SubItemsReports = () => { const { data: userPermissions } = usePermissions(); const { user } = useAuth(); const requestServer = useRequest(); - const hasProvincesPermission = userPermissions.includes("show-road-item-supervise-cartable"); + const hasProvincesPermission = userPermissions?.includes("show-road-item-supervise-cartable"); const defaultValues = { from_date: moment(new Date()).format("YYYY-MM-DD"), @@ -166,7 +166,7 @@ const SubItemsReports = () => { columns: createColumns(result.sub_items), filters: data, }); - } catch (e) {} + } catch (e) { } } else { try { params.set("province_id", `${data.province_id}`); @@ -216,8 +216,9 @@ const SubItemsReports = () => { }; useEffect(() => { + if (!userPermissions) return onSearchSubmit(defaultValues); - }, []); + }, [userPermissions]); const validationSchema = yup.object().shape({ from_date: yup diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx index b18a1da..971ad37 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx @@ -7,8 +7,8 @@ import { usePermissions } from "@/lib/hooks/usePermissions"; const RowActions = ({ row, mutate }) => { const { data: userPermissions } = usePermissions(); - const hasDeletePermission = userPermissions.includes("delete-road-item"); - const hasRestorePermission = userPermissions.includes("restore-road-item"); + const hasDeletePermission = userPermissions?.includes("delete-road-item"); + const hasRestorePermission = userPermissions?.includes("restore-road-item"); return ( {row.original.status === 0 && row.original.can_supervise === 1 && ( diff --git a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx index c66a8a2..01d0f0c 100644 --- a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx @@ -25,7 +25,7 @@ const SupervisorList = () => { { value: 2, label: "عدم تایید" }, ]; const { data: userPermissions } = usePermissions(); - const hasCountryPermission = userPermissions.includes("show-road-item-supervise-cartable"); + const hasCountryPermission = userPermissions?.includes("show-road-item-supervise-cartable"); const { user } = useAuth(); const columns = useMemo(() => { const dynamicColumns = { @@ -125,8 +125,8 @@ const SupervisorList = () => { props.dependencyFieldValue?.value === "" ? "empty" : loadingEdaratList - ? "loading" - : props.filterParameters.value + ? "loading" + : props.filterParameters.value } columnSelectOption={getColumnSelectOptions} /> @@ -217,8 +217,8 @@ const SupervisorList = () => { props.dependencyFieldValue.value === "" ? "empty" : loadingSubItemsList - ? "loading" - : props.filterParameters.value + ? "loading" + : props.filterParameters.value } columnSelectOption={getColumnSelectOptions} /> diff --git a/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx b/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx index 2cfa8af..01f63ab 100644 --- a/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx +++ b/src/components/dashboard/roadPatrols/reports/Search/FromDateController.jsx @@ -13,6 +13,7 @@ const FromDateController = ({ control }) => { name="from_date" error={!!error} value={value} + label={'تاریخ شروع (از تاریخ)'} placeholder={"از تاریخ"} setFieldValue={(name, newValue) => { const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); diff --git a/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx b/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx index 003ac20..2a995a8 100644 --- a/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx +++ b/src/components/dashboard/roadPatrols/reports/Search/ToDateController.jsx @@ -15,6 +15,7 @@ const ToDateController = ({ control }) => { minDate={minDate} error={!!error} value={value} + label={'تاریخ شروع (تا تاریخ)'} placeholder={"تا تاریخ"} setFieldValue={(name, newValue) => { const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); diff --git a/src/components/dashboard/roadPatrols/reports/index.jsx b/src/components/dashboard/roadPatrols/reports/index.jsx index a71eb35..a4b6216 100644 --- a/src/components/dashboard/roadPatrols/reports/index.jsx +++ b/src/components/dashboard/roadPatrols/reports/index.jsx @@ -18,7 +18,7 @@ const ReportPage = () => { const { data: userPermissions } = usePermissions(); const { user } = useAuth(); const requestServer = useRequest(); - const hasProvincesPermission = userPermissions.includes("show-road-patrol-supervise-cartable"); + const hasProvincesPermission = userPermissions?.includes("show-road-patrol-supervise-cartable"); const defaultValues = { from_date: moment(new Date()).format("YYYY-MM-DD"), @@ -66,7 +66,7 @@ const ReportPage = () => { ], filters: data, }); - } catch (e) {} + } catch (e) { } } else { try { params.set("province_id", `${data.province_id}`); @@ -90,13 +90,14 @@ const ReportPage = () => { ], filters: data, }); - } catch (e) {} + } catch (e) { } } }; useEffect(() => { + if (!userPermissions) return onSearchSubmit(defaultValues); - }, []); + }, [userPermissions]); const validationSchema = yup.object().shape({ from_date: yup @@ -119,7 +120,7 @@ const ReportPage = () => { }); return ( - + { const { data: userPermissions } = usePermissions(); - const hasPartialDeletePermission = userPermissions.includes("partial-delete-road-patrol"); - const hasCompleteDeletePermission = userPermissions.includes("complete-delete-road-patrol"); + const hasPartialDeletePermission = userPermissions?.includes("partial-delete-road-patrol"); + const hasCompleteDeletePermission = userPermissions?.includes("complete-delete-road-patrol"); return ( {hasPartialDeletePermission | hasCompleteDeletePermission && ( diff --git a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx index 4d1f366..4829e52 100644 --- a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx @@ -18,7 +18,7 @@ import { useAuth } from "@/lib/contexts/auth"; const SupervisorList = () => { const { data: userPermissions } = usePermissions(); - const hasCountryPermission = userPermissions.includes("show-road-patrol-supervise-cartable"); + const hasCountryPermission = userPermissions?.includes("show-road-patrol-supervise-cartable"); const { user } = useAuth(); const columns = useMemo(() => { const dynamicColumns = { @@ -119,8 +119,8 @@ const SupervisorList = () => { props.dependencyFieldValue?.value === "" ? "empty" : loadingEdaratList - ? "loading" - : props.filterParameters.value + ? "loading" + : props.filterParameters.value } columnSelectOption={getColumnSelectOptions} /> diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index da1a577..5aa47d6 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -485,9 +485,9 @@ export const pageMenu = [ id: "receiptManagmentReport", label: "گزارش ها", type: "page", - route: process.env.NEXT_PUBLIC_API_URL + "/v2/receipt/report", + route: "/dashboard/receipt/report", icon: , - permissions: ["all"], + permissions: ["show-receipt", 'show-receipt-province'], }, ], }, diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index e9631b7..78fe1dd 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -79,6 +79,12 @@ export const GET_IMAGES_ROAD_ITEM = api + "/api/v3/road_items/files"; export const GET_CMMS_MACHINE_ROAD_ITEM = api + "/api/v3/road_items/machines"; export const GET_RAHDARAN_ROAD_ITEM = api + "/api/v3/road_items/rahdaran"; +// recept report +export const GET_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/country_activity"; +export const GET_CITY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_activity"; +export const EXPORT_COUNTRY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/country_activity_excel"; +export const EXPORT_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_activity_excel"; + // damage items export const GET_RECEIPT_DAMAGE_ITEMS_LIST = api + "/api/v3/damages"; export const CREATE_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages"; @@ -88,5 +94,6 @@ export const ATIVITY_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages/activate"; // damage receipt export const GET_TECHNICAL_DAMAGE_OPERATOR_LIST = api + "/api/v3/receipts"; export const GET_DAMAGE_ITEM_LIST = api + "/api/v3/damages/list"; + // activity code log export const ACTIVITY_LOG = api + "/api/v3/profile/add_activity"; From 1e23c6b332cf2a699e17c14d7d9765b165072307 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 23 Feb 2025 13:30:22 +0000 Subject: [PATCH 10/32] Feature/amiriis receipt report --- .../dashboard/damages/report/page.js | 6 + .../damages/report/ExcelPrint/index.jsx | 72 ++++++ .../dashboard/damages/report/ReportLists.jsx | 243 ++++++++++++++++++ .../report/Search/FromDateController.jsx | 28 ++ .../report/Search/SearchReportField.jsx | 45 ++++ .../damages/report/Search/SelectProvince.jsx | 47 ++++ .../report/Search/ToDateController.jsx | 30 +++ .../dashboard/damages/report/Search/index.jsx | 13 + .../dashboard/damages/report/Toolbar.jsx | 11 + .../dashboard/damages/report/index.jsx | 137 ++++++++++ src/core/utils/pageMenu.js | 4 +- src/core/utils/routes.js | 6 + 12 files changed, 640 insertions(+), 2 deletions(-) create mode 100644 src/app/(withAuth)/dashboard/damages/report/page.js create mode 100644 src/components/dashboard/damages/report/ExcelPrint/index.jsx create mode 100644 src/components/dashboard/damages/report/ReportLists.jsx create mode 100644 src/components/dashboard/damages/report/Search/FromDateController.jsx create mode 100644 src/components/dashboard/damages/report/Search/SearchReportField.jsx create mode 100644 src/components/dashboard/damages/report/Search/SelectProvince.jsx create mode 100644 src/components/dashboard/damages/report/Search/ToDateController.jsx create mode 100644 src/components/dashboard/damages/report/Search/index.jsx create mode 100644 src/components/dashboard/damages/report/Toolbar.jsx create mode 100644 src/components/dashboard/damages/report/index.jsx diff --git a/src/app/(withAuth)/dashboard/damages/report/page.js b/src/app/(withAuth)/dashboard/damages/report/page.js new file mode 100644 index 0000000..27d0308 --- /dev/null +++ b/src/app/(withAuth)/dashboard/damages/report/page.js @@ -0,0 +1,6 @@ +import DamagesReportPage from "@/components/dashboard/damages/report" + +const Page = () => { + return +} +export default Page \ No newline at end of file diff --git a/src/components/dashboard/damages/report/ExcelPrint/index.jsx b/src/components/dashboard/damages/report/ExcelPrint/index.jsx new file mode 100644 index 0000000..79ea64f --- /dev/null +++ b/src/components/dashboard/damages/report/ExcelPrint/index.jsx @@ -0,0 +1,72 @@ +"use client"; +import { EXPORT_COUNTRY_RECEIPT_REPORT, EXPORT_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { useTheme } from "@emotion/react"; +import DescriptionIcon from "@mui/icons-material/Description"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; +import FileSaver from "file-saver"; +import moment from "jalali-moment"; +import { useMemo, useState } from "react"; + +const PrintExcel = ({ table, filterData }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [loading, setLoading] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { + if (filter.value) { + acc.push({ id: filter.id, value: filter.value }); + } + return acc; + }, []); + + const filterParams = useMemo(() => { + const params = new URLSearchParams(); + activeFilters.length > 0 && + activeFilters.map((filter, index) => { + params.set(`${filter.id}`, filter.value); + }); + return params; + }, [activeFilters]); + + const clickHandler = () => { + setLoading(true); + const CountryOrProvince = activeFilters.find((item) => item.id === "province_id"); + const requestUrl = + CountryOrProvince.value === "-1" ? EXPORT_COUNTRY_RECEIPT_REPORT : EXPORT_PROVINCE_RECEIPT_REPORT; + requestServer(`${requestUrl}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی کارتابل گزارشات خسارات وارده بر ابنیه فنی تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => { }) + .finally(() => { + setLoading(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + ); +}; +export default PrintExcel; diff --git a/src/components/dashboard/damages/report/ReportLists.jsx b/src/components/dashboard/damages/report/ReportLists.jsx new file mode 100644 index 0000000..b12a9b8 --- /dev/null +++ b/src/components/dashboard/damages/report/ReportLists.jsx @@ -0,0 +1,243 @@ +"use client"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { Box } from "@mui/material"; +import { useMemo } from "react"; +import Toolbar from "./Toolbar"; + +const ReportLists = ({ data, specialFilter }) => { + console.log(data); + + const columns = useMemo(() => { + return [ + { + accessorKey: "edare_name", + header: "اداره کل", + id: "edare_name", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + }, + { + accessorKey: "tedade", + header: "تعداد کل", + id: "tedade", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + header: "تعداد نحوه شناسایی خسارت", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + columns: [ + { + accessorKey: "police_rah", + header: "گزارش پلیس راه", + id: "police_rah", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "gozaresh_gasht", + header: "گزارش گشت راهداری", + id: "gozaresh_gasht", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + ] + }, + { + accessorKey: "kol_sabt_shode", + header: "مبلغ کل خسارات ثبت شده (ریال)", + id: "kol_sabt_shode", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "vasel_shode", + header: "کل مبلغ وصول شده (ریال)", + id: "vasel_shode", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + header: "مبلغ وصول شده به تفکیک نوع پرداخت (ریال)", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + columns: [ + { + accessorKey: "vasel_shode_final", + header: "فیش", + id: "vasel_shode_final", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "vasel_shode_bimeh", + header: "بیمه", + id: "vasel_shode_bimeh", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + { + accessorKey: "vasel_shode_daghi", + header: "داغی", + id: "vasel_shode_daghi", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + muiTableBodyCellProps: { + sx: { + textAlign: "center", + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + return (renderedCellValue / 1).toLocaleString(); + }, + }, + ] + }, + ]; + }, []); + + return ( + + + + ); +}; + +export default ReportLists; diff --git a/src/components/dashboard/damages/report/Search/FromDateController.jsx b/src/components/dashboard/damages/report/Search/FromDateController.jsx new file mode 100644 index 0000000..b18b320 --- /dev/null +++ b/src/components/dashboard/damages/report/Search/FromDateController.jsx @@ -0,0 +1,28 @@ +import { Controller } from "react-hook-form"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import React from "react"; +import moment from "jalali-moment"; + +const FromDateController = ({ control }) => { + return ( + ( + { + const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); + onChange(formattedDate); // Update the field value in react-hook-form + }} + helperText={error ? error.message : null} + /> + )} + /> + ); +}; +export default FromDateController; diff --git a/src/components/dashboard/damages/report/Search/SearchReportField.jsx b/src/components/dashboard/damages/report/Search/SearchReportField.jsx new file mode 100644 index 0000000..ab8442c --- /dev/null +++ b/src/components/dashboard/damages/report/Search/SearchReportField.jsx @@ -0,0 +1,45 @@ +import { Button, Grid, LinearProgress, Typography } from "@mui/material"; +import SearchIcon from "@mui/icons-material/Search"; +import React from "react"; +import FromDateController from "./FromDateController"; +import ToDateController from "./ToDateController"; +import SelectProvince from "./SelectProvince"; +import { Controller } from "react-hook-form"; + +const SearchReportField = ({ control, hasProvincesPermission }) => { + return ( + + + + + + + + {hasProvincesPermission && ( + + + + )} + + ( + + )} + /> + + + ); +}; + +export default SearchReportField; diff --git a/src/components/dashboard/damages/report/Search/SelectProvince.jsx b/src/components/dashboard/damages/report/Search/SelectProvince.jsx new file mode 100644 index 0000000..b1d8a39 --- /dev/null +++ b/src/components/dashboard/damages/report/Search/SelectProvince.jsx @@ -0,0 +1,47 @@ +import { Controller } from "react-hook-form"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import React from "react"; +import { FormControl, FormHelperText, InputLabel, LinearProgress, MenuItem, Select, Typography } from "@mui/material"; +import useProvinces from "@/lib/hooks/useProvince"; + +const SelectProvince = ({ control }) => { + const { provinces, loadingProvinces, errorProvinces } = useProvinces(); + return ( + ( + + استان + + {error && {error.message}} + + )} + /> + ); +}; +export default SelectProvince; diff --git a/src/components/dashboard/damages/report/Search/ToDateController.jsx b/src/components/dashboard/damages/report/Search/ToDateController.jsx new file mode 100644 index 0000000..6f8b0d8 --- /dev/null +++ b/src/components/dashboard/damages/report/Search/ToDateController.jsx @@ -0,0 +1,30 @@ +import { Controller, useWatch } from "react-hook-form"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import React from "react"; +import moment from "jalali-moment"; + +const ToDateController = ({ control }) => { + const minDate = useWatch({ control, name: "from_date" }); + return ( + ( + { + const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); + onChange(formattedDate); // Update the field value in react-hook-form + }} + helperText={error ? error.message : null} + /> + )} + /> + ); +}; +export default ToDateController; diff --git a/src/components/dashboard/damages/report/Search/index.jsx b/src/components/dashboard/damages/report/Search/index.jsx new file mode 100644 index 0000000..f29d302 --- /dev/null +++ b/src/components/dashboard/damages/report/Search/index.jsx @@ -0,0 +1,13 @@ +import StyledForm from "@/core/components/StyledForm"; +import SearchReportField from "./SearchReportField"; + +const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => { + return ( + <> + + + + + ); +}; +export default SearchReportList; diff --git a/src/components/dashboard/damages/report/Toolbar.jsx b/src/components/dashboard/damages/report/Toolbar.jsx new file mode 100644 index 0000000..7dd930f --- /dev/null +++ b/src/components/dashboard/damages/report/Toolbar.jsx @@ -0,0 +1,11 @@ +import PrintExcel from "./ExcelPrint"; +import { Box } from "@mui/material"; + +const Toolbar = ({ table, filterData, mutate }) => { + return ( + + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/damages/report/index.jsx b/src/components/dashboard/damages/report/index.jsx new file mode 100644 index 0000000..4961eb5 --- /dev/null +++ b/src/components/dashboard/damages/report/index.jsx @@ -0,0 +1,137 @@ +'use client' +import PageTitle from "@/core/components/PageTitle"; +import { GET_CITY_RECEIPT_REPORT, GET_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes"; +import { useAuth } from "@/lib/contexts/auth"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { LinearProgress, Stack } from "@mui/material"; +import moment from "jalali-moment"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import * as yup from "yup"; +import SearchReportList from "./Search"; +import ReportLists from "./ReportLists"; + +const DamagesReportPage = () => { + const [data, setData] = useState(null); + const { data: userPermissions } = usePermissions(); + const { user } = useAuth(); + const requestServer = useRequest(); + const hasProvincesPermission = userPermissions?.includes("show-receipt"); + + const defaultValues = { + from_date: moment(new Date()).format("YYYY-MM-DD"), + date_to: moment(new Date()).format("YYYY-MM-DD"), + province_id: hasProvincesPermission ? "-1" : user.province_id, + }; + const defaultFilter = [ + { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]; + const [specialFilter, setSpecialFilter] = useState(defaultFilter); + + const onSearchSubmit = async (data) => { + const params = new URLSearchParams(); + params.set("from_date", `${data.from_date}`); + params.set("date_to", `${data.date_to}`); + setSpecialFilter([ + { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, + { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" }, + { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" }, + ]); + if (data.province_id === "-1") { + try { + const response = await requestServer(`${GET_PROVINCE_RECEIPT_REPORT}?${params}`); + const result = response.data.data; + const nationalReportForItem = result.activities.find((report) => report.province_id == -1); + const nationalReport = { + edare_name: "کل کشور", + ...nationalReportForItem, + }; + + setData({ + data: [ + nationalReport, + ...result.provinces.map((province) => { + const filteredReports = result.activities.find( + (report) => report.province_id == province.id + ); + return { + ...filteredReports, + edare_name: province.name_fa, + }; + }), + ], + filters: data, + }); + } catch (e) { + console.log(e); + } + } else { + try { + params.set("province_id", `${data.province_id}`); + const response = await requestServer(`${GET_CITY_RECEIPT_REPORT}?${params}`); + const result = response.data.data; + const nationalReportForItem = result.activities.find((report) => report.city_id == "-1"); + const nationalReport = { + ...nationalReportForItem, + edare_name: "کل استان", + }; + setData({ + data: [ + nationalReport, + ...result.cities.map((edare) => { + const filteredReports = result.activities.find((report) => report.city_id == edare.id); + return { + edare_name: edare.name_fa, + ...filteredReports, + }; + }), + ], + filters: data, + }); + } catch (e) { } + } + }; + + useEffect(() => { + if (!userPermissions) return + onSearchSubmit(defaultValues); + }, [userPermissions]); + + const validationSchema = yup.object().shape({ + from_date: yup + .string() + .required("تاریخ شروع الزامی است!") + .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!") + .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), + date_to: yup + .string() + .required("تاریخ پایان الزامی است!") + .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!") + .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), + province_id: yup.string().nullable(), + }); + + const { control, handleSubmit } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onBlur", + }); + + return ( + + + + {!data ? : } + + ); +} +export default DamagesReportPage \ No newline at end of file diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index 5aa47d6..19d7689 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -482,10 +482,10 @@ export const pageMenu = [ permissions: ["manage-damage"], }, { - id: "receiptManagmentReport", + id: "damagesManagmentReport", label: "گزارش ها", type: "page", - route: "/dashboard/receipt/report", + route: "/dashboard/damages/report", icon: , permissions: ["show-receipt", 'show-receipt-province'], }, diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 78fe1dd..58bcda3 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -95,5 +95,11 @@ export const ATIVITY_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages/activate"; export const GET_TECHNICAL_DAMAGE_OPERATOR_LIST = api + "/api/v3/receipts"; export const GET_DAMAGE_ITEM_LIST = api + "/api/v3/damages/list"; +// recept report +export const GET_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_report"; +export const GET_CITY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/city_report"; +export const EXPORT_COUNTRY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_excel_report"; +export const EXPORT_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/city_excel_report"; + // activity code log export const ACTIVITY_LOG = api + "/api/v3/profile/add_activity"; From 759bc621d53af6fef96ff681634f1592f6269d8c Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 23 Feb 2025 13:33:21 +0000 Subject: [PATCH 11/32] Feature/amiriis add units --- .../{receipt/damages => damage-items}/page.js | 2 +- .../Actions/Create/Form/CreateFormContent.jsx | 0 .../Actions/Create/Form/index.jsx | 0 .../Actions/Create/index.jsx | 0 .../damages => damageItems}/DamagesList.jsx | 0 .../RowActions/Activity/index.jsx | 0 .../RowActions/Edit/Form/index.jsx | 0 .../RowActions/Edit/index.jsx | 0 .../RowActions/index.jsx | 0 .../damages => damageItems}/Toolbar.jsx | 0 .../damages => damageItems}/index.jsx | 0 src/core/utils/pageMenu.js | 2 +- src/lib/hooks/usePersianInput.js | 49 +++++++++++++++++++ 13 files changed, 51 insertions(+), 2 deletions(-) rename src/app/(withAuth)/dashboard/{receipt/damages => damage-items}/page.js (80%) rename src/components/dashboard/{receipt/damages => damageItems}/Actions/Create/Form/CreateFormContent.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/Actions/Create/Form/index.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/Actions/Create/index.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/DamagesList.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/RowActions/Activity/index.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/RowActions/Edit/Form/index.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/RowActions/Edit/index.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/RowActions/index.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/Toolbar.jsx (100%) rename src/components/dashboard/{receipt/damages => damageItems}/index.jsx (100%) create mode 100644 src/lib/hooks/usePersianInput.js diff --git a/src/app/(withAuth)/dashboard/receipt/damages/page.js b/src/app/(withAuth)/dashboard/damage-items/page.js similarity index 80% rename from src/app/(withAuth)/dashboard/receipt/damages/page.js rename to src/app/(withAuth)/dashboard/damage-items/page.js index 7310363..23e0190 100644 --- a/src/app/(withAuth)/dashboard/receipt/damages/page.js +++ b/src/app/(withAuth)/dashboard/damage-items/page.js @@ -1,4 +1,4 @@ -import DamagesPage from "@/components/dashboard/receipt/damages"; +import DamagesPage from "@/components/dashboard/damageItems"; import WithPermission from "@/core/middlewares/withPermission"; const Page = () => { diff --git a/src/components/dashboard/receipt/damages/Actions/Create/Form/CreateFormContent.jsx b/src/components/dashboard/damageItems/Actions/Create/Form/CreateFormContent.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/Actions/Create/Form/CreateFormContent.jsx rename to src/components/dashboard/damageItems/Actions/Create/Form/CreateFormContent.jsx diff --git a/src/components/dashboard/receipt/damages/Actions/Create/Form/index.jsx b/src/components/dashboard/damageItems/Actions/Create/Form/index.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/Actions/Create/Form/index.jsx rename to src/components/dashboard/damageItems/Actions/Create/Form/index.jsx diff --git a/src/components/dashboard/receipt/damages/Actions/Create/index.jsx b/src/components/dashboard/damageItems/Actions/Create/index.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/Actions/Create/index.jsx rename to src/components/dashboard/damageItems/Actions/Create/index.jsx diff --git a/src/components/dashboard/receipt/damages/DamagesList.jsx b/src/components/dashboard/damageItems/DamagesList.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/DamagesList.jsx rename to src/components/dashboard/damageItems/DamagesList.jsx diff --git a/src/components/dashboard/receipt/damages/RowActions/Activity/index.jsx b/src/components/dashboard/damageItems/RowActions/Activity/index.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/RowActions/Activity/index.jsx rename to src/components/dashboard/damageItems/RowActions/Activity/index.jsx diff --git a/src/components/dashboard/receipt/damages/RowActions/Edit/Form/index.jsx b/src/components/dashboard/damageItems/RowActions/Edit/Form/index.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/RowActions/Edit/Form/index.jsx rename to src/components/dashboard/damageItems/RowActions/Edit/Form/index.jsx diff --git a/src/components/dashboard/receipt/damages/RowActions/Edit/index.jsx b/src/components/dashboard/damageItems/RowActions/Edit/index.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/RowActions/Edit/index.jsx rename to src/components/dashboard/damageItems/RowActions/Edit/index.jsx diff --git a/src/components/dashboard/receipt/damages/RowActions/index.jsx b/src/components/dashboard/damageItems/RowActions/index.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/RowActions/index.jsx rename to src/components/dashboard/damageItems/RowActions/index.jsx diff --git a/src/components/dashboard/receipt/damages/Toolbar.jsx b/src/components/dashboard/damageItems/Toolbar.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/Toolbar.jsx rename to src/components/dashboard/damageItems/Toolbar.jsx diff --git a/src/components/dashboard/receipt/damages/index.jsx b/src/components/dashboard/damageItems/index.jsx similarity index 100% rename from src/components/dashboard/receipt/damages/index.jsx rename to src/components/dashboard/damageItems/index.jsx diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index 19d7689..05dee59 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -477,7 +477,7 @@ export const pageMenu = [ id: "damageItems", label: "آیتم خسارات", type: "page", - route: "/dashboard/receipt/damages", + route: "/dashboard/damage-items", icon: , permissions: ["manage-damage"], }, diff --git a/src/lib/hooks/usePersianInput.js b/src/lib/hooks/usePersianInput.js new file mode 100644 index 0000000..069606f --- /dev/null +++ b/src/lib/hooks/usePersianInput.js @@ -0,0 +1,49 @@ +import { useEffect } from 'react'; +function usePersianInput(inputRef, onChange) { + useEffect(() => { + const input = inputRef.current; + if (!input) return; + + const handleInput = (event) => { + const originalValue = event.target.value; + const filteredValue = originalValue.replace(/[A-Za-z]/g, ''); + if (filteredValue !== originalValue) { + event.target.value = filteredValue; + if (onChange) { + onChange(filteredValue); + } + } else if (onChange) { + onChange(originalValue); + } + }; + + const handleKeyDown = (event) => { + const controlKeys = [ + 'Backspace', + 'Delete', + 'Tab', + 'Escape', + 'Enter', + 'ArrowLeft', + 'ArrowRight', + 'ArrowUp', + 'ArrowDown' + ]; + if (controlKeys.includes(event.key)) return; + + if (/[A-Za-z]/.test(event.key)) { + event.preventDefault(); + } + }; + + input.addEventListener('input', handleInput); + input.addEventListener('keydown', handleKeyDown); + + return () => { + input.removeEventListener('input', handleInput); + input.removeEventListener('keydown', handleKeyDown); + }; + }, [inputRef, onChange]); +} + +export default usePersianInput; \ No newline at end of file From 6da6e5a7f5cb2013c31c4e9404fbbc49589cf5de Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Tue, 25 Feb 2025 10:34:49 +0330 Subject: [PATCH 12/32] change directory --- .../dashboard/damages/report/page.js | 3 +- .../dashboard/receipt/report/page.js | 6 - .../dashboard/damages/report/ReportLists.jsx | 4 +- .../dashboard/damages/report/index.jsx | 6 +- .../receipt/report/ExcelPrint/index.jsx | 72 ------ .../dashboard/receipt/report/ReportLists.jsx | 242 ------------------ .../report/Search/FromDateController.jsx | 28 -- .../report/Search/SearchReportField.jsx | 45 ---- .../receipt/report/Search/SelectProvince.jsx | 47 ---- .../report/Search/ToDateController.jsx | 30 --- .../dashboard/receipt/report/Search/index.jsx | 13 - .../dashboard/receipt/report/Toolbar.jsx | 11 - .../dashboard/receipt/report/index.jsx | 135 ---------- src/core/utils/routes.js | 12 +- 14 files changed, 9 insertions(+), 645 deletions(-) delete mode 100644 src/app/(withAuth)/dashboard/receipt/report/page.js delete mode 100644 src/components/dashboard/receipt/report/ExcelPrint/index.jsx delete mode 100644 src/components/dashboard/receipt/report/ReportLists.jsx delete mode 100644 src/components/dashboard/receipt/report/Search/FromDateController.jsx delete mode 100644 src/components/dashboard/receipt/report/Search/SearchReportField.jsx delete mode 100644 src/components/dashboard/receipt/report/Search/SelectProvince.jsx delete mode 100644 src/components/dashboard/receipt/report/Search/ToDateController.jsx delete mode 100644 src/components/dashboard/receipt/report/Search/index.jsx delete mode 100644 src/components/dashboard/receipt/report/Toolbar.jsx delete mode 100644 src/components/dashboard/receipt/report/index.jsx diff --git a/src/app/(withAuth)/dashboard/damages/report/page.js b/src/app/(withAuth)/dashboard/damages/report/page.js index 27d0308..a9d9507 100644 --- a/src/app/(withAuth)/dashboard/damages/report/page.js +++ b/src/app/(withAuth)/dashboard/damages/report/page.js @@ -1,6 +1,7 @@ import DamagesReportPage from "@/components/dashboard/damages/report" +import WithPermission from "@/core/middlewares/withPermission" const Page = () => { - return + return } export default Page \ No newline at end of file diff --git a/src/app/(withAuth)/dashboard/receipt/report/page.js b/src/app/(withAuth)/dashboard/receipt/report/page.js deleted file mode 100644 index acc26be..0000000 --- a/src/app/(withAuth)/dashboard/receipt/report/page.js +++ /dev/null @@ -1,6 +0,0 @@ -import ReceiptReportPage from "@/components/dashboard/receipt/report" - -const Page = () => { - return -} -export default Page \ No newline at end of file diff --git a/src/components/dashboard/damages/report/ReportLists.jsx b/src/components/dashboard/damages/report/ReportLists.jsx index b12a9b8..c4923c9 100644 --- a/src/components/dashboard/damages/report/ReportLists.jsx +++ b/src/components/dashboard/damages/report/ReportLists.jsx @@ -5,13 +5,11 @@ import { useMemo } from "react"; import Toolbar from "./Toolbar"; const ReportLists = ({ data, specialFilter }) => { - console.log(data); - const columns = useMemo(() => { return [ { accessorKey: "edare_name", - header: "اداره کل", + header: "اداره", id: "edare_name", enableColumnFilter: false, enableSorting: false, diff --git a/src/components/dashboard/damages/report/index.jsx b/src/components/dashboard/damages/report/index.jsx index 4961eb5..2765bb1 100644 --- a/src/components/dashboard/damages/report/index.jsx +++ b/src/components/dashboard/damages/report/index.jsx @@ -1,6 +1,6 @@ 'use client' import PageTitle from "@/core/components/PageTitle"; -import { GET_CITY_RECEIPT_REPORT, GET_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes"; +import { GET_PROVINCE_RECEIPT_REPORT, GET_COUNTRY_RECEIPT_REPORT } from "@/core/utils/routes"; import { useAuth } from "@/lib/contexts/auth"; import { usePermissions } from "@/lib/hooks/usePermissions"; import useRequest from "@/lib/hooks/useRequest"; @@ -43,7 +43,7 @@ const DamagesReportPage = () => { ]); if (data.province_id === "-1") { try { - const response = await requestServer(`${GET_PROVINCE_RECEIPT_REPORT}?${params}`); + const response = await requestServer(`${GET_COUNTRY_RECEIPT_REPORT}?${params}`); const result = response.data.data; const nationalReportForItem = result.activities.find((report) => report.province_id == -1); const nationalReport = { @@ -72,7 +72,7 @@ const DamagesReportPage = () => { } else { try { params.set("province_id", `${data.province_id}`); - const response = await requestServer(`${GET_CITY_RECEIPT_REPORT}?${params}`); + const response = await requestServer(`${GET_PROVINCE_RECEIPT_REPORT}?${params}`); const result = response.data.data; const nationalReportForItem = result.activities.find((report) => report.city_id == "-1"); const nationalReport = { diff --git a/src/components/dashboard/receipt/report/ExcelPrint/index.jsx b/src/components/dashboard/receipt/report/ExcelPrint/index.jsx deleted file mode 100644 index 79ea64f..0000000 --- a/src/components/dashboard/receipt/report/ExcelPrint/index.jsx +++ /dev/null @@ -1,72 +0,0 @@ -"use client"; -import { EXPORT_COUNTRY_RECEIPT_REPORT, EXPORT_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes"; -import useRequest from "@/lib/hooks/useRequest"; -import { useTheme } from "@emotion/react"; -import DescriptionIcon from "@mui/icons-material/Description"; -import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; -import FileSaver from "file-saver"; -import moment from "jalali-moment"; -import { useMemo, useState } from "react"; - -const PrintExcel = ({ table, filterData }) => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down("sm")); - const [loading, setLoading] = useState(false); - const requestServer = useRequest({ notificationSuccess: true }); - const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { - if (filter.value) { - acc.push({ id: filter.id, value: filter.value }); - } - return acc; - }, []); - - const filterParams = useMemo(() => { - const params = new URLSearchParams(); - activeFilters.length > 0 && - activeFilters.map((filter, index) => { - params.set(`${filter.id}`, filter.value); - }); - return params; - }, [activeFilters]); - - const clickHandler = () => { - setLoading(true); - const CountryOrProvince = activeFilters.find((item) => item.id === "province_id"); - const requestUrl = - CountryOrProvince.value === "-1" ? EXPORT_COUNTRY_RECEIPT_REPORT : EXPORT_PROVINCE_RECEIPT_REPORT; - requestServer(`${requestUrl}?${filterParams}`, "get", { - requestOptions: { responseType: "blob" }, - }) - .then((response) => { - const filename = `خروجی کارتابل گزارشات خسارات وارده بر ابنیه فنی تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; - FileSaver.saveAs(response.data, filename); - }) - .catch(() => { }) - .finally(() => { - setLoading(false); - }); - }; - - return ( - <> - {isMobile ? ( - - - - ) : ( - - )} - - ); -}; -export default PrintExcel; diff --git a/src/components/dashboard/receipt/report/ReportLists.jsx b/src/components/dashboard/receipt/report/ReportLists.jsx deleted file mode 100644 index 4386ac6..0000000 --- a/src/components/dashboard/receipt/report/ReportLists.jsx +++ /dev/null @@ -1,242 +0,0 @@ -"use client"; -import DataTableWithAuth from "@/core/components/DataTableWithAuth"; -import { Box } from "@mui/material"; -import { useMemo } from "react"; -import Toolbar from "./Toolbar"; - -const ReportLists = ({ data, specialFilter }) => { - const columns = useMemo(() => { - return [ - { - accessorKey: "edare_name", - header: "اداره کل", - id: "edare_name", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - }, - { - accessorKey: "tedade", - header: "تعداد کل", - id: "tedade", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - { - header: "تعداد نحوه شناسایی خسارت", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - columns: [ - { - accessorKey: "policeCount", - header: "گزارش پلیس راه", - id: "policeCount", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - { - accessorKey: "gashtCount", - header: "گزارش گشت راهداری", - id: "gashtCount", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - ] - }, - { - accessorKey: "kesartAll", - header: "مبلغ کل خسارات ثبت شده (ریال)", - id: "kesartAll", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - { - accessorKey: "vosolAll", - header: "کل مبلغ وصول شده (ریال)", - id: "vosolAll", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - { - header: "مبلغ وصول شده به تفکیک نوع پرداخت (ریال)", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - columns: [ - { - accessorKey: "fish", - header: "فیش", - id: "fish", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - { - accessorKey: "bime", - header: "بیمه", - id: "bime", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - { - accessorKey: "daghi", - header: "داغی", - id: "daghi", - enableColumnFilter: false, - enableSorting: false, - datatype: "text", - grow: false, - size: 50, - muiTableBodyCellProps: { - sx: { - textAlign: "center", - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - return (renderedCellValue / 1).toLocaleString(); - }, - }, - ] - }, - ]; - }, []); - - return ( - - - - ); -}; - -export default ReportLists; diff --git a/src/components/dashboard/receipt/report/Search/FromDateController.jsx b/src/components/dashboard/receipt/report/Search/FromDateController.jsx deleted file mode 100644 index b18b320..0000000 --- a/src/components/dashboard/receipt/report/Search/FromDateController.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Controller } from "react-hook-form"; -import MuiDatePicker from "@/core/components/MuiDatePicker"; -import React from "react"; -import moment from "jalali-moment"; - -const FromDateController = ({ control }) => { - return ( - ( - { - const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); - onChange(formattedDate); // Update the field value in react-hook-form - }} - helperText={error ? error.message : null} - /> - )} - /> - ); -}; -export default FromDateController; diff --git a/src/components/dashboard/receipt/report/Search/SearchReportField.jsx b/src/components/dashboard/receipt/report/Search/SearchReportField.jsx deleted file mode 100644 index ab8442c..0000000 --- a/src/components/dashboard/receipt/report/Search/SearchReportField.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Button, Grid, LinearProgress, Typography } from "@mui/material"; -import SearchIcon from "@mui/icons-material/Search"; -import React from "react"; -import FromDateController from "./FromDateController"; -import ToDateController from "./ToDateController"; -import SelectProvince from "./SelectProvince"; -import { Controller } from "react-hook-form"; - -const SearchReportField = ({ control, hasProvincesPermission }) => { - return ( - - - - - - - - {hasProvincesPermission && ( - - - - )} - - ( - - )} - /> - - - ); -}; - -export default SearchReportField; diff --git a/src/components/dashboard/receipt/report/Search/SelectProvince.jsx b/src/components/dashboard/receipt/report/Search/SelectProvince.jsx deleted file mode 100644 index b1d8a39..0000000 --- a/src/components/dashboard/receipt/report/Search/SelectProvince.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Controller } from "react-hook-form"; -import MuiDatePicker from "@/core/components/MuiDatePicker"; -import React from "react"; -import { FormControl, FormHelperText, InputLabel, LinearProgress, MenuItem, Select, Typography } from "@mui/material"; -import useProvinces from "@/lib/hooks/useProvince"; - -const SelectProvince = ({ control }) => { - const { provinces, loadingProvinces, errorProvinces } = useProvinces(); - return ( - ( - - استان - - {error && {error.message}} - - )} - /> - ); -}; -export default SelectProvince; diff --git a/src/components/dashboard/receipt/report/Search/ToDateController.jsx b/src/components/dashboard/receipt/report/Search/ToDateController.jsx deleted file mode 100644 index 6f8b0d8..0000000 --- a/src/components/dashboard/receipt/report/Search/ToDateController.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Controller, useWatch } from "react-hook-form"; -import MuiDatePicker from "@/core/components/MuiDatePicker"; -import React from "react"; -import moment from "jalali-moment"; - -const ToDateController = ({ control }) => { - const minDate = useWatch({ control, name: "from_date" }); - return ( - ( - { - const formattedDate = moment(new Date(newValue)).locale("en").format("YYYY-MM-DD"); - onChange(formattedDate); // Update the field value in react-hook-form - }} - helperText={error ? error.message : null} - /> - )} - /> - ); -}; -export default ToDateController; diff --git a/src/components/dashboard/receipt/report/Search/index.jsx b/src/components/dashboard/receipt/report/Search/index.jsx deleted file mode 100644 index f29d302..0000000 --- a/src/components/dashboard/receipt/report/Search/index.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import StyledForm from "@/core/components/StyledForm"; -import SearchReportField from "./SearchReportField"; - -const SearchReportList = ({ handleSubmit, onSearchSubmit, control, hasProvincesPermission }) => { - return ( - <> - - - - - ); -}; -export default SearchReportList; diff --git a/src/components/dashboard/receipt/report/Toolbar.jsx b/src/components/dashboard/receipt/report/Toolbar.jsx deleted file mode 100644 index 7dd930f..0000000 --- a/src/components/dashboard/receipt/report/Toolbar.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import PrintExcel from "./ExcelPrint"; -import { Box } from "@mui/material"; - -const Toolbar = ({ table, filterData, mutate }) => { - return ( - - - - ); -}; -export default Toolbar; diff --git a/src/components/dashboard/receipt/report/index.jsx b/src/components/dashboard/receipt/report/index.jsx deleted file mode 100644 index 3efedfe..0000000 --- a/src/components/dashboard/receipt/report/index.jsx +++ /dev/null @@ -1,135 +0,0 @@ -'use client' -import PageTitle from "@/core/components/PageTitle"; -import { GET_CITY_RECEIPT_REPORT, GET_PROVINCE_RECEIPT_REPORT } from "@/core/utils/routes"; -import { useAuth } from "@/lib/contexts/auth"; -import { usePermissions } from "@/lib/hooks/usePermissions"; -import useRequest from "@/lib/hooks/useRequest"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { LinearProgress, Stack } from "@mui/material"; -import moment from "jalali-moment"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import * as yup from "yup"; -import SearchReportList from "./Search"; -import ReportLists from "./ReportLists"; - -const ReceiptReportPage = () => { - const [data, setData] = useState([]); - const { data: userPermissions } = usePermissions(); - const { user } = useAuth(); - const requestServer = useRequest(); - const hasProvincesPermission = userPermissions?.includes("show-receipt"); - - const defaultValues = { - from_date: moment(new Date()).format("YYYY-MM-DD"), - date_to: moment(new Date()).format("YYYY-MM-DD"), - province_id: hasProvincesPermission ? "-1" : user.province_id, - }; - const defaultFilter = [ - { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, - { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" }, - { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" }, - ]; - const [specialFilter, setSpecialFilter] = useState(defaultFilter); - - const onSearchSubmit = async (data) => { - const params = new URLSearchParams(); - params.set("from_date", `${data.from_date}`); - params.set("date_to", `${data.date_to}`); - setSpecialFilter([ - { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, - { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" }, - { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" }, - ]); - if (data.province_id === "-1") { - try { - const response = await requestServer(`${GET_PROVINCE_RECEIPT_REPORT}?${params}`); - const result = response.data.data; - const nationalReportForItem = result.activities.find((report) => report.province_id === -1); - const nationalReport = { - edare_name: "کل کشور", - ...nationalReportForItem, - }; - - setData({ - data: [ - nationalReport, - ...result.provinces.map((province) => { - const filteredReports = result.activities.find( - (report) => report.province_id === province.id - ); - return { - ...filteredReports, - edare_name: province.name_fa, - }; - }), - ], - filters: data, - }); - } catch (e) { } - } else { - try { - params.set("province_id", `${data.province_id}`); - const response = await requestServer(`${GET_CITY_RECEIPT_REPORT}?${params}`); - const result = response.data.data; - const nationalReportForItem = result.activities.find((report) => report.edare_id === "-1"); - const nationalReport = { - ...nationalReportForItem, - edare_name: "کل استان", - }; - setData({ - data: [ - nationalReport, - ...result.edarateShahri.map((edare) => { - const filteredReports = result.activities.find((report) => report.edare_id == edare.id); - return { - edare_name: edare.name_fa, - ...filteredReports, - }; - }), - ], - filters: data, - }); - } catch (e) { } - } - }; - - useEffect(() => { - if (!userPermissions) return - onSearchSubmit(defaultValues); - }, [userPermissions]); - - const validationSchema = yup.object().shape({ - from_date: yup - .string() - .required("تاریخ شروع الزامی است!") - .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ شروع الزامی است!") - .test("is-valid-date", "تاریخ شروع معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), - date_to: yup - .string() - .required("تاریخ پایان الزامی است!") - .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ پایان الزامی است!") - .test("is-valid-date", "تاریخ پایان معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), - province_id: yup.string().nullable(), - }); - - const { control, handleSubmit } = useForm({ - defaultValues, - resolver: yupResolver(validationSchema), - mode: "onBlur", - }); - - return ( - - - - {!data ? : } - - ); -} -export default ReceiptReportPage \ No newline at end of file diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 58bcda3..6dc868c 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -79,12 +79,6 @@ export const GET_IMAGES_ROAD_ITEM = api + "/api/v3/road_items/files"; export const GET_CMMS_MACHINE_ROAD_ITEM = api + "/api/v3/road_items/machines"; export const GET_RAHDARAN_ROAD_ITEM = api + "/api/v3/road_items/rahdaran"; -// recept report -export const GET_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/country_activity"; -export const GET_CITY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_activity"; -export const EXPORT_COUNTRY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/country_activity_excel"; -export const EXPORT_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_activity_excel"; - // damage items export const GET_RECEIPT_DAMAGE_ITEMS_LIST = api + "/api/v3/damages"; export const CREATE_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages"; @@ -96,10 +90,10 @@ export const GET_TECHNICAL_DAMAGE_OPERATOR_LIST = api + "/api/v3/receipts"; export const GET_DAMAGE_ITEM_LIST = api + "/api/v3/damages/list"; // recept report +export const GET_COUNTRY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/country_report"; export const GET_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_report"; -export const GET_CITY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/city_report"; -export const EXPORT_COUNTRY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_excel_report"; -export const EXPORT_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/city_excel_report"; +export const EXPORT_COUNTRY_RECEIPT_REPORT = api + "/api/v3/receipt_reports/country_excel_report"; +export const EXPORT_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/province_excel_report"; // activity code log export const ACTIVITY_LOG = api + "/api/v3/profile/add_activity"; From 597240bfccb8d102987cee2b2f0c89cf080eb704 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Tue, 25 Feb 2025 08:17:35 +0000 Subject: [PATCH 13/32] Feature/insurance registration --- .../(withAuth)/dashboard/damage-items/page.js | 4 +- .../operator/page.js | 2 +- .../damageItems/RowActions/Activity/index.jsx | 8 +- .../damageItems/RowActions/index.jsx | 2 +- .../Actions/create/Forms/CityController.jsx | 0 .../create/Forms/CreateFormContent.jsx | 86 ++-- .../Actions/create/Forms/DamageAmount.jsx | 10 +- .../Actions/create/Forms/DamageInfo.jsx | 6 +- .../Actions/create/Forms/DamageItem.jsx | 63 ++- .../Actions/create/Forms/DamageItemInfo.jsx | 60 +++ .../Actions/create/Forms/DamagePrice.jsx | 6 +- .../Actions/create/Forms/DamageReport.jsx | 34 +- .../Actions/create/Forms/ImageUpload.jsx | 77 ++++ .../Actions/create/Forms/PoliceFileReport.jsx | 20 +- .../operator/Actions/create/Forms/index.jsx | 86 ++++ .../operator/Actions/create/index.jsx | 0 .../damages/operator/ExcelPrint/index.jsx | 75 ++++ .../Form/CreateFactor/CreateFactorContent.jsx | 208 +++++++++ .../operator/Form/CreateFactor/index.jsx | 39 ++ .../operator/Form/EditForm/EditController.jsx | 113 +++++ .../damages/operator/Form/EditForm/index.jsx | 45 ++ .../Form/RegisterInsurance/DaghiFile.jsx | 47 ++ .../Form/RegisterInsurance/DaghiPayment.jsx | 53 +++ .../Form/RegisterInsurance}/ImageUpload.jsx | 0 .../Form/RegisterInsurance/InsuranceFile.jsx | 47 ++ .../RegisterInsurance/InsurancePayment.jsx | 52 +++ .../RegisterInsuranceContent.jsx | 202 +++++++++ .../operator/Form/RegisterInsurance/index.jsx | 37 ++ .../damages/operator/OperatorList.jsx | 403 ++++++++++++++++++ .../DamageItemDialog/DamageItemContent.jsx | 76 ++++ .../RowActions/DamageItemDialog/index.jsx | 43 ++ .../RowActions/DeleteDialog/DeleteContent.jsx | 39 ++ .../RowActions/DeleteDialog/index.jsx | 32 ++ .../ImageDialog/ImageFormContent.jsx | 0 .../operator/RowActions/ImageDialog/index.jsx | 0 .../LocationForm/LocationFormContent.jsx | 0 .../RowActions/LocationForm/index.jsx | 0 .../PoliceRahLetterContent.jsx | 41 ++ .../RowActions/PoliceRahLetter/index.jsx | 32 ++ .../SendToInsuranceContent.jsx | 41 ++ .../RowActions/SendToInsurance/index.jsx | 36 ++ .../operator/RowActions/ShowPlate/index.jsx | 4 +- .../damages/operator/RowActions/index.jsx | 27 ++ .../operator/Toolbar.jsx | 2 + .../operator/index.jsx | 0 .../Actions/ObservedGashtCreate/TableInfo.jsx | 2 +- .../reports/ItemsReports/ExcelPrint/index.jsx | 2 +- .../reports/ItemsReports/ReportLists.jsx | 2 +- .../roadItems/reports/ItemsReports/index.jsx | 2 +- .../SubItemsReports/Search/SelectItems.jsx | 4 +- .../reports/SubItemsReports/index.jsx | 2 +- .../Actions/create/Forms/DamageItemInfo.jsx | 43 -- .../operator/Actions/create/Forms/index.jsx | 12 - .../operator/OperatorList.jsx | 356 ---------------- .../operator/RowActions/index.jsx | 6 - src/core/utils/pageMenu.js | 4 +- src/core/utils/routes.js | 11 + 57 files changed, 2075 insertions(+), 529 deletions(-) rename src/app/(withAuth)/dashboard/{technical-building-damage => damages}/operator/page.js (81%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/CityController.jsx (100%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/CreateFormContent.jsx (77%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/DamageAmount.jsx (87%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/DamageInfo.jsx (97%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/DamageItem.jsx (75%) create mode 100644 src/components/dashboard/damages/operator/Actions/create/Forms/DamageItemInfo.jsx rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/DamagePrice.jsx (91%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/DamageReport.jsx (79%) create mode 100644 src/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload.jsx rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/Forms/PoliceFileReport.jsx (83%) create mode 100644 src/components/dashboard/damages/operator/Actions/create/Forms/index.jsx rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Actions/create/index.jsx (100%) create mode 100644 src/components/dashboard/damages/operator/ExcelPrint/index.jsx create mode 100644 src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx create mode 100644 src/components/dashboard/damages/operator/Form/CreateFactor/index.jsx create mode 100644 src/components/dashboard/damages/operator/Form/EditForm/EditController.jsx create mode 100644 src/components/dashboard/damages/operator/Form/EditForm/index.jsx create mode 100644 src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiFile.jsx create mode 100644 src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiPayment.jsx rename src/components/dashboard/{technicalBuildingDamage/operator/Actions/create/Forms => damages/operator/Form/RegisterInsurance}/ImageUpload.jsx (100%) create mode 100644 src/components/dashboard/damages/operator/Form/RegisterInsurance/InsuranceFile.jsx create mode 100644 src/components/dashboard/damages/operator/Form/RegisterInsurance/InsurancePayment.jsx create mode 100644 src/components/dashboard/damages/operator/Form/RegisterInsurance/RegisterInsuranceContent.jsx create mode 100644 src/components/dashboard/damages/operator/Form/RegisterInsurance/index.jsx create mode 100644 src/components/dashboard/damages/operator/OperatorList.jsx create mode 100644 src/components/dashboard/damages/operator/RowActions/DamageItemDialog/DamageItemContent.jsx create mode 100644 src/components/dashboard/damages/operator/RowActions/DamageItemDialog/index.jsx create mode 100644 src/components/dashboard/damages/operator/RowActions/DeleteDialog/DeleteContent.jsx create mode 100644 src/components/dashboard/damages/operator/RowActions/DeleteDialog/index.jsx rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/RowActions/ImageDialog/ImageFormContent.jsx (100%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/RowActions/ImageDialog/index.jsx (100%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/RowActions/LocationForm/LocationFormContent.jsx (100%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/RowActions/LocationForm/index.jsx (100%) create mode 100644 src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/PoliceRahLetterContent.jsx create mode 100644 src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/index.jsx create mode 100644 src/components/dashboard/damages/operator/RowActions/SendToInsurance/SendToInsuranceContent.jsx create mode 100644 src/components/dashboard/damages/operator/RowActions/SendToInsurance/index.jsx rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/RowActions/ShowPlate/index.jsx (90%) create mode 100644 src/components/dashboard/damages/operator/RowActions/index.jsx rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/Toolbar.jsx (73%) rename src/components/dashboard/{technicalBuildingDamage => damages}/operator/index.jsx (100%) delete mode 100644 src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItemInfo.jsx delete mode 100644 src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/index.jsx delete mode 100644 src/components/dashboard/technicalBuildingDamage/operator/OperatorList.jsx delete mode 100644 src/components/dashboard/technicalBuildingDamage/operator/RowActions/index.jsx diff --git a/src/app/(withAuth)/dashboard/damage-items/page.js b/src/app/(withAuth)/dashboard/damage-items/page.js index 23e0190..1a222ae 100644 --- a/src/app/(withAuth)/dashboard/damage-items/page.js +++ b/src/app/(withAuth)/dashboard/damage-items/page.js @@ -3,9 +3,7 @@ import WithPermission from "@/core/middlewares/withPermission"; const Page = () => { return ( - + ); diff --git a/src/app/(withAuth)/dashboard/technical-building-damage/operator/page.js b/src/app/(withAuth)/dashboard/damages/operator/page.js similarity index 81% rename from src/app/(withAuth)/dashboard/technical-building-damage/operator/page.js rename to src/app/(withAuth)/dashboard/damages/operator/page.js index 42644d6..8715b11 100644 --- a/src/app/(withAuth)/dashboard/technical-building-damage/operator/page.js +++ b/src/app/(withAuth)/dashboard/damages/operator/page.js @@ -1,6 +1,6 @@ import WithPermission from "@/core/middlewares/withPermission"; import ActivityCodeLog from "@/core/components/ActivityCodeLog"; -import OperatorPage from "@/components/dashboard/technicalBuildingDamage/operator"; +import OperatorPage from "@/components/dashboard/damages/operator"; const Page = () => { return ( diff --git a/src/components/dashboard/damageItems/RowActions/Activity/index.jsx b/src/components/dashboard/damageItems/RowActions/Activity/index.jsx index 4f64584..3683899 100644 --- a/src/components/dashboard/damageItems/RowActions/Activity/index.jsx +++ b/src/components/dashboard/damageItems/RowActions/Activity/index.jsx @@ -4,14 +4,14 @@ import { Switch } from "@mui/material"; import { useState } from "react"; const Activity = ({ rowId, mutate, status }) => { - const [disabled, setDisabled] = useState(false) + const [disabled, setDisabled] = useState(false); const [checked, setChecked] = useState(status == 1); const requestServer = useRequest(); const handleSwitch = (event) => { const newChecked = event.target.checked; setChecked(newChecked); - setDisabled(true) + setDisabled(true); requestServer(`${ATIVITY_RECEIPT_DAMAGE_ITEMS}/${rowId}`, "post") .then(() => { @@ -21,8 +21,8 @@ const Activity = ({ rowId, mutate, status }) => { setChecked(!newChecked); }) .finally(() => { - setDisabled(false) - }) + setDisabled(false); + }); }; return ; diff --git a/src/components/dashboard/damageItems/RowActions/index.jsx b/src/components/dashboard/damageItems/RowActions/index.jsx index 18aa6ea..8fbaea1 100644 --- a/src/components/dashboard/damageItems/RowActions/index.jsx +++ b/src/components/dashboard/damageItems/RowActions/index.jsx @@ -4,7 +4,7 @@ import Edit from "./Edit"; const RowActions = ({ row, mutate }) => { return ( - + diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CityController.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/CityController.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CityController.jsx rename to src/components/dashboard/damages/operator/Actions/create/Forms/CityController.jsx diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CreateFormContent.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx similarity index 77% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CreateFormContent.jsx rename to src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx index d7ae67c..bf369b0 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CreateFormContent.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx @@ -2,8 +2,7 @@ import React, { useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import { array, mixed, number, object, string } from "yup"; -import { useTheme } from "@emotion/react"; -import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material"; +import { Box, Button, DialogActions, DialogContent, Tab, Tabs } from "@mui/material"; import StyledForm from "@/core/components/StyledForm"; import FileCopyIcon from "@mui/icons-material/FileCopy"; import InfoIcon from "@mui/icons-material/Info"; @@ -44,7 +43,7 @@ const validationSchema = object({ .required("لطفا کد ملی را وارد کنید!!!"), start_point: mixed() .test("start-point-required", "لطفاً نقطه تصادف را مشخص کنید!", function (value) { - return !!value; // چک می‌کند که مقدار موجود است + return !!value; }) .required("لطفاً نقطه تصادف را مشخص کنید!"), plate_part1: mixed() @@ -59,50 +58,50 @@ const validationSchema = object({ .required("plate_part4الزامیست"), damage_picture1: mixed().nullable().required("لطفا عکس تصادف را بارگذاری کنید!"), damage_picture2: mixed().nullable().required("لطفا عکس تصادف را بارگذاری کنید!"), - police_serial: string().when("police_file_checkbox", { + police_serial: string().when("radio_button", { is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد then: (schema) => schema.required("شماره کروکی یا نامه پلیس راه الزامی است"), }), - police_file_date: string().when("police_file_checkbox", { + police_file_date: string().when("radio_button", { is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد then: (schema) => schema.required("تاریخ کروکی یا نامه پلیس راه الزامی است"), }), - police_file: string().when("police_file_checkbox", { + police_file: mixed().when("radio_button", { is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد - then: (schema) => schema.required("تصویر کروکی یا نامه پلیس راه الزامی است"), + then: (schema) => schema.nullable().required("تصویر کروکی یا نامه پلیس راه الزامی است"), + otherwise: (schema) => schema.notRequired(), }), - items_damage: array().min(1, "حداقل یک ایتم خسارت ضروریست!!!"), + items_damage: array().min(1, "حداقل یک آیتم خسارت ضروریست!!!"), }); -const CreateFormContent = ({ setOpen, mutate }) => { +const CreateFormContent = ({ setOpen, SubmitDamage, defaultData }) => { const defaultValues = { - accident_date: "", - accident_time: "", - accident_type: "", - province_id: null, - city_id: null, - axis_name: "", - driver_name: "", - phone_number: "", - national_code: "", - plate_part1: "", - plate_part2: "الف", - plate_part3: "", - plate_part4: "", - damage_picture1: null, - damage_picture2: null, - report_base: "", - police_file_checkbox: "", - police_file: "", - police_file_date: "", - police_serial: "", - start_point: "", - items_damage: [], + accident_date: defaultData.accident_date, + accident_time: defaultData.accident_time, + accident_type: defaultData.accident_type, + province_id: defaultData.province_id, + city_id: defaultData.city_id, + axis_name: defaultData.axis_name, + driver_name: defaultData.driver_name, + phone_number: defaultData.phone_number, + national_code: defaultData.national_code, + plate_part1: defaultData.plate_part1, + plate_part2: defaultData.plate_part2, + plate_part3: defaultData.plate_part3, + plate_part4: defaultData.plate_part4, + radio_button: defaultData.radio_button, + damage_picture1: defaultData.damage_picture1, + damage_picture2: defaultData.damage_picture2, + report_base: defaultData.report_base, + police_file_checkbox: defaultData.police_file_checkbox, + police_file: defaultData.police_file, + police_file_date: defaultData.police_file_date, + police_serial: defaultData.police_serial, + start_point: defaultData.start_point, + items_damage: defaultData.items_damage, }; - const [tabState, setTabState] = useState(1); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [tabState, setTabState] = useState(0); const handleClose = () => setOpen(false); const handleChangeTab = (event, newValue) => setTabState(newValue); const handlePrev = () => { @@ -124,7 +123,7 @@ const CreateFormContent = ({ setOpen, mutate }) => { mode: "all", }); const onSubmitBase = async (data) => { - console.log(data); + await SubmitDamage(data); }; const handleNext = async () => { let fieldsToValidate = []; @@ -163,7 +162,7 @@ const CreateFormContent = ({ setOpen, mutate }) => { allowScrollButtonsMobile value={tabState} onChange={handleChangeTab} - variant={`${isMobile ? "scrollable" : "fullWidth"}`} + variant="fullWidth" sx={{ display: "flex", justifyContent: "center", @@ -174,7 +173,7 @@ const CreateFormContent = ({ setOpen, mutate }) => { > } label="اطلاعات تصادف" /> } label="گزارش میزان خسارت" /> - } label="ایتم خسارت" /> + } label="آیتم خسارت" /> @@ -189,7 +188,13 @@ const CreateFormContent = ({ setOpen, mutate }) => { name={"items_damage"} control={control} render={({ field, fieldState: { error } }) => { - return ; + return ( + + ); }} /> @@ -198,6 +203,7 @@ const CreateFormContent = ({ setOpen, mutate }) => { )} diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageAmount.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageAmount.jsx similarity index 87% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageAmount.jsx rename to src/components/dashboard/damages/operator/Actions/create/Forms/DamageAmount.jsx index b2c7b90..bd8c646 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageAmount.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageAmount.jsx @@ -4,27 +4,27 @@ import NumberField from "@/core/components/NumberField"; import { useEffect } from "react"; const DamageAmount = ({ control, damageItemList, setValue }) => { - const itemDamage = useWatch({ control, name: "items_damge_id" }); - const itemValue = useWatch({ control, name: "items_damge_value" }); + const itemDamage = useWatch({ control, name: "id" }); + const itemValue = useWatch({ control, name: "value" }); const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage); const isDisabled = itemDamageId?.base_price ? itemDamageId?.base_price !== 0 : false; const calculatedValue = itemValue * (itemDamageId?.base_price ? itemDamageId?.base_price : 0); useEffect(() => { if (isDisabled) { - setValue("items_damge_amount", calculatedValue); + setValue("amount", calculatedValue); } }, [itemValue, itemDamageId, isDisabled, setValue]); return ( { return ( { + const StartPoint = useWatch({ control, name: "start_point" }); const { provinces, errorProvinces, loadingProvinces } = useProvinces(); return ( @@ -263,6 +264,7 @@ const DamageInfo = ({ control, setValue, errors }) => { diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItem.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx similarity index 75% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItem.jsx rename to src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx index 3a5de73..e52c280 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageItem.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx @@ -1,5 +1,17 @@ -import { Autocomplete, Box, Button, Chip, Divider, Fade, Grid, Stack, TextField, Typography } from "@mui/material"; -import { Controller, useController, useForm } from "react-hook-form"; +import { + Autocomplete, + Box, + Button, + Chip, + Divider, + Fade, + Grid, + Skeleton, + Stack, + TextField, + Typography, +} from "@mui/material"; +import { Controller, useForm, useWatch } from "react-hook-form"; import LinearProgress from "@mui/material/LinearProgress"; import { useEffect, useState } from "react"; import DamageItemInfo from "./DamageItemInfo"; @@ -9,17 +21,30 @@ import DamagePrice from "./DamagePrice"; import { yupResolver } from "@hookform/resolvers/yup"; const schema = object({ - items_damge_id: number().required("لطفا نوع خسارت را وارد کنید!!!"), - items_damge_value: number().required("لطفا میزان خسارت را وارد کنید!!!"), - items_damge_amount: number().required("لطفا هزینه خسارت را وارد کنید!!!"), + id: number().required("لطفا نوع خسارت را وارد کنید!!!"), + value: number().required("لطفا میزان خسارت را وارد کنید!!!"), + amount: number().required("لطفا هزینه خسارت را وارد کنید!!!"), }); const defaultValues = { - items_damge_id: null, - items_damge_value: null, - items_damge_amount: null, + id: null, + value: null, + amount: null, }; -const DamageItem = ({ baseOnChange, getValues }) => { - const [selectedDamageItemList, setSelectedDamageItemList] = useState([]); +const DamageItem = ({ baseOnChange, baseDamageItems }) => { + const flattenBaseDamageItems = (items) => { + return items.map((item) => ({ + id: item.id, + title: item.title, + unit: item.unit, + base_price: item.base_price, + status: item.status, + until_27_shahrivar: item.until_27_shahrivar, + ...item.pivot, // اضافه کردن مقادیر موجود در pivot به سطح بالا + })); + }; + const [selectedDamageItemList, setSelectedDamageItemList] = useState( + baseDamageItems.length !== 0 ? flattenBaseDamageItems(baseDamageItems) : [] + ); const { damageItemList, loadingDamageItemList, errorDamageItemList } = useDamageItemList(); const { control, handleSubmit, reset, setValue } = useForm({ defaultValues, @@ -27,14 +52,14 @@ const DamageItem = ({ baseOnChange, getValues }) => { mode: "onBlur", }); const deleteDamageItem = (id) => { - setSelectedDamageItemList((prev) => prev.filter((DamageItem) => DamageItem.items_damge_id !== id)); + setSelectedDamageItemList((prev) => prev.filter((DamageItem) => DamageItem.id !== id)); }; useEffect(() => { baseOnChange(selectedDamageItemList); }, [selectedDamageItemList]); const handleCreateDamage = (data) => { setSelectedDamageItemList((prev) => { - const isDuplicate = prev.some((item) => item.items_damge_id === data.items_damge_id); + const isDuplicate = prev.some((item) => item.id === data.id); if (!isDuplicate) { return [...prev, data]; } else { @@ -55,7 +80,7 @@ const DamageItem = ({ baseOnChange, getValues }) => { <> ( { )} - + {selectedDamageItemList.length === 0 && ( @@ -118,7 +143,12 @@ const DamageItem = ({ baseOnChange, getValues }) => { overflowY: "auto", }} > - {selectedDamageItemList.length !== 0 && + {loadingDamageItemList ? ( + + + + ) : ( + selectedDamageItemList.length !== 0 && selectedDamageItemList.map((selectedDamageItem, index) => ( { deleteDamageItem={deleteDamageItem} /> - ))} + )) + )} diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItemInfo.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItemInfo.jsx new file mode 100644 index 0000000..ca95574 --- /dev/null +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItemInfo.jsx @@ -0,0 +1,60 @@ +import { Box, Card, IconButton, Stack, Typography } from "@mui/material"; +import DeleteIcon from "@mui/icons-material/Delete"; +import React from "react"; +import MinorCrashIcon from "@mui/icons-material/MinorCrash"; + +const DamageItemInfo = ({ deleteDamageItem, selectedDamageItem, damageItemList }) => { + const itemDamageId = damageItemList.find((item) => item.id == selectedDamageItem.id); + return ( + + + + + + {itemDamageId.title} + + + + میزان خسارت ({itemDamageId.unit}) :{" "} + + {(selectedDamageItem.value / 1).toLocaleString()} + + + + + هزینه خسارت (ریال) :{" "} + + {(selectedDamageItem.amount / 1).toLocaleString()} + + + + + + + deleteDamageItem(selectedDamageItem.id)}> + + + + + ); +}; +export default DamageItemInfo; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamagePrice.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamagePrice.jsx similarity index 91% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamagePrice.jsx rename to src/components/dashboard/damages/operator/Actions/create/Forms/DamagePrice.jsx index 02fe21a..96ff80a 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamagePrice.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamagePrice.jsx @@ -3,14 +3,14 @@ import { Grid, TextField } from "@mui/material"; import DamageAmount from "./DamageAmount"; const DamagePrice = ({ damageItemList, control, setValue }) => { - const itemDamage = useWatch({ control, name: "items_damge_id" }); + const itemDamage = useWatch({ control, name: "id" }); const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage); return ( <> { return ( @@ -23,7 +23,7 @@ const DamagePrice = ({ damageItemList, control, setValue }) => { min: 0, pattern: "[0-9]*", }} - name="items_damge_value" + name="value" onChange={(event) => { const inputValue = event.target.value; if (isNaN(Number(inputValue))) { diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageReport.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageReport.jsx similarity index 79% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageReport.jsx rename to src/components/dashboard/damages/operator/Actions/create/Forms/DamageReport.jsx index a649b14..d994aff 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/DamageReport.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageReport.jsx @@ -1,18 +1,16 @@ import { FormControlLabel, RadioGroup, Stack } from "@mui/material"; -import { Controller } from "react-hook-form"; +import { Controller, useWatch } from "react-hook-form"; import { useState } from "react"; import PoliceFileReport from "./PoliceFileReport"; import Radio from "@mui/material/Radio"; -const DamageReport = ({ control, setValue }) => { - const [selectedOption, setSelectedOption] = useState("report_base"); +const DamageReport = ({ control }) => { + const watchedStatus = useWatch({ control, name: "radio_button" }); + const [selectedOption, setSelectedOption] = useState(watchedStatus || null); + // const [selectedOption, setSelectedOption] = useState("report_base"); const handleOptionChange = (e) => { setSelectedOption(e.target.value); - if (e.target.value === "report_base") { - setValue("police_file_checkbox", ""); - setValue("report_base", "1"); - } }; return ( @@ -27,23 +25,23 @@ const DamageReport = ({ control, setValue }) => { } - /> - } - label="کروکی یا نامه پلیس راه (معرفی فرد به اداره)" - /> - } /> } label="بازدید عوامل و کارشناسان راهداری" /> + } + /> + } + label="کروکی یا نامه پلیس راه (معرفی فرد به اداره)" + /> {selectedOption === "police_file_checkbox" && } diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload.jsx new file mode 100644 index 0000000..5186a4b --- /dev/null +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload.jsx @@ -0,0 +1,77 @@ +import { FormControl, FormHelperText } from "@mui/material"; +import UploadSystem from "@/core/components/UploadSystem"; +import React, { useEffect, useState } from "react"; + +const ImageUpload = ({ name, value, onChange, error, title }) => { + const [beforeImg, setBeforeImg] = useState(value ? value : null); + const [beforeFileType, setBeforeFileType] = useState(value ? "image/" : null); + const [beforeFileName, setBeforeFileName] = useState(null); + const [showBeforeImage, setShowBeforeImage] = useState(!value); + + useEffect(() => { + if (value) { + setShowBeforeImage(false); + if (typeof value === "string") { + setBeforeImg(value); + setBeforeFileType("image/"); + } else if (value instanceof File) { + setBeforeImg(URL.createObjectURL(value)); + setBeforeFileType(value.type); + } + } + }, [value]); + + const handleFileChange = (event) => { + const uploadedFile = event.target?.files?.[0]; + if (uploadedFile) { + const fileType = event.target?.files?.[0].type; + const fileName = event.target?.files?.[0].name; + setBeforeImg(URL.createObjectURL(uploadedFile)); + setBeforeFileType(fileType); + setBeforeFileName(fileName); + onChange(uploadedFile); + setShowBeforeImage(false); + } + }; + + return ( + + + {title} + + + {error ? error.message : null} + + ); +}; +export default ImageUpload; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/PoliceFileReport.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/PoliceFileReport.jsx similarity index 83% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/PoliceFileReport.jsx rename to src/components/dashboard/damages/operator/Actions/create/Forms/PoliceFileReport.jsx index 9a93566..64f1067 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/PoliceFileReport.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/PoliceFileReport.jsx @@ -49,15 +49,17 @@ const PoliceFileReport = ({ control }) => { ( - - )} + render={({ field, fieldState: { error } }) => { + return ( + + ); + }} /> ); diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/index.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/index.jsx new file mode 100644 index 0000000..4a25e69 --- /dev/null +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/index.jsx @@ -0,0 +1,86 @@ +"use client"; +import { Dialog } from "@mui/material"; +import CreateFormContent from "./CreateFormContent"; +import { format } from "date-fns"; +import { CREATE_DAMAGE } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; + +const OperatorCreateForm = ({ open, setOpen, mutate }) => { + const requestServer = useRequest({ notificationSuccess: true }); + const SubmitCreateDamage = async (result) => { + const PlateNumber = `${result.plate_part1}-${result.plate_part2}-${result.plate_part3}-${result.plate_part4}`; + const formData = new FormData(); + formData.append("accident_date", result.accident_date); + formData.append("accident_time", format(new Date(result.accident_time), "HH:mm")); + formData.append("accident_type", result.accident_type); + formData.append("province_id", result.province_id); + formData.append("city_id", result.city_id); + formData.append("axis_name", result.axis_name); + formData.append("driver_name", result.driver_name); + formData.append("driver_phone_number", result.phone_number); + formData.append("driver_national_code", result.national_code); + formData.append("plaque", PlateNumber); + formData.append("damage_picture1", result.damage_picture1); + formData.append("damage_picture2", result.damage_picture2); + formData.append("lat", result.start_point.lat); + formData.append("lng", result.start_point.lng); + result.items_damage.map((item, index) => { + formData.append(`damage_items[${index}][id]`, item.id); + formData.append(`damage_items[${index}][value]`, item.value); + formData.append(`damage_items[${index}][amount]`, item.amount); + }); + if (result.radio_button === "report_base") { + formData.append("report_base", 1); + } + if (result.radio_button === "police_file_checkbox") { + formData.append("report_base", 0); + formData.append("police_file", result.police_file); + formData.append("police_file_date", result.police_file_date); + formData.append("police_serial", result.police_serial); + } + await requestServer(`${CREATE_DAMAGE}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpen(false); + }) + .catch(() => {}); + }; + const defaultData = { + accident_date: "", + accident_time: "", + accident_type: "", + province_id: null, + city_id: null, + axis_name: "", + driver_name: "", + phone_number: "", + national_code: "", + plate_part1: "", + plate_part2: "الف", + plate_part3: "", + plate_part4: "", + radio_button: "report_base", + damage_picture1: null, + damage_picture2: null, + report_base: "", + police_file_checkbox: "", + police_file: null, + police_file_date: "", + police_serial: "", + start_point: "", + items_damage: [], + }; + return ( + + + + ); +}; +export default OperatorCreateForm; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/index.jsx b/src/components/dashboard/damages/operator/Actions/create/index.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/index.jsx rename to src/components/dashboard/damages/operator/Actions/create/index.jsx diff --git a/src/components/dashboard/damages/operator/ExcelPrint/index.jsx b/src/components/dashboard/damages/operator/ExcelPrint/index.jsx new file mode 100644 index 0000000..f38d451 --- /dev/null +++ b/src/components/dashboard/damages/operator/ExcelPrint/index.jsx @@ -0,0 +1,75 @@ +"use client"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; +import { useMemo, useState } from "react"; +import moment from "jalali-moment"; +import FileSaver from "file-saver"; +import DescriptionIcon from "@mui/icons-material/Description"; +import useRequest from "@/lib/hooks/useRequest"; +import { useTheme } from "@emotion/react"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; +import { EXPORT_DAMAGES_OPERATOR_LIST } from "@/core/utils/routes"; + +const PrintExcel = ({ table, filterData }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [loading, setLoading] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { + if (filter.value && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) { + acc.push({ id: key, value: filter.value }); + } + return acc; + }, []); + + const filterParams = useMemo(() => { + const params = new URLSearchParams(); + if (activeFilters.length > 0) { + activeFilters.map((filter, index) => { + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); + }); + } else { + params.set("filters", JSON.stringify([])); + } + return params; + }, [activeFilters]); + + const clickHandler = () => { + setLoading(true); + requestServer(`${EXPORT_DAMAGES_OPERATOR_LIST}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی کارتابل عملیات فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + ); +}; +export default PrintExcel; diff --git a/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx b/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx new file mode 100644 index 0000000..e33e5f8 --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/CreateFactor/CreateFactorContent.jsx @@ -0,0 +1,208 @@ +import { Box, Button, Chip, DialogActions, DialogContent, Divider, Grid, Typography } from "@mui/material"; +import PaymentIcon from "@mui/icons-material/Payment"; +import WebAssetIcon from "@mui/icons-material/WebAsset"; +import RequestQuoteIcon from "@mui/icons-material/RequestQuote"; +import ReceiptLongIcon from "@mui/icons-material/ReceiptLong"; +import { useReducer, useState } from "react"; +import { CHECK_PAYMENT_STATUS, CREATE_FACTOR_DAMAGE, SEND_SMS_AGAIN } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; + +const initialState = { + submittingCreateFactor: false, + submittingCheckFactor: false, + submittingSms: false, +}; + +const reducer = (state, action) => { + switch (action.type) { + case "SET_SUBMITTING_CREATE_FACTOR": + return { ...state, submittingCreateFactor: action.payload }; + case "SET_SUBMITTING_CHECK_FACTOR": + return { ...state, submittingCheckFactor: action.payload }; + case "SET_SUBMITTING_SMS": + return { ...state, submittingSms: action.payload }; + default: + return state; + } +}; +const CreateFactorContent = ({ row, mutate, rowId, setOpenCreateFactorDialog }) => { + const [state, dispatch] = useReducer(reducer, initialState); + const requestServer = useRequest({ notificationSuccess: true }); + const [factorCreated, setFactorCreated] = useState(row.original?.status === 3); + const handleCreateFactor = () => { + dispatch({ type: "SET_SUBMITTING_CREATE_FACTOR", payload: true }); + requestServer(`${CREATE_FACTOR_DAMAGE}/${rowId}`, "get") + .then(() => { + mutate(); + dispatch({ type: "SET_SUBMITTING_CREATE_FACTOR", payload: false }); + setFactorCreated(true); + }) + .catch(() => { + dispatch({ type: "SET_SUBMITTING_CREATE_FACTOR", payload: false }); + }); + }; + const handleCheckPaymentStatus = () => { + dispatch({ type: "SET_SUBMITTING_CHECK_FACTOR", payload: true }); + requestServer(`${CHECK_PAYMENT_STATUS}/${rowId}`, "get") + .then(() => { + setOpenCreateFactorDialog(false); + mutate(); + dispatch({ type: "SET_SUBMITTING_CHECK_FACTOR", payload: false }); + }) + .catch(() => { + dispatch({ type: "SET_SUBMITTING_CHECK_FACTOR", payload: false }); + }); + }; + const handleSendSMS = () => { + dispatch({ type: "SET_SUBMITTING_SMS", payload: true }); + requestServer(`${SEND_SMS_AGAIN}/${rowId}`, "get") + .then(() => { + dispatch({ type: "SET_SUBMITTING_SMS", payload: false }); + }) + .catch(() => { + dispatch({ type: "SET_SUBMITTING_SMS", payload: false }); + }); + }; + + return ( + <> + + + + + } + label={ + + مبلغ کسری بیمه + + } + /> + + + + {(row.original?.deposit_insurance_amount / 1).toLocaleString() || "0"} ریال + + + + + } + label={ + + مبلغ کسری داغی + + } + /> + + + + {(row.original?.deposit_daghi_amount / 1).toLocaleString() || "0"} ریال + + + + + } + label={ + + مبلغ کل خسارت برآورد شده + + } + /> + + + + {(row.original?.sum / 1).toLocaleString() || "0"} ریال + + + + + } + label={ + + مبلغ قابل پرداخت + + } + /> + + + + {(row.original?.sum / 1).toLocaleString() || "0"} ریال + + + {!factorCreated && ( + + + * + + پس از ایجاد فاکتور امکان تغییر در اطلاعات کاربر و مبالغ وجود نخواهد داشت. + + + + * + + لینک پرداخت فاکتور به شماره {row.original?.driver_phone_number} ارسال خواهد شد. + + + + )} + + + + {factorCreated ? ( + <> + + + + + ) : ( + <> + + + + )} + + + ); +}; +export default CreateFactorContent; diff --git a/src/components/dashboard/damages/operator/Form/CreateFactor/index.jsx b/src/components/dashboard/damages/operator/Form/CreateFactor/index.jsx new file mode 100644 index 0000000..e245e0c --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/CreateFactor/index.jsx @@ -0,0 +1,39 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ReceiptLongIcon from "@mui/icons-material/ReceiptLong"; +import CreateFactorContent from "./CreateFactorContent"; + +const CreateFactor = ({ row, rowId, mutate }) => { + const [openCreateFactorDialog, setOpenCreateFactorDialog] = useState(false); + + return ( + <> + + setOpenCreateFactorDialog(true)}> + + + + setOpenCreateFactorDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"sm"} + scroll="paper" + > + مدیریت پرداخت + + + + ); +}; +export default CreateFactor; diff --git a/src/components/dashboard/damages/operator/Form/EditForm/EditController.jsx b/src/components/dashboard/damages/operator/Form/EditForm/EditController.jsx new file mode 100644 index 0000000..f545cc6 --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/EditForm/EditController.jsx @@ -0,0 +1,113 @@ +import useRequest from "@/lib/hooks/useRequest"; +import CreateFormContent from "@/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent"; +import { useEffect, useState } from "react"; +import { GET_DAMAGE_ITEM_DETAILS, UPDATE_DAMAGE_ITEM } from "@/core/utils/routes"; +import DialogLoading from "@/core/components/DialogLoading"; +import { format } from "date-fns"; +import moment from "jalali-moment"; + +const EditController = ({ rowId, mutate, setOpenEditDialog }) => { + const requestServer = useRequest(); + const [damageItemDetails, setDamageItemDetails] = useState(null); + const [damageItemDetailsLoading, setDamageItemDetailsLoading] = useState(false); + + useEffect(() => { + setDamageItemDetailsLoading(true); + requestServer(`${GET_DAMAGE_ITEM_DETAILS}/${rowId}`, "get") + .then((response) => { + setDamageItemDetails(response.data.data); + setDamageItemDetailsLoading(false); + }) + .catch((e) => { + setDamageItemDetailsLoading(false); + }); + }, [rowId]); + + const today = new Date().toISOString().split("T")[0]; + const plaqueNumber = damageItemDetails?.plaque.split("-") || []; + const defaultData = { + damage_picture1: damageItemDetails?.damage_picture1 || null, + damage_picture2: damageItemDetails?.damage_picture2 || null, + accident_date: new Date(damageItemDetails?.accident_date) || "", + accident_time: new Date(`${today} ${damageItemDetails?.accident_time || ""}`), + accident_type: damageItemDetails?.accident_type || "", + axis_name: damageItemDetails?.axis_name || "", + driver_name: damageItemDetails?.driver_name || "", + police_file: damageItemDetails?.police_file || null, + police_file_date: damageItemDetails?.police_file_date || "", + police_serial: damageItemDetails?.police_serial || "", + items_damage: damageItemDetails?.damages || [], + phone_number: damageItemDetails?.driver_phone_number || "", + national_code: damageItemDetails?.driver_national_code || "", + plate_part1: plaqueNumber[0] || "", + plate_part2: plaqueNumber[1] || "", + plate_part3: plaqueNumber[2] || "", + plate_part4: plaqueNumber[3] || "", + radio_button: damageItemDetails?.report_base === 1 ? "report_base" : "police_file_checkbox", + report_base: damageItemDetails?.report_base, + province_id: damageItemDetails?.province_id || null, + city_id: damageItemDetails?.city_id || null, + start_point: { lat: damageItemDetails?.lat || "", lng: damageItemDetails?.lng || "" }, + }; + const HandleSubmit = async (result) => { + const PlateNumber = `${result.plate_part1}-${result.plate_part2}-${result.plate_part3}-${result.plate_part4}`; + const formData = new FormData(); + const accidentDate = + result.accident_date == defaultData.accident_date + ? moment(result.accident_date).locale("en").format("YYYY-MM-DD") + : result.accident_date; + formData.append("accident_date", accidentDate); + formData.append("accident_time", format(new Date(result.accident_time), "HH:mm")); + formData.append("accident_type", result.accident_type); + formData.append("province_id", result.province_id); + formData.append("city_id", result.city_id); + formData.append("axis_name", result.axis_name); + formData.append("driver_name", result.driver_name); + formData.append("driver_phone_number", result.phone_number); + formData.append("driver_national_code", result.national_code); + formData.append("plaque", PlateNumber); + result.damage_picture1 !== defaultData.damage_picture1 && + formData.append("damage_picture1", result.damage_picture1); + result.damage_picture2 !== defaultData.damage_picture2 && + formData.append("damage_picture2", result.damage_picture2); + formData.append("lat", result.start_point.lat); + formData.append("lng", result.start_point.lng); + result.items_damage.map((item, index) => { + formData.append(`damage_items[${index}][id]`, item.id); + formData.append(`damage_items[${index}][value]`, item.value); + formData.append(`damage_items[${index}][amount]`, item.amount); + }); + if (result.radio_button === "report_base") { + formData.append("report_base", 1); + } + if (result.radio_button === "police_file_checkbox") { + formData.append("report_base", 0); + result.police_file !== defaultData.police_file && formData.append("police_file", result.police_file); + formData.append("police_file_date", result.police_file_date); + formData.append("police_serial", result.police_serial); + } + await requestServer(`${UPDATE_DAMAGE_ITEM}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenEditDialog(false); + }) + .catch(() => {}); + }; + return ( + <> + {damageItemDetailsLoading ? ( + + ) : ( + + )} + + ); +}; +export default EditController; diff --git a/src/components/dashboard/damages/operator/Form/EditForm/index.jsx b/src/components/dashboard/damages/operator/Form/EditForm/index.jsx new file mode 100644 index 0000000..9b1b17f --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/EditForm/index.jsx @@ -0,0 +1,45 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import BorderColorIcon from "@mui/icons-material/BorderColor"; +import EditController from "./EditController"; + +const EditForm = ({ row, mutate, rowId }) => { + const [openEditDialog, setOpenEditDialog] = useState(false); + + return ( + <> + + { + setOpenEditDialog(true); + }} + > + + + + + {openEditDialog && ( + + )} + + + ); +}; +export default EditForm; diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiFile.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiFile.jsx new file mode 100644 index 0000000..939e534 --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiFile.jsx @@ -0,0 +1,47 @@ +import { Grid, Stack, TextField } from "@mui/material"; +import { Controller } from "react-hook-form"; +import ImageUpload from "./ImageUpload"; +import NumberField from "@/core/components/NumberField"; + +const DaghiFile = ({ control }) => { + return ( + + + + + ( + + )} + /> + + + + ( + + )} + /> + + ); +}; +export default DaghiFile; diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiPayment.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiPayment.jsx new file mode 100644 index 0000000..c21dda6 --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/DaghiPayment.jsx @@ -0,0 +1,53 @@ +import { useState } from "react"; +import { FormControlLabel, RadioGroup, Stack } from "@mui/material"; +import { Controller, useWatch } from "react-hook-form"; +import Radio from "@mui/material/Radio"; +import DaghiFile from "./DaghiFile"; + +const DaghiPayment = ({ control }) => { + const watchedStatus = useWatch({ control, name: "deposit_daghi_status" }); + const [selectedOption, setSelectedOption] = useState(watchedStatus || null); + + const handleOptionChange = (e) => { + setSelectedOption(e.target.value); + }; + + return ( + <> + + + } + /> + } + label="صورتجلسه تحویل داغی به انبار (بیمه) ندارد" + /> + } + /> + } + label="صورتجلسه تحویل داغی به انبار (بیمه) دارد" + /> + + + + {selectedOption === "has_daghi" && } + + ); +}; +export default DaghiPayment; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/ImageUpload.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/ImageUpload.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/ImageUpload.jsx rename to src/components/dashboard/damages/operator/Form/RegisterInsurance/ImageUpload.jsx diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsuranceFile.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsuranceFile.jsx new file mode 100644 index 0000000..e96521a --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsuranceFile.jsx @@ -0,0 +1,47 @@ +import { Grid, Stack, TextField } from "@mui/material"; +import { Controller } from "react-hook-form"; +import ImageUpload from "./ImageUpload"; +import NumberField from "@/core/components/NumberField"; + +const InsuranceFile = ({ control }) => { + return ( + + + + + ( + + )} + /> + + + + ( + + )} + /> + + ); +}; +export default InsuranceFile; diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsurancePayment.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsurancePayment.jsx new file mode 100644 index 0000000..50ee912 --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/InsurancePayment.jsx @@ -0,0 +1,52 @@ +import { FormControlLabel, FormHelperText, RadioGroup, Stack } from "@mui/material"; +import { Controller, useWatch } from "react-hook-form"; +import Radio from "@mui/material/Radio"; +import { useState } from "react"; +import InsuranceFile from "./InsuranceFile"; + +const InsurancePayment = ({ control }) => { + const watchedStatus = useWatch({ control, name: "deposit_insurance_status" }); + const [selectedOption, setSelectedOption] = useState(watchedStatus || null); + + const handleOptionChange = (e) => { + setSelectedOption(e.target.value); + }; + + return ( + <> + + + } + /> + } + label="فیش واریزی (بیمه) ندارد" + /> + } + /> + } + label="فیش واریزی (بیمه) دارد" + /> + + + {selectedOption === "has_insurance" && } + + ); +}; +export default InsurancePayment; diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/RegisterInsuranceContent.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/RegisterInsuranceContent.jsx new file mode 100644 index 0000000..090609b --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/RegisterInsuranceContent.jsx @@ -0,0 +1,202 @@ +import { Box, Button, DialogActions, DialogContent, Tab, Tabs, Typography } from "@mui/material"; +import ExitToAppIcon from "@mui/icons-material/ExitToApp"; +import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; +import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; +import LocalPostOfficeIcon from "@mui/icons-material/LocalPostOffice"; +import BeenhereIcon from "@mui/icons-material/Beenhere"; +import StyledForm from "@/core/components/StyledForm"; +import PaymentIcon from "@mui/icons-material/Payment"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { mixed, object, string } from "yup"; +import InsurancePayment from "./InsurancePayment"; +import DaghiPayment from "./DaghiPayment"; +import { CONFIRM_PAYMENT_INFO } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +function TabPanel(props) { + const { children, value, index } = props; + return ( + + ); +} +const validationSchema = object({ + deposit_insurance_status: string().required("انتخاب یک گزینه الزامیست."), + deposit_daghi_status: string().required("انتخاب یک گزینه الزامیست."), + deposit_insurance_amount: string().when("deposit_insurance_status", { + is: "has_insurance", + then: (schema) => schema.required("مبلغ بیمه الزامی است."), + otherwise: (schema) => schema.notRequired(), + }), + deposit_insurance_image: mixed().when("deposit_insurance_status", { + is: "has_insurance", + then: (schema) => schema.required("تصویر فیش واریزی بیمه الزامی است."), + otherwise: (schema) => schema.notRequired(), + }), + deposit_daghi_amount: string() + .when("deposit_daghi_status", { + is: "has_daghi", + then: (schema) => schema.required("مبلغ داغی الزامی است."), + otherwise: (schema) => schema.notRequired(), + }) + .test("max-percentage", "مبلغ داغی نباید بیشتر از ۳۰ درصد مبلغ کل خسارت باشد.", function (value) { + const sum = this.options.context.sum || 0; + if (!value || isNaN(value) || !sum) return true; + return parseFloat(value) <= sum * 0.3; + }), + deposit_daghi_image: mixed().when("deposit_daghi_status", { + is: "has_daghi", + then: (schema) => schema.required("تصویر صورتجلسه تحویل داغی به انبار الزامی است."), + otherwise: (schema) => schema.notRequired(), + }), +}).test("total-amount-limit", "مجموع مبلغ بیمه و داغی نباید از کل مبلغ خسارت بیشتر باشد.", function (values) { + const sum = this.options.context.sum || 0; + const depositInsuranceAmount = parseFloat(values.deposit_insurance_amount) || 0; + const depositDaghiAmount = parseFloat(values.deposit_daghi_amount) || 0; + return depositInsuranceAmount + depositDaghiAmount <= sum; +}); + +const RegisterInsuranceContent = ({ setOpenRegisterInsuranceDialog, row, mutate, rowId }) => { + const [tabState, setTabState] = useState(0); + const requestServer = useRequest({ notificationSuccess: true }); + const handleClose = () => setOpenRegisterInsuranceDialog(false); + const handleChangeTab = (event, newValue) => setTabState(newValue); + + const defaultValues = { + deposit_insurance_status: "no_insurance", + deposit_insurance_amount: "", + deposit_insurance_image: null, + deposit_daghi_status: "no_daghi", + deposit_daghi_amount: "", + deposit_daghi_image: null, + }; + const handlePrev = () => { + if (tabState === 0) { + handleClose(); + } else { + setTabState(tabState - 1); + } + }; + const handleNext = async () => { + let fieldsToValidate = []; + if (tabState === 0) { + fieldsToValidate = ["deposit_insurance_status", "deposit_insurance_amount", "deposit_insurance_image"]; + } + if (tabState === 1) { + fieldsToValidate = ["deposit_daghi_status", "deposit_daghi_amount", "deposit_daghi_image"]; + } + + const isValid = await trigger(fieldsToValidate); + if (isValid) { + setTabState(tabState + 1); + } + }; + + const { + control, + handleSubmit, + trigger, + formState: { isSubmitting, errors }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + context: { sum: row?.original?.sum }, + }); + + const onSubmitBase = async (data) => { + const formData = new FormData(); + const depositDaghiStatus = data.deposit_daghi_status === "no_daghi" ? 0 : 1; + const depositInsuranceStatus = data.deposit_insurance_status === "no_insurance" ? 0 : 1; + + if (data.deposit_daghi_status === "has_daghi") { + formData.append("deposit_daghi_status", depositDaghiStatus); + formData.append("deposit_daghi_amount", data.deposit_daghi_amount); + formData.append("deposit_daghi_image", data.deposit_daghi_image); + } + if (data.deposit_insurance_status === "has_insurance") { + formData.append("deposit_insurance_status", depositInsuranceStatus); + formData.append("deposit_insurance_amount", data.deposit_insurance_amount); + formData.append("deposit_insurance_image", data.deposit_insurance_image); + } + requestServer(`${CONFIRM_PAYMENT_INFO}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenRegisterInsuranceDialog(false); + }) + .catch(() => {}); + }; + return ( + + + } label="فیش واریزی بیمه" /> + } label="صورتجلسه تحویل داغی به انبار" /> + + + + + + + + + + + + {errors[""]?.message && ( + + {errors[""].message} + + )} + + + {tabState !== 1 && ( + + )} + {tabState === 1 && ( + + )} + + + ); +}; +export default RegisterInsuranceContent; diff --git a/src/components/dashboard/damages/operator/Form/RegisterInsurance/index.jsx b/src/components/dashboard/damages/operator/Form/RegisterInsurance/index.jsx new file mode 100644 index 0000000..8a02fdf --- /dev/null +++ b/src/components/dashboard/damages/operator/Form/RegisterInsurance/index.jsx @@ -0,0 +1,37 @@ +import { Dialog, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ReceiptIcon from "@mui/icons-material/Receipt"; +import RegisterInsuranceContent from "./RegisterInsuranceContent"; + +const RegisterInsurance = ({ row, mutate, rowId }) => { + const [openRegisterInsuranceDialog, setOpenRegisterInsuranceDialog] = useState(false); + + return ( + <> + + setOpenRegisterInsuranceDialog(true)}> + + + + + + + + ); +}; +export default RegisterInsurance; diff --git a/src/components/dashboard/damages/operator/OperatorList.jsx b/src/components/dashboard/damages/operator/OperatorList.jsx new file mode 100644 index 0000000..4bc0359 --- /dev/null +++ b/src/components/dashboard/damages/operator/OperatorList.jsx @@ -0,0 +1,403 @@ +"use client"; +import { useEffect, useMemo, useState } from "react"; +import { Box, Stack, Typography } from "@mui/material"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import Toolbar from "./Toolbar"; +import { GET_TECHNICAL_DAMAGE_OPERATOR_LIST } from "@/core/utils/routes"; +import moment from "jalali-moment"; +import RowActions from "./RowActions"; +import useProvinces from "@/lib/hooks/useProvince"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import ImageDialog from "./RowActions/ImageDialog"; +import LocationForm from "./RowActions/LocationForm"; +import ShowPlate from "./RowActions/ShowPlate"; +import DamageItemDialog from "@/components/dashboard/damages/operator/RowActions/DamageItemDialog"; + +const OperatorList = () => { + const statusOptions = [ + { value: 0, label: "بدون اقدام" }, + { value: 1, label: "صدور نامه بیمه و کارشناسی داغی" }, + { value: 2, label: "فیش ها ثبت شده است" }, + { value: 3, label: "فاکتور صادر شده است" }, + { value: 4, label: "فاکتور پرداخت شده است" }, + { value: 5, label: "نامه پلیس راه صادر شده است" }, + ]; + const columns = useMemo(() => { + return [ + { + accessorKey: "id", + header: "کد یکتا", // Unique Code + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + header: "اطلاعات تصادف", + id: "accidentInfo", + enableColumnFilter: false, + enableSorting: false, + grow: false, + size: 50, + columns: [ + { + accessorKey: "province_id", + header: "استان", + id: "province_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 130, + ColumnSelectComponent: (props) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const getColumnSelectOptions = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.province_fa}, + }, + { + accessorKey: "city_id", + header: "شهرستان", // Office + id: "city_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + dependencyId: "province_id", + grow: false, + size: 120, + ColumnSelectComponent: (props) => { + const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( + props.dependencyFieldValue.value + ); + const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value); + + const getColumnSelectOptions = useMemo(() => { + if (props.dependencyFieldValue.value === "") { + return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; + } + if (loadingEdaratList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorEdaratList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "شهر" }, + ...edaratList.map((edare) => ({ + value: edare.id, + label: edare.name_fa, + })), + ]; + }, [edaratList, loadingEdaratList, errorEdaratList]); + useEffect(() => { + if (prevDependency === props.dependencyFieldValue?.value) return; + props.handleChange({ ...props.filterParameters, value: "" }); + setPrevDependency(props.dependencyFieldValue?.value); + }, [props.dependencyFieldValue?.value]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.city_fa}, + }, + { + accessorKey: "axis_name", + header: "نام محور", + id: "axis_name", + enableColumnFilter: true, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "accident_type", + header: "نوع تصادف", // Value + id: "accident_type", + enableColumnFilter: true, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + Cell: ({ row }) => <>{row.original.accident_type_fa}, + }, + { + accessorFn: (row) => { + const formattedDate = moment(row.accident_date, "YYYY-MM-DD"); // تنظیم فرمت صحیح تاریخ + const formattedTime = row.accident_time || "00:00:00"; // در صورت خالی بودن مقدار، مقدار پیش‌فرض داده شود + return moment(`${formattedDate.format("YYYY-MM-DD")}T${formattedTime}`) + .locale("fa") + .format("HH:mm | YYYY/MM/DD"); + }, + header: "تاریخ تصادف", + id: "accident_date", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorKey: "files", + header: "تصاویر", + id: "files", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + if (row.original.damage_picture1) { + const imagesList = [row.original.damage_picture1, row.original.damage_picture2]; + return ( + + + + ); + } + return ( + + بدون تصویر + + ); + }, + }, + { + accessorKey: "location", + header: "موقعیت", + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + return ( + + + + ); + }, + }, + { + accessorKey: "plaque", + header: "پلاک", + id: "plaque", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: true, + grow: false, + size: 100, + Cell: ({ renderedCellValue }) => , + }, + ], + }, + { + header: "اطلاعات خسارت", + id: "damagesInfo", + enableColumnFilter: false, + enableSorting: false, + grow: false, + size: 50, + columns: [ + { + header: "آیتم های خسارت", + id: "damageItems", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + return ( + + + + ); + }, + }, + { + accessorKey: "sum", + header: "مبلغ کل خسارت (ریال)", + id: "sum", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + sortDescFirst: true, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.sum / 1).toLocaleString()} + + ), + }, + ], + }, + { + header: "اطلاعات واریز", + id: "items", + enableColumnFilter: false, + enableSorting: false, + grow: false, + size: 50, + columns: [ + { + header: "مبلغ فیش (ریال)", + id: "final_amount", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.final_amount / 1).toLocaleString()} + + ), + }, + { + header: "مبلغ بیمه (ریال)", + id: "deposit_insurance_amount", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.deposit_insurance_amount / 1).toLocaleString()} + + ), + }, + { + header: "مبلغ داغی (ریال)", + id: "deposit_daghi_amount", + enableColumnFilter: false, + grow: false, + size: 100, + Cell: ({ row }) => ( + + {(row.original.deposit_daghi_amount / 1).toLocaleString()} + + ), + }, + ], + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "created_at", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorKey: "status", + header: "اخرین وضعیت", + id: "status", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + columnSelectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + Cell: ({ row }) => { + return {row.original.status_fa}; + }, + }, + ]; + }, []); + + return ( + <> + + + + + ); +}; +export default OperatorList; diff --git a/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/DamageItemContent.jsx b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/DamageItemContent.jsx new file mode 100644 index 0000000..e9a76e5 --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/DamageItemContent.jsx @@ -0,0 +1,76 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_DAMAGE_ITEM_DETAILS, GET_RAHDARAN_ROAD_ITEM } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { + DialogContent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from "@mui/material"; +import { useEffect, useState } from "react"; + +const DamageItemContent = ({ rowId }) => { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + const request = useRequest(); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const response = await request(`${GET_DAMAGE_ITEM_DETAILS}/${rowId}`); + setData(response.data.data.damages); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [rowId]); + + return ( + <> + + {loading ? ( + + ) : data ? ( + + + + + نوع خسارت + میزان خسارت + هزینه خسارت (ریال) + + + + {data.map((item) => { + return ( + + {item?.title} + + {(item?.pivot?.value / 1).toLocaleString()} ({item?.pivot?.unit}) + + {(item?.pivot?.amount / 1).toLocaleString()} + + ); + })} + +
+
+ ) : ( + اطلاعات یافت نشد + )} +
+ + ); +}; +export default DamageItemContent; diff --git a/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/index.jsx b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/index.jsx new file mode 100644 index 0000000..6164de2 --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/DamageItemDialog/index.jsx @@ -0,0 +1,43 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import MinorCrashIcon from "@mui/icons-material/MinorCrash"; +import { useState } from "react"; +import DamageItemContent from "./DamageItemContent"; + +const DamageItemDialog = ({ rowId }) => { + const [openDamageItemDialog, setOpenDamageItemDialog] = useState(false); + return ( + <> + + setOpenDamageItemDialog(true)}> + + + + setOpenDamageItemDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"md"} + > + آیتم های خسارت + {openDamageItemDialog && } + + + + + + ); +}; +export default DamageItemDialog; diff --git a/src/components/dashboard/damages/operator/RowActions/DeleteDialog/DeleteContent.jsx b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/DeleteContent.jsx new file mode 100644 index 0000000..28791c7 --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/DeleteContent.jsx @@ -0,0 +1,39 @@ +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { DELETE_DAMAGE_ITEM } from "@/core/utils/routes"; + +const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const handleClick = () => { + setSubmitting(true); + requestServer(`${DELETE_DAMAGE_ITEM}/${rowId}`, "delete") + .then(() => { + mutate(); + setOpenDeleteDialog(false); + setSubmitting(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + آیا از حذف فعالیت اطمینان دارید؟ + + + + + + + + ); +}; +export default DeleteContent; diff --git a/src/components/dashboard/damages/operator/RowActions/DeleteDialog/index.jsx b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/index.jsx new file mode 100644 index 0000000..b7f6d4b --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/DeleteDialog/index.jsx @@ -0,0 +1,32 @@ +import DeleteIcon from "@mui/icons-material/Delete"; +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import DeleteContent from "./DeleteContent"; +const DeleteDialog = ({ rowId, mutate }) => { + const [openDeleteDialog, setOpenDeleteDialog] = useState(false); + + return ( + <> + + setOpenDeleteDialog(true)}> + + + + setOpenDeleteDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"md"} + > + حذف فعالیت + + + + ); +}; +export default DeleteDialog; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/ImageFormContent.jsx rename to src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/index.jsx b/src/components/dashboard/damages/operator/RowActions/ImageDialog/index.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/RowActions/ImageDialog/index.jsx rename to src/components/dashboard/damages/operator/RowActions/ImageDialog/index.jsx diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/damages/operator/RowActions/LocationForm/LocationFormContent.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/LocationFormContent.jsx rename to src/components/dashboard/damages/operator/RowActions/LocationForm/LocationFormContent.jsx diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/index.jsx b/src/components/dashboard/damages/operator/RowActions/LocationForm/index.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/RowActions/LocationForm/index.jsx rename to src/components/dashboard/damages/operator/RowActions/LocationForm/index.jsx diff --git a/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/PoliceRahLetterContent.jsx b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/PoliceRahLetterContent.jsx new file mode 100644 index 0000000..8495455 --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/PoliceRahLetterContent.jsx @@ -0,0 +1,41 @@ +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { GET_POLICERAH_PAGE } from "@/core/utils/routes"; + +const PoliceRahLetterContent = ({ setOpenPoliceRahDialog, rowId, mutate }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const handleClick = () => { + setSubmitting(true); + const url = `https://rms.witel.ir/v2/receipt/document-release/${rowId}`; + requestServer(`${GET_POLICERAH_PAGE}/${rowId}`, "get") + .then(() => { + mutate(); + window.open(url, "_blank"); + setSubmitting(false); + setOpenPoliceRahDialog(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + آیا از صدور نامه به پلیس راه اطمینان دارید؟ + + + + + + + + ); +}; +export default PoliceRahLetterContent; diff --git a/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/index.jsx b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/index.jsx new file mode 100644 index 0000000..cbdb3c6 --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/PoliceRahLetter/index.jsx @@ -0,0 +1,32 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import GarageIcon from "@mui/icons-material/Garage"; +import React, { useState } from "react"; +import PoliceRahLetterContent from "./PoliceRahLetterContent"; + +const PoliceRahLetter = ({ rowId, mutate }) => { + const [openPoliceRahDialog, setOpenPoliceRahDialog] = useState(false); + return ( + <> + + setOpenPoliceRahDialog(true)}> + + + + setOpenPoliceRahDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"xs"} + scroll="paper" + > + صدور نامه پلیس راه + + + + ); +}; +export default PoliceRahLetter; diff --git a/src/components/dashboard/damages/operator/RowActions/SendToInsurance/SendToInsuranceContent.jsx b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/SendToInsuranceContent.jsx new file mode 100644 index 0000000..704f517 --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/SendToInsuranceContent.jsx @@ -0,0 +1,41 @@ +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { GET_INSURANCE_PAGE } from "@/core/utils/routes"; + +const SendToInsuranceContent = ({ setOpenSendInsuranceDialog, rowId, mutate }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const handleClick = () => { + setSubmitting(true); + const url = `https://rms.witel.ir/v2/receipt/send-to-insurance/${rowId}`; + requestServer(`${GET_INSURANCE_PAGE}/${rowId}`, "get") + .then(() => { + mutate(); + window.open(url, "_blank"); + setSubmitting(false); + setOpenSendInsuranceDialog(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + آیا از صدور نامه بیمه اطمینان دارید؟ + + + + + + + + ); +}; +export default SendToInsuranceContent; diff --git a/src/components/dashboard/damages/operator/RowActions/SendToInsurance/index.jsx b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/index.jsx new file mode 100644 index 0000000..e32a85b --- /dev/null +++ b/src/components/dashboard/damages/operator/RowActions/SendToInsurance/index.jsx @@ -0,0 +1,36 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import EmailIcon from "@mui/icons-material/Email"; +import React, { useState } from "react"; +import SendToInsuranceContent from "./SendToInsuranceContent"; + +const SendToInsurance = ({ rowId, mutate }) => { + const [openSendInsuranceDialog, setOpenSendInsuranceDialog] = useState(false); + return ( + <> + + setOpenSendInsuranceDialog(true)}> + + + + setOpenSendInsuranceDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"xs"} + scroll="paper" + > + نامه بیمه + + + + ); +}; +export default SendToInsurance; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ShowPlate/index.jsx b/src/components/dashboard/damages/operator/RowActions/ShowPlate/index.jsx similarity index 90% rename from src/components/dashboard/technicalBuildingDamage/operator/RowActions/ShowPlate/index.jsx rename to src/components/dashboard/damages/operator/RowActions/ShowPlate/index.jsx index 90d3ad0..af1f495 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/RowActions/ShowPlate/index.jsx +++ b/src/components/dashboard/damages/operator/RowActions/ShowPlate/index.jsx @@ -3,10 +3,10 @@ import { Stack } from "@mui/material"; const ShowPlate = ({ plate }) => { return ( - + {plate.split("-")[3]} - + { + return ( + + {[0, 1, 2].includes(row.original?.status) && } + {[1, 2].includes(row.original?.status) && ( + + )} + {[2, 3].includes(row.original?.status) && ( + + )} + {[4, 5].includes(row.original?.status) && } + {[0, 1, 2].includes(row.original?.status) && ( + + )} + + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/technicalBuildingDamage/operator/Toolbar.jsx b/src/components/dashboard/damages/operator/Toolbar.jsx similarity index 73% rename from src/components/dashboard/technicalBuildingDamage/operator/Toolbar.jsx rename to src/components/dashboard/damages/operator/Toolbar.jsx index b774945..7c3ea62 100644 --- a/src/components/dashboard/technicalBuildingDamage/operator/Toolbar.jsx +++ b/src/components/dashboard/damages/operator/Toolbar.jsx @@ -1,9 +1,11 @@ import { Box } from "@mui/material"; import OperatorCreate from "./Actions/create"; +import PrintExcel from "./ExcelPrint"; const Toolbar = ({ table, filterData, mutate }) => { return ( + ); diff --git a/src/components/dashboard/technicalBuildingDamage/operator/index.jsx b/src/components/dashboard/damages/operator/index.jsx similarity index 100% rename from src/components/dashboard/technicalBuildingDamage/operator/index.jsx rename to src/components/dashboard/damages/operator/index.jsx diff --git a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/TableInfo.jsx b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/TableInfo.jsx index 73f9cdd..ca60b84 100644 --- a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/TableInfo.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/TableInfo.jsx @@ -43,7 +43,7 @@ const TableInfo = ({ specialFilter, setTabState, setItemInfo }) => { }, { accessorKey: "item_name", - header: "نام ایتم", + header: "نام آیتم", id: "item_name", enableColumnFilter: false, datatype: "text", diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/ExcelPrint/index.jsx index 0e70403..bf00587 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/ExcelPrint/index.jsx @@ -44,7 +44,7 @@ const PrintExcel = ({ table, filterData }) => { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل گزارشات فعالیت روزانه براساس ایتم تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل گزارشات فعالیت روزانه براساس آیتم تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx index 80821f8..b739780 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/ReportLists.jsx @@ -66,7 +66,7 @@ const ReportLists = ({ itemsList, data, specialFilter }) => { }, }, { - header: "ایتم های فعالیت روزانه", + header: "آیتم های فعالیت روزانه", id: "items", enableColumnFilter: false, enableSorting: false, diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx index 528be1a..f485c51 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx @@ -157,7 +157,7 @@ const ReportPage = () => { }); return ( - + { control={control} render={({ field, fieldState: { error } }) => ( - ایتم + آیتم } + size="small" + onChange={(e) => onChange(e.target.value)} + displayEmpty + > + {columnSelectOption.map((option) => ( + + {option.label} + + ))} + + {error && {error.message}} + + ); +}; +export default EdarateShahriField; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ProvinceField.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ProvinceField.jsx new file mode 100644 index 0000000..455339e --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ProvinceField.jsx @@ -0,0 +1,33 @@ +import useProvinces from "@/lib/hooks/useProvince"; +import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material"; + +const ProvinceField = ({ field, fieldState: { error } }) => { + const { provinces, loadingProvinces, errorProvinces } = useProvinces(); + return ( + + استان + + {error && {error.message}} + + ); +}; +export default ProvinceField; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx new file mode 100644 index 0000000..59774e6 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx @@ -0,0 +1,101 @@ +import { REFER_FAST_REACT } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material"; +import { Controller, useForm } from "react-hook-form"; +import * as Yup from "yup"; +import EdarateShahriField from "./EdarateShahriField"; +import ProvinceField from "./ProvinceField"; + +const ReferContent = ({ rowId, mutate, setOpenReferDialog }) => { + const requestServer = useRequest({ notificationSuccess: true }); + + const validationSchema = Yup.object().shape({ + province_id: Yup.string().required("لطفاً استان را وارد کنید."), + edarat_shahri_id: Yup.string().required("لطفاً اداره را وارد کنید."), + description: Yup.string().required("لطفاً توضیحات را وارد کنید."), + }); + + const { + control, + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + defaultValues: { + province_id: "", + edarat_shahri_id: "", + description: "", + }, + resolver: yupResolver(validationSchema), + mode: "onBlur", + }); + + const onSubmit = async (data) => { + const formData = new FormData(); + formData.append("province_id", data.province_id); + formData.append("edarate_shahri_id", data.edarat_shahri_id); + formData.append("refer_description", data.description); + await requestServer(`${REFER_FAST_REACT}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenReferDialog(false); + }) + .catch(() => {}); + }; + + return ( + <> + + + { + return ; + }} + /> + { + return ; + }} + /> + + + + + + + + + + + ); +}; +export default ReferContent; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/index.jsx new file mode 100644 index 0000000..8dc9875 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/index.jsx @@ -0,0 +1,33 @@ +import SettingsBackupRestoreIcon from "@mui/icons-material/SettingsBackupRestore"; +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ReferContent from "./ReferContent"; + +const Refer = ({ rowId, mutate }) => { + const [openReferDialog, setOpenReferDialog] = useState(false); + return ( + <> + + setOpenReferDialog(true)}> + + + + + ارجاع + {openReferDialog && ( + + )} + + + ); +}; +export default Refer; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx index 29e6e23..4c9093a 100644 --- a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx @@ -1,9 +1,11 @@ import { Box } from "@mui/material"; import ReferList from "./ReferList"; +import Refer from "./Refer"; const RowActions = ({ row, mutate }) => { return ( + ); diff --git a/src/components/dashboard/fastReact/operator/index.jsx b/src/components/dashboard/fastReact/operator/index.jsx index 85759c2..a2b328c 100644 --- a/src/components/dashboard/fastReact/operator/index.jsx +++ b/src/components/dashboard/fastReact/operator/index.jsx @@ -1,13 +1,12 @@ "use client"; import PageTitle from "@/core/components/PageTitle"; import { Stack } from "@mui/material"; -import SupervisorList from "./OperatorList"; import OperatorList from "./OperatorList"; const OperatorPage = () => { return ( - + ); diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx index b5945e2..3d319fa 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx @@ -29,7 +29,7 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => { const formData = new FormData(); formData.append("description", data.description); formData.append("verify", 2); - requestServer(`${REJECT_BY_SUPERVISOR}/${rowId}`, "post", { + await requestServer(`${REJECT_BY_SUPERVISOR}/${rowId}`, "post", { data: formData, }) .then(() => { diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index ed9e8c1..1bee5c2 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -118,3 +118,4 @@ export const VERIFY_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/ export const REJECT_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/verify"; export const GET_FAST_REACT_OPERATOR = api + "/api/v3/road_observations/operator_index"; export const GET_FAST_REACT_REFER_LIST = api + "/api/v3/road_observations/refer_list"; +export const REFER_FAST_REACT = api + "/api/v3/road_observations/refer"; From e8d097756993cffdfd61f5c54b21ddab5e1d694b Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Thu, 27 Feb 2025 11:55:04 +0000 Subject: [PATCH 23/32] Resolve #86c24bdcu "Feature/restore fast react " --- .../RowActions/RestoreForm/RestoreContent.jsx | 42 +++++++++++++++++++ .../RowActions/RestoreForm/index.jsx | 32 ++++++++++++++ .../fastReact/supervisor/RowActions/index.jsx | 3 ++ src/core/utils/routes.js | 1 + 4 files changed, 78 insertions(+) create mode 100644 src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx create mode 100644 src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/index.jsx diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx new file mode 100644 index 0000000..06c86a6 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx @@ -0,0 +1,42 @@ +import { RESTORE_FAST_REACT } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import { useState } from "react"; + +const RestoreContent = ({ rowId, mutate, setOpenRestoreDialog }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const handleClick = () => { + setSubmitting(true); + requestServer(`${RESTORE_FAST_REACT}/${rowId}`, "post") + .then(() => { + mutate(); + setOpenRestoreDialog(false); + setSubmitting(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + + آیا از بازگردانی فعالیت اطمینان دارید؟ + + + + + + + + + ); +}; + +export default RestoreContent; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/index.jsx new file mode 100644 index 0000000..2632c20 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/index.jsx @@ -0,0 +1,32 @@ +import React, { useState } from "react"; +import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; +import ReplyIcon from "@mui/icons-material/Reply"; +import RestoreContent from "./RestoreContent"; +const RestoreForm = ({ rowId, mutate }) => { + const [openRestoreDialog, setOpenRestoreDialog] = useState(false); + + return ( + <> + + setOpenRestoreDialog(true)}> + + + + setOpenRestoreDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"md"} + > + بازگردانی فعالیت + + + + ); +}; +export default RestoreForm; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx index bf60960..b889295 100644 --- a/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx @@ -2,12 +2,14 @@ import { Box } from "@mui/material"; import ConfirmForm from "./ConfirmDialog"; import RejectForm from "./RejectDialog"; import { usePermissions } from "@/lib/hooks/usePermissions"; +import RestoreForm from "./RestoreForm"; const RowActions = ({ row, mutate }) => { const { data: userPermissions } = usePermissions(); const hasSupervisePermission = userPermissions.some((item) => ["supervise-fast-react", "supervise-fast-react-province"].includes(item) ); + const hasRestorePermission = userPermissions.includes("restore-fast-react"); return ( {hasSupervisePermission && row.original?.status === 0 && ( @@ -16,6 +18,7 @@ const RowActions = ({ row, mutate }) => { {hasSupervisePermission && row.original?.status === 0 && ( )} + {hasRestorePermission && } ); }; diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 1bee5c2..7fa077c 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -119,3 +119,4 @@ export const REJECT_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/ export const GET_FAST_REACT_OPERATOR = api + "/api/v3/road_observations/operator_index"; export const GET_FAST_REACT_REFER_LIST = api + "/api/v3/road_observations/refer_list"; export const REFER_FAST_REACT = api + "/api/v3/road_observations/refer"; +export const RESTORE_FAST_REACT = api + "/api/v3/road_observations/restore"; From fa82abd086aa00f2fea223cf2cc8f1260c622270 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Thu, 27 Feb 2025 13:02:31 +0000 Subject: [PATCH 24/32] Feature/complaint create --- .../create/Forms/CreateFormContent.jsx | 1 - .../complaintList/ComplaintListTable.jsx | 2 +- .../Form/registerAction/ImageUpload.jsx | 77 +++++++++++++ .../registerAction/RegisterActionContent.jsx | 108 ++++++++++++++++++ .../registerAction/RegisterActionDone.jsx | 86 ++++++++++++++ .../registerAction/RegisterActionUndone.jsx | 37 ++++++ .../Form/registerAction/index.jsx | 66 +++++++++++ .../complaintList/RowActions/index.jsx | 2 + .../fastReact/supervisor/RowActions/index.jsx | 6 +- .../fastReact/supervisor/SupervisorList.jsx | 12 +- src/core/utils/routes.js | 1 + 11 files changed, 389 insertions(+), 9 deletions(-) create mode 100644 src/components/dashboard/fastReact/complaintList/Form/registerAction/ImageUpload.jsx create mode 100644 src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx create mode 100644 src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx create mode 100644 src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionUndone.jsx create mode 100644 src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx index 026f16a..11ec3b4 100644 --- a/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/CreateFormContent.jsx @@ -4,7 +4,6 @@ import { yupResolver } from "@hookform/resolvers/yup"; import { array, mixed, number, object, string } from "yup"; import { Box, Button, DialogActions, DialogContent, Tab, Tabs } from "@mui/material"; import StyledForm from "@/core/components/StyledForm"; -import FileCopyIcon from "@mui/icons-material/FileCopy"; import InfoIcon from "@mui/icons-material/Info"; import ExitToAppIcon from "@mui/icons-material/ExitToApp"; import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; diff --git a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx index 25ded60..82083b5 100644 --- a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx +++ b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx @@ -242,7 +242,7 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { }, []); return ( - setOpen(false)} open={open} fullWidth maxWidth={"md"}> + setOpen(false)} open={open} fullWidth maxWidth={"xl"}> {open && ( { + const [beforeImg, setBeforeImg] = useState(value ? value : null); + const [beforeFileType, setBeforeFileType] = useState(value ? "image/" : null); + const [beforeFileName, setBeforeFileName] = useState(null); + const [showBeforeImage, setShowBeforeImage] = useState(!value); + + useEffect(() => { + if (value) { + setShowBeforeImage(false); + if (typeof value === "string") { + setBeforeImg(value); + setBeforeFileType("image/"); + } else if (value instanceof File) { + setBeforeImg(URL.createObjectURL(value)); + setBeforeFileType(value.type); + } + } + }, [value]); + + const handleFileChange = (event) => { + const uploadedFile = event.target?.files?.[0]; + if (uploadedFile) { + const fileType = event.target?.files?.[0].type; + const fileName = event.target?.files?.[0].name; + setBeforeImg(URL.createObjectURL(uploadedFile)); + setBeforeFileType(fileType); + setBeforeFileName(fileName); + onChange(uploadedFile); + setShowBeforeImage(false); + } + }; + + return ( + + + {title} + + + {error ? error.message : null} + + ); +}; +export default ImageUpload; diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx new file mode 100644 index 0000000..5e57e5a --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx @@ -0,0 +1,108 @@ +import { Button, DialogActions, DialogContent, FormControlLabel, RadioGroup, Stack } from "@mui/material"; +import { Controller, useForm } from "react-hook-form"; +import Radio from "@mui/material/Radio"; +import StyledForm from "@/core/components/StyledForm"; +import { useState } from "react"; +import RegisterActionDone from "./RegisterActionDone"; +import RegisterActionUndone from "./RegisterActionUndone"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { mixed, object, string } from "yup"; +import BeenhereIcon from "@mui/icons-material/Beenhere"; + +const RegisterActionContent = ({ setOpen, defaultValues, onBaseSubmit }) => { + const [selectedOption, setSelectedOption] = useState("1"); + + const handleOptionChange = (e) => { + setSelectedOption(e.target.value); + }; + const validationSchema = object({ + description: string().required("توضیحات الزامی است"), + start_point: mixed().when("rms_status", { + is: "1", + then: (schema) => schema.required("مکان اقدام الزامی است"), + otherwise: (schema) => schema.notRequired(), + }), + image_before_1: mixed().when("rms_status", { + is: "1", + then: (schema) => schema.required("تصویر قبل از اقدام الزامی است"), + otherwise: (schema) => schema.notRequired(), + }), + image_after_1: mixed().when("rms_status", { + is: "1", + then: (schema) => schema.required("تصویر بعد از اقدام الزامی است"), + otherwise: (schema) => schema.notRequired(), + }), + }); + + const { + control, + handleSubmit, + setValue, + formState: { isSubmitting, errors }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onChange", + }); + + const onSubmit = async (data) => { + console.log(data); + await onBaseSubmit(data); + }; + + return ( + + + + + } + /> + } + label="اقدام انجام شد" + /> + } + /> + } + label="اقدام انجام نشد" + /> + + + {selectedOption === "1" ? ( + + ) : ( + + )} + + + + + + + ); +}; +export default RegisterActionContent; diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx new file mode 100644 index 0000000..8ba2616 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx @@ -0,0 +1,86 @@ +import { Controller, useWatch } from "react-hook-form"; +import { Grid, Stack, TextField } from "@mui/material"; +import ImageUpload from "./ImageUpload"; +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; + +const RegisterActionDone = ({ control, setValue, errors }) => { + const StartPoint = useWatch({ control, name: "rms_start_latlng" }); + return ( + + + + + { + return ( + + ); + }} + /> + + + + + + + ( + + )} + /> + + + ( + + )} + /> + + + + + + + + + + + + ); +}; +export default RegisterActionDone; diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionUndone.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionUndone.jsx new file mode 100644 index 0000000..dd2b43b --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionUndone.jsx @@ -0,0 +1,37 @@ +import { Grid, Stack, TextField } from "@mui/material"; +import { Controller } from "react-hook-form"; + +const RegisterActionUndone = ({ control }) => { + return ( + + + + + { + return ( + + ); + }} + /> + + + + + ); +}; +export default RegisterActionUndone; diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx new file mode 100644 index 0000000..7362f4b --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx @@ -0,0 +1,66 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import RegisterActionContent from "./RegisterActionContent"; +import AppRegistrationIcon from "@mui/icons-material/AppRegistration"; +import useRequest from "@/lib/hooks/useRequest"; +import { REGISTER_COMPLAINTS_LIST } from "@/core/utils/routes"; +const RegisterAction = ({ rowId, mutate }) => { + const requestServer = useRequest({ notificationSuccess: true }); + const [openRegisterActionDialog, setOpenRegisterActionDialog] = useState(false); + const defaultValues = { + rms_status: "1", + description: "", + start_point: "", + image_before_1: null, + image_after_1: null, + }; + const onBaseSubmit = async (result) => { + console.log(result); + const formData = new FormData(); + const rmsLatLng = `${result.start_point.lat},${result.start_point.lng}`; + if (result.rms_status === "1") { + formData.append("rms_description", result.description); + formData.append("rms_status", 1); + formData.append("start_point", rmsLatLng); + formData.append("image_before", result.image_before_1); + formData.append("image_after", result.image_after_1); + } else { + formData.append("rms_description", result.description); + formData.append("rms_status", 2); + } + await requestServer(`${REGISTER_COMPLAINTS_LIST}/${rowId}`, "post", { + data: formData, + }) + .then((res) => { + mutate(); + setOpenRegisterActionDialog(false); + }) + .catch(() => {}); + }; + return ( + <> + + setOpenRegisterActionDialog(true)}> + + + + + ثبت اقدام + + + + ); +}; +export default RegisterAction; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx index 4c9093a..71874db 100644 --- a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx @@ -1,10 +1,12 @@ import { Box } from "@mui/material"; +import RegisterAction from "../Form/registerAction"; import ReferList from "./ReferList"; import Refer from "./Refer"; const RowActions = ({ row, mutate }) => { return ( + diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx index b889295..6125a74 100644 --- a/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx @@ -13,12 +13,12 @@ const RowActions = ({ row, mutate }) => { return ( {hasSupervisePermission && row.original?.status === 0 && ( - + )} {hasSupervisePermission && row.original?.status === 0 && ( - + )} - {hasRestorePermission && } + {hasRestorePermission && } ); }; diff --git a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx index cb13855..3a607c2 100644 --- a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx @@ -67,7 +67,7 @@ const SupervisorList = () => { { accessorKey: "id", header: "کد یکتا", - id: "id", + id: "road_observeds__id", enableColumnFilter: true, datatype: "text", filterMode: "equals", @@ -91,7 +91,7 @@ const SupervisorList = () => { ...(hasCountryPermission ? [dynamicColumns] : []), { header: "اداره", - id: "edarat_id", + id: "city_id", enableColumnFilter: true, datatype: "numeric", filterMode: "equals", @@ -272,7 +272,7 @@ const SupervisorList = () => { { accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", - id: "created_at", + id: "road_observeds__created_at", enableColumnFilter: true, datatype: "date", filterMode: "between", @@ -280,7 +280,11 @@ const SupervisorList = () => { size: 100, }, { - accessorFn: (row) => moment(row.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => { + return row.rms_last_activity + ? moment(row?.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD") + : "بدون تاریخ"; + }, header: "تاریخ اقدام", id: "rms_last_activity", enableColumnFilter: true, diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 7fa077c..ebcf2ce 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -116,6 +116,7 @@ export const GET_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/superv export const GET_FAST_REACT_COMPLAINTS = api + "/api/v3/road_observations/complaints_index"; export const VERIFY_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/verify"; export const REJECT_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/verify"; +export const REGISTER_COMPLAINTS_LIST = api + "/api/v3/road_observations/register"; export const GET_FAST_REACT_OPERATOR = api + "/api/v3/road_observations/operator_index"; export const GET_FAST_REACT_REFER_LIST = api + "/api/v3/road_observations/refer_list"; export const REFER_FAST_REACT = api + "/api/v3/road_observations/refer"; From 63f5dc8f5f67ce364b71582fd8acaaaeac1a1e6c Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Thu, 27 Feb 2025 16:38:20 +0330 Subject: [PATCH 25/32] fixed bug --- src/core/middlewares/withPermission.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/middlewares/withPermission.js b/src/core/middlewares/withPermission.js index 0da81c9..9a7d3a6 100644 --- a/src/core/middlewares/withPermission.js +++ b/src/core/middlewares/withPermission.js @@ -14,7 +14,7 @@ function WithPermission({ children, permission_name }) { } }, [data]); - if (error || isLoading || !cachedData || !permission_name) { + if (isLoading || !cachedData || !permission_name) { return null; } From 8f7368585d0cb779a769e7fa0462f75750d8a823 Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Sat, 1 Mar 2025 10:56:22 +0000 Subject: [PATCH 26/32] Feature/edit complaint --- .../complaintList/ComplaintListTable.jsx | 4 +- .../complaintList/ExcelPrint/index.jsx | 74 +++++++++++++++++++ .../registerAction/RegisterActionContent.jsx | 4 +- .../registerAction/RegisterActionDone.jsx | 2 +- .../complaintList/RowActions/index.jsx | 6 +- .../fastReact/complaintList/Toolbar.jsx | 7 +- .../fastReact/operator/ExcelPrint/index.jsx | 6 +- .../operator/Form/EditForm/EditController.jsx | 72 ++++++++++++++++++ .../operator/Form/EditForm/index.jsx | 59 +++++++++++++++ .../fastReact/operator/OperatorList.jsx | 14 +++- .../fastReact/operator/RowActions/index.jsx | 9 ++- .../fastReact/supervisor/ExcelPrint/index.jsx | 6 +- .../fastReact/supervisor/SupervisorList.jsx | 10 ++- src/core/utils/routes.js | 5 ++ 14 files changed, 255 insertions(+), 23 deletions(-) create mode 100644 src/components/dashboard/fastReact/complaintList/ExcelPrint/index.jsx create mode 100644 src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx create mode 100644 src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx diff --git a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx index 82083b5..be9a79c 100644 --- a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx +++ b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx @@ -57,7 +57,7 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { { accessorKey: "id", header: "کد یکتا", - id: "id", + id: "road_observeds__id", enableColumnFilter: true, datatype: "text", filterMode: "equals", @@ -81,7 +81,7 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { ...(hasCountryPermission ? [dynamicColumns] : []), { header: "اداره", - id: "edarat_id", + id: "road_observeds__edarate_shahri_id", enableColumnFilter: true, datatype: "numeric", filterMode: "equals", diff --git a/src/components/dashboard/fastReact/complaintList/ExcelPrint/index.jsx b/src/components/dashboard/fastReact/complaintList/ExcelPrint/index.jsx new file mode 100644 index 0000000..05f3362 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/ExcelPrint/index.jsx @@ -0,0 +1,74 @@ +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; +import { useMemo, useState } from "react"; +import moment from "jalali-moment"; +import FileSaver from "file-saver"; +import DescriptionIcon from "@mui/icons-material/Description"; +import useRequest from "@/lib/hooks/useRequest"; +import { EXPORT_FAST_REACT_COMPLAINT_LIST } from "@/core/utils/routes"; +import { useTheme } from "@emotion/react"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; + +const PrintExcel = ({ table, filterData }) => { + const [loading, setLoading] = useState(false); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const requestServer = useRequest(); + const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { + if (filter.value && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) { + acc.push({ id: key, value: filter.value }); + } + return acc; + }, []); + + const filterParams = useMemo(() => { + const params = new URLSearchParams(); + if (activeFilters.length > 0) { + activeFilters.map((filter, index) => { + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); + }); + } else { + params.set("filters", JSON.stringify([])); + } + return params; + }, [activeFilters]); + + const clickHandler = () => { + setLoading(true); + requestServer(`${EXPORT_FAST_REACT_COMPLAINT_LIST}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی لیست شکایات واکنش سریع تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + ); +}; +export default PrintExcel; diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx index 5e57e5a..34d4130 100644 --- a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx @@ -10,7 +10,7 @@ import { mixed, object, string } from "yup"; import BeenhereIcon from "@mui/icons-material/Beenhere"; const RegisterActionContent = ({ setOpen, defaultValues, onBaseSubmit }) => { - const [selectedOption, setSelectedOption] = useState("1"); + const [selectedOption, setSelectedOption] = useState(defaultValues?.rms_status || "1"); const handleOptionChange = (e) => { setSelectedOption(e.target.value); @@ -46,10 +46,8 @@ const RegisterActionContent = ({ setOpen, defaultValues, onBaseSubmit }) => { }); const onSubmit = async (data) => { - console.log(data); await onBaseSubmit(data); }; - return ( diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx index 8ba2616..567832d 100644 --- a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionDone.jsx @@ -4,7 +4,7 @@ import ImageUpload from "./ImageUpload"; import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; const RegisterActionDone = ({ control, setValue, errors }) => { - const StartPoint = useWatch({ control, name: "rms_start_latlng" }); + const StartPoint = useWatch({ control, name: "start_point" }); return ( diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx index 71874db..0cae4b8 100644 --- a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx @@ -6,9 +6,9 @@ import Refer from "./Refer"; const RowActions = ({ row, mutate }) => { return ( - - - + + + ); }; diff --git a/src/components/dashboard/fastReact/complaintList/Toolbar.jsx b/src/components/dashboard/fastReact/complaintList/Toolbar.jsx index 1f291d1..22d8bd3 100644 --- a/src/components/dashboard/fastReact/complaintList/Toolbar.jsx +++ b/src/components/dashboard/fastReact/complaintList/Toolbar.jsx @@ -1,6 +1,11 @@ import { Stack } from "@mui/material"; +import PrintExcel from "./ExcelPrint"; const Toolbar = ({ table, filterData, mutate }) => { - return ; + return ( + + + + ); }; export default Toolbar; diff --git a/src/components/dashboard/fastReact/operator/ExcelPrint/index.jsx b/src/components/dashboard/fastReact/operator/ExcelPrint/index.jsx index ae703fa..c1e2bea 100644 --- a/src/components/dashboard/fastReact/operator/ExcelPrint/index.jsx +++ b/src/components/dashboard/fastReact/operator/ExcelPrint/index.jsx @@ -4,7 +4,7 @@ import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; -import { EXPORT_ROAD_ITEMS_SUPERVISOR_LIST } from "@/core/utils/routes"; +import { EXPORT_FAST_REACT_OPERATOR_LIST } from "@/core/utils/routes"; import { useTheme } from "@emotion/react"; import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; import isArrayEmpty from "@/core/utils/isArrayEmpty"; @@ -36,11 +36,11 @@ const PrintExcel = ({ table, filterData }) => { const clickHandler = () => { setLoading(true); - requestServer(`${EXPORT_ROAD_ITEMS_SUPERVISOR_LIST}?${filterParams}`, "get", { + requestServer(`${EXPORT_FAST_REACT_OPERATOR_LIST}?${filterParams}`, "get", { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل ارزیابی فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل عملیات واکنش سریع تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) diff --git a/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx b/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx new file mode 100644 index 0000000..5025d54 --- /dev/null +++ b/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx @@ -0,0 +1,72 @@ +import useRequest from "@/lib/hooks/useRequest"; +import { useEffect, useState } from "react"; +import { GET_FAST_REACT_ITEM_DETAIL, UPDATE_FAST_REACT } from "@/core/utils/routes"; +import DialogLoading from "@/core/components/DialogLoading"; +import RegisterActionContent from "@/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent"; + +const EditController = ({ rowId, mutate, setOpenEditDialog }) => { + const requestServer = useRequest(); + const [fastReactItemDetails, setFastReactItemDetails] = useState(null); + const [fastReactItemDetailsLoading, setFastReactItemDetailsLoading] = useState(false); + + useEffect(() => { + setFastReactItemDetailsLoading(true); + requestServer(`${GET_FAST_REACT_ITEM_DETAIL}/${rowId}`, "get") + .then((response) => { + setFastReactItemDetails(response.data.data); + setFastReactItemDetailsLoading(false); + }) + .catch((e) => { + setFastReactItemDetailsLoading(false); + }); + }, [rowId]); + const startPoint = fastReactItemDetails?.rms_start_latlng + ? { lat: fastReactItemDetails?.rms_start_latlng[0], lng: fastReactItemDetails?.rms_start_latlng[1] } + : { lat: "", lng: "" }; + const defaultData = { + rms_status: `${fastReactItemDetails?.rms_status || "1"}`, + description: fastReactItemDetails?.rms_description || "", + start_point: startPoint, + image_before_1: fastReactItemDetails?.image_before || null, + image_after_1: fastReactItemDetails?.image_after || null, + }; + const HandleSubmit = async (result) => { + console.log("result", result); + const formData = new FormData(); + const rmsLatLng = `${result.start_point.lat},${result.start_point.lng}`; + if (result.rms_status === "1") { + formData.append("rms_description", result.description); + formData.append("rms_status", 1); + formData.append("start_point", rmsLatLng); + result.image_before_1 !== defaultData.image_before_1 && + formData.append("image_before", result.image_before_1); + result.image_after_1 !== defaultData.image_after_1 && formData.append("image_after", result.image_after_1); + } else { + formData.append("rms_description", result.description); + formData.append("rms_status", 2); + } + + await requestServer(`${UPDATE_FAST_REACT}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenEditDialog(false); + }) + .catch(() => {}); + }; + return ( + <> + {fastReactItemDetailsLoading ? ( + + ) : ( + + )} + + ); +}; +export default EditController; diff --git a/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx b/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx new file mode 100644 index 0000000..e608b32 --- /dev/null +++ b/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx @@ -0,0 +1,59 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import BorderColorIcon from "@mui/icons-material/BorderColor"; +import EditController from "./EditController"; +import CloseIcon from "@mui/icons-material/Close"; + +const EditForm = ({ row, mutate, rowId }) => { + const [openEditDialog, setOpenEditDialog] = useState(false); + + return ( + <> + + { + setOpenEditDialog(true); + }} + > + + + + + setOpenEditDialog(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {openEditDialog && ( + + )} + + + ); +}; +export default EditForm; diff --git a/src/components/dashboard/fastReact/operator/OperatorList.jsx b/src/components/dashboard/fastReact/operator/OperatorList.jsx index 0234f2b..cc837b7 100644 --- a/src/components/dashboard/fastReact/operator/OperatorList.jsx +++ b/src/components/dashboard/fastReact/operator/OperatorList.jsx @@ -118,9 +118,13 @@ const OperatorList = () => { }, }, Cell: ({ row }) => { + if (!row.original.rms_start_latlng) return <>بدون موقعیت; return ( - + ); }, @@ -171,7 +175,7 @@ const OperatorList = () => { { accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", - id: "created_at", + id: "road_observeds__created_at", enableColumnFilter: true, datatype: "date", filterMode: "between", @@ -179,7 +183,11 @@ const OperatorList = () => { size: 100, }, { - accessorFn: (row) => moment(row.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => { + return row.rms_last_activity + ? moment(row?.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD") + : "بدون تاریخ"; + }, header: "تاریخ اقدام", id: "rms_last_activity", enableColumnFilter: true, diff --git a/src/components/dashboard/fastReact/operator/RowActions/index.jsx b/src/components/dashboard/fastReact/operator/RowActions/index.jsx index 9fb298d..5b632d6 100644 --- a/src/components/dashboard/fastReact/operator/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/operator/RowActions/index.jsx @@ -1,6 +1,13 @@ import { Box } from "@mui/material"; +import EditForm from "../Form/EditForm"; const RowActions = ({ row, mutate }) => { - return ; + return ( + + {row.original.status === 2 && ( + + )} + + ); }; export default RowActions; diff --git a/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx index ae703fa..120a8f0 100644 --- a/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx +++ b/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx @@ -4,7 +4,7 @@ import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; -import { EXPORT_ROAD_ITEMS_SUPERVISOR_LIST } from "@/core/utils/routes"; +import { EXPORT_FAST_REACT_SUPERVISOR_LIST } from "@/core/utils/routes"; import { useTheme } from "@emotion/react"; import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; import isArrayEmpty from "@/core/utils/isArrayEmpty"; @@ -36,11 +36,11 @@ const PrintExcel = ({ table, filterData }) => { const clickHandler = () => { setLoading(true); - requestServer(`${EXPORT_ROAD_ITEMS_SUPERVISOR_LIST}?${filterParams}`, "get", { + requestServer(`${EXPORT_FAST_REACT_SUPERVISOR_LIST}?${filterParams}`, "get", { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل ارزیابی فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل ارزیابی واکنش سریع تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) diff --git a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx index 3a607c2..be912f1 100644 --- a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx @@ -91,7 +91,7 @@ const SupervisorList = () => { ...(hasCountryPermission ? [dynamicColumns] : []), { header: "اداره", - id: "city_id", + id: "edarate_shahri_id", enableColumnFilter: true, datatype: "numeric", filterMode: "equals", @@ -144,7 +144,7 @@ const SupervisorList = () => { /> ); }, - Cell: ({ renderedCellValue, row }) => <>{row.original.city_fa}, + Cell: ({ renderedCellValue, row }) => <>{row.original.name_fa}, }, { accessorKey: "Title", @@ -219,9 +219,13 @@ const SupervisorList = () => { }, }, Cell: ({ row }) => { + if (!row.original.rms_start_latlng) return <>بدون موقعیت; return ( - + ); }, diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index ebcf2ce..d7d4193 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -121,3 +121,8 @@ export const GET_FAST_REACT_OPERATOR = api + "/api/v3/road_observations/operator export const GET_FAST_REACT_REFER_LIST = api + "/api/v3/road_observations/refer_list"; export const REFER_FAST_REACT = api + "/api/v3/road_observations/refer"; export const RESTORE_FAST_REACT = api + "/api/v3/road_observations/restore"; +export const GET_FAST_REACT_ITEM_DETAIL = api + "/api/v3/road_observations"; +export const UPDATE_FAST_REACT = api + "/api/v3/road_observations/modify_registration"; +export const EXPORT_FAST_REACT_SUPERVISOR_LIST = api + "/api/v3/road_observations/supervisor_report"; +export const EXPORT_FAST_REACT_OPERATOR_LIST = api + "/api/v3/road_observations/operator_report"; +export const EXPORT_FAST_REACT_COMPLAINT_LIST = api + "/api/v3/road_observations/complaints_report"; From cedc29ff5e0a02ce90b5a87853a4d2252c73d7a6 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 1 Mar 2025 15:38:38 +0330 Subject: [PATCH 27/32] change columns --- .../complaintList/ComplaintListTable.jsx | 230 +++++++------- .../fastReact/operator/OperatorList.jsx | 290 ++++++++--------- .../fastReact/supervisor/SupervisorList.jsx | 294 +++++++++--------- 3 files changed, 422 insertions(+), 392 deletions(-) diff --git a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx index be9a79c..90102fc 100644 --- a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx +++ b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx @@ -66,18 +66,6 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { grow: false, size: 100, }, - { - accessorKey: "fk_RegisteredEventMessage", - header: "کد سوانح", - id: "fk_RegisteredEventMessage", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, ...(hasCountryPermission ? [dynamicColumns] : []), { header: "اداره", @@ -127,8 +115,8 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { props.dependencyFieldValue?.value === "" ? "empty" : loadingEdaratList - ? "loading" - : props.filterParameters.value + ? "loading" + : props.filterParameters.value } columnSelectOption={getColumnSelectOptions} /> @@ -137,107 +125,129 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { Cell: ({ row }) => <>{row.original.edarate_shahri_name_fa}, }, { - accessorKey: "Title", - header: "موضوع گزارش", - id: "Title", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorKey: "FeatureTypeTitle", - header: "نوع گزارش", - id: "FeatureTypeTitle", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorKey: "Description", - header: "توضیح گزارش", - id: "Description", - enableColumnFilter: false, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - if (renderedCellValue) { - return ( - - - - ); - } - return بدون توضیحات; - }, - }, - { - header: "موقعیت", - id: "location", + header: "اطلاعات ثبت‌ شده در سامانه سوانح", + id: "fkInfo", enableColumnFilter: false, enableSorting: false, - datatype: "array", grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", + size: 50, + columns: [ + { + accessorKey: "fk_RegisteredEventMessage", + header: "کد سوانح", + id: "fk_RegisteredEventMessage", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Title", + header: "موضوع گزارش", + id: "Title", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "FeatureTypeTitle", + header: "نوع گزارش", + id: "FeatureTypeTitle", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Description", + header: "توضیح گزارش", + id: "Description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون توضیحات; }, }, - }, - Cell: ({ row }) => { - return ( - - - - ); - }, - }, - { - accessorKey: "MobileForSendEventSms", - header: "شماره تماس گیرنده", - id: "MobileForSendEventSms", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), - header: "تاریخ ثبت", - id: "created_at", - enableColumnFilter: true, - datatype: "date", - filterMode: "between", - grow: false, - size: 100, - }, + { + header: "موقعیت", + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + return ( + + + + ); + }, + }, + { + accessorKey: "MobileForSendEventSms", + header: "شماره تماس گیرنده", + id: "MobileForSendEventSms", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorFn: (row) => moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A").locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "StartTime_DateTime", + enableColumnFilter: false, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + ] + } ]; }, []); diff --git a/src/components/dashboard/fastReact/operator/OperatorList.jsx b/src/components/dashboard/fastReact/operator/OperatorList.jsx index cc837b7..3ff010b 100644 --- a/src/components/dashboard/fastReact/operator/OperatorList.jsx +++ b/src/components/dashboard/fastReact/operator/OperatorList.jsx @@ -34,153 +34,163 @@ const OperatorList = () => { size: 100, }, { - accessorKey: "fk_RegisteredEventMessage", - header: "کد سوانح", - id: "fk_RegisteredEventMessage", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorKey: "Title", - header: "موضوع گزارش", - id: "Title", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorKey: "FeatureTypeTitle", - header: "نوع گزارش", - id: "FeatureTypeTitle", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorKey: "Description", - header: "توضیح گزارش", - id: "Description", - enableColumnFilter: false, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - if (renderedCellValue) { - return ( - - - - ); - } - return بدون توضیحات; - }, - }, - { - header: "موقعیت", - id: "location", + header: "اطلاعات ثبت‌ شده در سامانه سوانح", + id: "fkInfo", enableColumnFilter: false, enableSorting: false, - datatype: "array", grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", + size: 50, + columns: [ + { + accessorKey: "fk_RegisteredEventMessage", + header: "کد سوانح", + id: "fk_RegisteredEventMessage", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Title", + header: "موضوع گزارش", + id: "Title", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "FeatureTypeTitle", + header: "نوع گزارش", + id: "FeatureTypeTitle", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Description", + header: "توضیح گزارش", + id: "Description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون توضیحات; }, }, - }, - Cell: ({ row }) => { - if (!row.original.rms_start_latlng) return <>بدون موقعیت; - return ( - - - - ); - }, - }, - { - header: "تصاویر", - id: "images", - enableColumnFilter: false, - enableSorting: false, - datatype: "array", - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", + { + header: "موقعیت", + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + if (!row.original.rms_start_latlng) return <>بدون موقعیت; + return ( + + + + ); }, }, - }, - Cell: ({ row }) => { - if (!row.original?.image_before && !row.original?.image_after) return <>بدون تصویر; - return ( - - {row.original?.image_before && row.original?.image_after && ( - - )} - - ); - }, - }, - { - accessorKey: "MobileForSendEventSms", - header: "شماره تماس گیرنده", - id: "MobileForSendEventSms", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), - header: "تاریخ ثبت", - id: "road_observeds__created_at", - enableColumnFilter: true, - datatype: "date", - filterMode: "between", - grow: false, - size: 100, + { + header: "تصاویر", + id: "images", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + if (!row.original?.image_before && !row.original?.image_after) return <>بدون تصویر; + return ( + + {row.original?.image_before && row.original?.image_after && ( + + )} + + ); + }, + }, + { + accessorKey: "MobileForSendEventSms", + header: "شماره تماس گیرنده", + id: "MobileForSendEventSms", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorFn: (row) => moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A").locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "StartTime_DateTime", + enableColumnFilter: false, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + ] }, { accessorFn: (row) => { diff --git a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx index be912f1..89eb53c 100644 --- a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx @@ -76,18 +76,6 @@ const SupervisorList = () => { grow: false, size: 100, }, - { - accessorKey: "fk_RegisteredEventMessage", - header: "کد سوانح", - id: "fk_RegisteredEventMessage", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, ...(hasCountryPermission ? [dynamicColumns] : []), { header: "اداره", @@ -137,8 +125,8 @@ const SupervisorList = () => { props.dependencyFieldValue?.value === "" ? "empty" : loadingEdaratList - ? "loading" - : props.filterParameters.value + ? "loading" + : props.filterParameters.value } columnSelectOption={getColumnSelectOptions} /> @@ -147,141 +135,163 @@ const SupervisorList = () => { Cell: ({ renderedCellValue, row }) => <>{row.original.name_fa}, }, { - accessorKey: "Title", - header: "موضوع گزارش", - id: "Title", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorKey: "FeatureTypeTitle", - header: "نوع گزارش", - id: "FeatureTypeTitle", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorKey: "Description", - header: "توضیح گزارش", - id: "Description", - enableColumnFilter: false, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", - }, - }, - }, - Cell: ({ renderedCellValue }) => { - if (renderedCellValue) { - return ( - - - - ); - } - return بدون توضیحات; - }, - }, - { - header: "موقعیت", - id: "location", + header: "اطلاعات ثبت‌ شده در سامانه سوانح", + id: "fkInfo", enableColumnFilter: false, enableSorting: false, - datatype: "array", grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", + size: 50, + columns: [ + { + accessorKey: "fk_RegisteredEventMessage", + header: "کد سوانح", + id: "fk_RegisteredEventMessage", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Title", + header: "موضوع گزارش", + id: "Title", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "FeatureTypeTitle", + header: "نوع گزارش", + id: "FeatureTypeTitle", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Description", + header: "توضیح گزارش", + id: "Description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون توضیحات; }, }, - }, - Cell: ({ row }) => { - if (!row.original.rms_start_latlng) return <>بدون موقعیت; - return ( - - - - ); - }, - }, - { - header: "تصاویر", - id: "images", - enableColumnFilter: false, - enableSorting: false, - datatype: "array", - grow: false, - size: 100, - muiTableBodyCellProps: { - sx: { - borderLeft: "1px solid #e1e1e1", - py: 0, - "&:first-of-type": { - borderLeft: "unset", + { + header: "موقعیت", + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + if (!row.original.rms_start_latlng) return <>بدون موقعیت; + return ( + + + + ); }, }, - }, - Cell: ({ row }) => { - if (!row.original?.image_before && !row.original?.image_after) return <>بدون تصویر; - return ( - - {row.original?.image_before && row.original?.image_after && ( - - )} - - ); - }, - }, - { - accessorKey: "MobileForSendEventSms", - header: "شماره تماس گیرنده", - id: "MobileForSendEventSms", - enableColumnFilter: true, - datatype: "text", - filterMode: "equals", - sortDescFirst: false, - columnFilterModeOptions: ["equals", "contains"], - grow: false, - size: 100, - }, - { - accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), - header: "تاریخ ثبت", - id: "road_observeds__created_at", - enableColumnFilter: true, - datatype: "date", - filterMode: "between", - grow: false, - size: 100, + { + header: "تصاویر", + id: "images", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + if (!row.original?.image_before && !row.original?.image_after) return <>بدون تصویر; + return ( + + {row.original?.image_before && row.original?.image_after && ( + + )} + + ); + }, + }, + { + accessorKey: "MobileForSendEventSms", + header: "شماره تماس گیرنده", + id: "MobileForSendEventSms", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorFn: (row) => moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A").locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "StartTime_DateTime", + enableColumnFilter: false, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + ] }, { accessorFn: (row) => { From 09749d4881b494dc0a1e36ab309ad6f5eae31714 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 1 Mar 2025 15:44:23 +0330 Subject: [PATCH 28/32] fixed bug --- .../dashboard/fastReact/complaintList/RowActions/index.jsx | 7 ++++++- .../dashboard/fastReact/operator/Form/EditForm/index.jsx | 7 +++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx index 0cae4b8..e9972aa 100644 --- a/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx @@ -2,11 +2,16 @@ import { Box } from "@mui/material"; import RegisterAction from "../Form/registerAction"; import ReferList from "./ReferList"; import Refer from "./Refer"; +import { usePermissions } from "@/lib/hooks/usePermissions"; const RowActions = ({ row, mutate }) => { + const { data: userPermissions } = usePermissions(); + const hasActionPermission = userPermissions.includes("show-fast-react-edarate-shahri") return ( - + {hasActionPermission && ( + + )} diff --git a/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx b/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx index e608b32..0af8244 100644 --- a/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx +++ b/src/components/dashboard/fastReact/operator/Form/EditForm/index.jsx @@ -1,8 +1,8 @@ -import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; -import { useState } from "react"; import BorderColorIcon from "@mui/icons-material/BorderColor"; -import EditController from "./EditController"; import CloseIcon from "@mui/icons-material/Close"; +import { Dialog, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import EditController from "./EditController"; const EditForm = ({ row, mutate, rowId }) => { const [openEditDialog, setOpenEditDialog] = useState(false); @@ -21,7 +21,6 @@ const EditForm = ({ row, mutate, rowId }) => { Date: Sat, 1 Mar 2025 15:51:51 +0330 Subject: [PATCH 29/32] fixed bug --- .../dashboard/fastReact/supervisor/RowActions/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx index 6125a74..f9947ab 100644 --- a/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx +++ b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx @@ -18,7 +18,7 @@ const RowActions = ({ row, mutate }) => { {hasSupervisePermission && row.original?.status === 0 && ( )} - {hasRestorePermission && } + {hasRestorePermission && row.original?.status !== 0 && } ); }; From af9be31dbf2ca17655ff54dc73e1866e6fe6e8a2 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 1 Mar 2025 16:42:37 +0330 Subject: [PATCH 30/32] fixed bug --- .../operator/RowActions/ImageDialog/ImageFormContent.jsx | 3 ++- .../operator/RowActions/ImageDialog/ImageFormContent.jsx | 3 ++- .../supervisor/RowActions/ImageDialog/ImageFormContent.jsx | 3 ++- .../operator/RowActions/ImageForm/ImageFormContent.jsx | 3 ++- .../supervisor/RowActions/ImageForm/ImageFormContent.jsx | 1 + 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx index 9aaa92e..c8f22d0 100644 --- a/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx +++ b/src/components/dashboard/damages/operator/RowActions/ImageDialog/ImageFormContent.jsx @@ -1,4 +1,4 @@ -import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; +import { Box, Stack, Typography, useTheme } from "@mui/material"; import Image from "next/image"; const ImageFormContent = ({ image, title }) => { @@ -29,6 +29,7 @@ const ImageFormContent = ({ image, title }) => { src={image} alt="Image" fill={true} + sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" loading="lazy" style={{ objectFit: "contain", diff --git a/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/ImageFormContent.jsx index 9aaa92e..c8f22d0 100644 --- a/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/ImageFormContent.jsx +++ b/src/components/dashboard/fastReact/operator/RowActions/ImageDialog/ImageFormContent.jsx @@ -1,4 +1,4 @@ -import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; +import { Box, Stack, Typography, useTheme } from "@mui/material"; import Image from "next/image"; const ImageFormContent = ({ image, title }) => { @@ -29,6 +29,7 @@ const ImageFormContent = ({ image, title }) => { src={image} alt="Image" fill={true} + sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" loading="lazy" style={{ objectFit: "contain", diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx index 9aaa92e..c8f22d0 100644 --- a/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx +++ b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx @@ -1,4 +1,4 @@ -import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; +import { Box, Stack, Typography, useTheme } from "@mui/material"; import Image from "next/image"; const ImageFormContent = ({ image, title }) => { @@ -29,6 +29,7 @@ const ImageFormContent = ({ image, title }) => { src={image} alt="Image" fill={true} + sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" loading="lazy" style={{ objectFit: "contain", diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx index 9aaa92e..c8f22d0 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx @@ -1,4 +1,4 @@ -import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; +import { Box, Stack, Typography, useTheme } from "@mui/material"; import Image from "next/image"; const ImageFormContent = ({ image, title }) => { @@ -29,6 +29,7 @@ const ImageFormContent = ({ image, title }) => { src={image} alt="Image" fill={true} + sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" loading="lazy" style={{ objectFit: "contain", diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx index 9aaa92e..9ddb19b 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx @@ -29,6 +29,7 @@ const ImageFormContent = ({ image, title }) => { src={image} alt="Image" fill={true} + sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" loading="lazy" style={{ objectFit: "contain", From 437891a7086e5861e2b9d2d8bd8d067883fc2083 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sun, 2 Mar 2025 13:48:46 +0330 Subject: [PATCH 31/32] fixed bug --- .../roadPatrols/operator/OperatorList.jsx | 3 +- .../MachinePerformanceContent.jsx | 2 +- .../MachinesCodeFetch.jsx | 49 +++++++++++++++ .../MachinePerformanceForm/index.jsx | 18 ++---- .../MachinePerformanceContent.jsx | 2 +- .../MachinesCodeFetch.jsx | 49 +++++++++++++++ .../MachinePerformanceForm/StopsInfo.jsx | 61 +++---------------- .../MachinePerformanceForm/index.jsx | 18 ++---- .../roadPatrols/supervisor/SupervisorList.jsx | 6 +- src/core/utils/routes.js | 3 +- 10 files changed, 123 insertions(+), 88 deletions(-) create mode 100644 src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx create mode 100644 src/components/dashboard/roadPatrols/supervisor/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx diff --git a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx index f9e144d..52bb4b4 100644 --- a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx +++ b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx @@ -7,9 +7,10 @@ import { GET_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes"; import moment from "jalali-moment"; import RowActions from "./RowActions"; import ReportForm from "./RowActions/ReportForm"; -import RahdaranDialog from "./RowActions/RahdaranForm"; +import RahdaranDialog from "./RowActions/RahdaranForm" import MachinePerformanceForm from "./RowActions/MachinePerformanceForm"; + const OperatorList = () => { const columns = useMemo( () => [ diff --git a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx index 8570f58..ead48ff 100644 --- a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx +++ b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinePerformanceContent.jsx @@ -2,11 +2,11 @@ import { Box, Chip, DialogContent, Divider, Grid, Stack, Typography } from "@mui import React, { useEffect, useState, useRef } from "react"; import { Marker, useMap } from "react-leaflet"; import AzmayeshActiveIcon from "@/assets/images/examine_marker_active.png"; -import StopsInfo from "@/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/StopsInfo"; import MapLayer from "@/core/components/MapLayer"; import PinDropIcon from "@mui/icons-material/PinDrop"; import AccessAlarmsIcon from "@mui/icons-material/AccessAlarms"; import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS"; +import StopsInfo from "./StopsInfo"; const defaultIconSize = [35, 35]; const MachinePerformanceContent = ({ machinesLists }) => { const [selectedPoint, setSelectedPoint] = useState(null); diff --git a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx new file mode 100644 index 0000000..0eb47d2 --- /dev/null +++ b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/MachinesCodeFetch.jsx @@ -0,0 +1,49 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_CMMS_MACHINE_ROAD_PATROL } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { DialogContent, Typography } from "@mui/material"; +import { useEffect, useState } from "react"; +import MachinePerformanceContent from "./MachinePerformanceContent"; + +const MachinePerformanceFetch = ({ row }) => { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + const request = useRequest(); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const response = await request(`${GET_CMMS_MACHINE_ROAD_PATROL}/${row.getValue('id')}`); + setData(response.data.data); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); + + return ( + <> + + {loading ? ( + + ) : data && row.original?.stop_points?.length > 0 ? ( + + ) : ( + عملکرد خودرویی در سامانه یافت نشد + )} + + + ) +} +export default MachinePerformanceFetch \ No newline at end of file diff --git a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx index 8269166..c8dcb9e 100644 --- a/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx +++ b/src/components/dashboard/roadPatrols/operator/RowActions/MachinePerformanceForm/index.jsx @@ -1,18 +1,10 @@ -import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip, Typography } from "@mui/material"; import DirectionsCarIcon from "@mui/icons-material/DirectionsCar"; -import MachinePerformanceContent from "./MachinePerformanceContent"; +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; import { useState } from "react"; +import MachinePerformanceFetch from "./MachinesCodeFetch"; const MachinePerformanceForm = ({ row }) => { const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false); - const machineData = { - cmms_machine_code: row.original?.cmms_machines[0], - resultCode: 0, - mileage: row.original?.distance, - accOnDuration: row.original?.vehicle_runtime, - fuelConsumption: row.original?.fuel_consumption, - stopPoints: row.original?.stop_points, - }; return ( <> @@ -33,10 +25,8 @@ const MachinePerformanceForm = ({ row }) => { maxWidth={"md"} > عملکرد خودرو - {row.original?.stop_points ? ( - - ) : ( - عملکردی در سامانه ثبت نشده است. + {openMachinesCodeDialog && ( + )}