From fd63972a75e997fb0565fdaeafba197651460e97 Mon Sep 17 00:00:00 2001 From: AmirHossein Mahmoodi Date: Sat, 12 Apr 2025 12:08:48 +0000 Subject: [PATCH] Feature/road safety map --- .../dashboard/road-safety/reports/map/page.js | 9 + .../road-safety/{report => reports}/page.js | 2 +- .../complaintList/ComplaintListTable.jsx | 5 +- .../fastReact/operator/OperatorList.jsx | 5 +- .../fastReact/supervisor/SupervisorList.jsx | 5 +- .../roadSafety/operator/OperatorList.jsx | 10 +- .../reports/map/ClusterSwitch/index.jsx | 25 +++ .../reports/map/Filter/Drawer/ActionDate.jsx | 32 ++++ .../Filter/Drawer/ActivityDateTimeFilter.jsx | 32 ++++ .../map/Filter/Drawer/AxisTypeFilter.jsx | 37 ++++ .../map/Filter/Drawer/CreatedAtFilter.jsx | 32 ++++ .../map/Filter/Drawer/EdarehShahriFilter.jsx | 65 +++++++ .../reports/map/Filter/Drawer/InfoFilter.jsx | 47 +++++ .../JudiciaryDocumentUploadDateFilter.jsx | 32 ++++ .../map/Filter/Drawer/ProvinceFilter.jsx | 47 +++++ .../reports/map/Filter/Drawer/StepFilter.jsx | 35 ++++ .../reports/map/Filter/Drawer/index.jsx | 166 ++++++++++++++++++ .../roadSafety/reports/map/Filter/index.jsx | 44 +++++ .../roadSafety/reports/map/Legend/index.jsx | 48 +++++ .../DialogInfoItem/ContentInfoItem.jsx | 94 ++++++++++ .../map/PointsOnMap/DialogInfoItem/index.jsx | 51 ++++++ .../reports/map/PointsOnMap/index.jsx | 71 ++++++++ .../roadSafety/reports/map/index.jsx | 160 +++++++++++++++++ .../reportPage}/ExcelPrint/index.jsx | 0 .../reportPage}/ReportLists.jsx | 0 .../reportPage}/Search/FromDateController.jsx | 0 .../reportPage}/Search/SearchReportField.jsx | 0 .../reportPage}/Search/SelectAxisType.jsx | 0 .../reportPage}/Search/SelectProvince.jsx | 0 .../reportPage}/Search/ToDateController.jsx | 0 .../reportPage}/Search/index.jsx | 0 .../reportPage}/Toolbar.jsx | 0 .../{report => reports/reportPage}/index.jsx | 0 .../Sidebar/SidebarSubitems.jsx | 5 +- src/core/utils/pageMenu.js | 12 +- src/core/utils/routes.js | 2 + 36 files changed, 1049 insertions(+), 24 deletions(-) create mode 100644 src/app/(withAuth)/dashboard/road-safety/reports/map/page.js rename src/app/(withAuth)/dashboard/road-safety/{report => reports}/page.js (97%) create mode 100644 src/components/dashboard/roadSafety/reports/map/ClusterSwitch/index.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActionDate.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActivityDateTimeFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/AxisTypeFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/CreatedAtFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/EdarehShahriFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/InfoFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/JudiciaryDocumentUploadDateFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ProvinceFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/StepFilter.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/Drawer/index.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Filter/index.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/Legend/index.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/index.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/PointsOnMap/index.jsx create mode 100644 src/components/dashboard/roadSafety/reports/map/index.jsx rename src/components/dashboard/roadSafety/{report => reports/reportPage}/ExcelPrint/index.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/ReportLists.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/Search/FromDateController.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/Search/SearchReportField.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/Search/SelectAxisType.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/Search/SelectProvince.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/Search/ToDateController.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/Search/index.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/Toolbar.jsx (100%) rename src/components/dashboard/roadSafety/{report => reports/reportPage}/index.jsx (100%) diff --git a/src/app/(withAuth)/dashboard/road-safety/reports/map/page.js b/src/app/(withAuth)/dashboard/road-safety/reports/map/page.js new file mode 100644 index 0000000..b272e5e --- /dev/null +++ b/src/app/(withAuth)/dashboard/road-safety/reports/map/page.js @@ -0,0 +1,9 @@ +import RoadSafetyReportMap from "@/components/dashboard/roadSafety/reports/map"; +export const metadata = { + title: "پراکندگی بر روی نقشه نگهداری حریم", +}; + +const Page = () => { + return ; +}; +export default Page; diff --git a/src/app/(withAuth)/dashboard/road-safety/report/page.js b/src/app/(withAuth)/dashboard/road-safety/reports/page.js similarity index 97% rename from src/app/(withAuth)/dashboard/road-safety/report/page.js rename to src/app/(withAuth)/dashboard/road-safety/reports/page.js index 26681da..54df8d0 100644 --- a/src/app/(withAuth)/dashboard/road-safety/report/page.js +++ b/src/app/(withAuth)/dashboard/road-safety/reports/page.js @@ -1,4 +1,4 @@ -import ReportPage from "@/components/dashboard/roadSafety/report"; +import ReportPage from "@/components/dashboard/roadSafety/reports/reportPage"; import WithPermission from "@/core/middlewares/withPermission"; export const metadata = { title: "کارتابل گزارش نگهداری حریم", diff --git a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx index 4ca8402..ac16216 100644 --- a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx +++ b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx @@ -237,10 +237,7 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { size: 100, }, { - accessorFn: (row) => - moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A") - .locale("fa") - .format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => moment(row.StartTime_DateTime).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", id: "StartTime_DateTime", enableColumnFilter: false, diff --git a/src/components/dashboard/fastReact/operator/OperatorList.jsx b/src/components/dashboard/fastReact/operator/OperatorList.jsx index 925d89e..9de64f1 100644 --- a/src/components/dashboard/fastReact/operator/OperatorList.jsx +++ b/src/components/dashboard/fastReact/operator/OperatorList.jsx @@ -149,10 +149,7 @@ const OperatorList = () => { size: 100, }, { - accessorFn: (row) => - moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A") - .locale("fa") - .format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => moment(row.StartTime_DateTime).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", id: "StartTime_DateTime", enableColumnFilter: false, diff --git a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx index b10eade..d543a18 100644 --- a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx @@ -250,10 +250,7 @@ const SupervisorList = () => { size: 100, }, { - accessorFn: (row) => - moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A") - .locale("fa") - .format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => moment(row.StartTime_DateTime).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", id: "StartTime_DateTime", enableColumnFilter: false, diff --git a/src/components/dashboard/roadSafety/operator/OperatorList.jsx b/src/components/dashboard/roadSafety/operator/OperatorList.jsx index c59e81b..facec54 100644 --- a/src/components/dashboard/roadSafety/operator/OperatorList.jsx +++ b/src/components/dashboard/roadSafety/operator/OperatorList.jsx @@ -396,15 +396,13 @@ const OperatorList = () => { }, { accessorFn: (row) => - row.action_picture_document_upload_date ? ( - moment(row.action_picture_document_upload_date) - .locale("fa") - .format("HH:mm | yyyy/MM/DD") + row.action_date ? ( + moment(row.action_date).locale("fa").format("HH:mm | yyyy/MM/DD") ) : ( <>- ), - header: "تاریخ ثبت اقدام", - id: "action_picture_document_upload_date", + header: "تاریخ اقدام", + id: "action_date", enableColumnFilter: true, datatype: "date", filterMode: "between", diff --git a/src/components/dashboard/roadSafety/reports/map/ClusterSwitch/index.jsx b/src/components/dashboard/roadSafety/reports/map/ClusterSwitch/index.jsx new file mode 100644 index 0000000..e8fac87 --- /dev/null +++ b/src/components/dashboard/roadSafety/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/roadSafety/reports/map/Filter/Drawer/ActionDate.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActionDate.jsx new file mode 100644 index 0000000..431c7ac --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActionDate.jsx @@ -0,0 +1,32 @@ +import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker"; +import { Box } from "@mui/material"; + +const ActionDate = ({ 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 ActionDate; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActivityDateTimeFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActivityDateTimeFilter.jsx new file mode 100644 index 0000000..143c0cd --- /dev/null +++ b/src/components/dashboard/roadSafety/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/roadSafety/reports/map/Filter/Drawer/AxisTypeFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/AxisTypeFilter.jsx new file mode 100644 index 0000000..037a6a2 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/AxisTypeFilter.jsx @@ -0,0 +1,37 @@ +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; + +const AxisTypeFilter = ({ value, onChange }) => { + const axisOptions = [ + { value: "", label: "همه محور ها" }, + { value: 1, label: "آزادراه" }, + { value: 2, label: "بزرگراه" }, + { value: 3, label: "اصلی" }, + { value: 4, label: "فرعی" }, + { value: 5, label: "روستایی" }, + ]; + + return ( + + + نوع محور + + + + ); +}; +export default AxisTypeFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/CreatedAtFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/CreatedAtFilter.jsx new file mode 100644 index 0000000..1bcffba --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/CreatedAtFilter.jsx @@ -0,0 +1,32 @@ +import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker"; +import { Box } from "@mui/material"; + +const CreatedAtFilter = ({ 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 CreatedAtFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/EdarehShahriFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/EdarehShahriFilter.jsx new file mode 100644 index 0000000..eb08e27 --- /dev/null +++ b/src/components/dashboard/roadSafety/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/roadSafety/reports/map/Filter/Drawer/InfoFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/InfoFilter.jsx new file mode 100644 index 0000000..605c0af --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/InfoFilter.jsx @@ -0,0 +1,47 @@ +import useSafetyAndPrivacyGetItems from "@/lib/hooks/useSafetyAndPrivacyGetItems"; +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; +import { useMemo } from "react"; + +const InfoFilter = ({ value, onChange }) => { + const { itemsList, loadingItemsList, errorItemsList } = useSafetyAndPrivacyGetItems(); + 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 InfoFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/JudiciaryDocumentUploadDateFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/JudiciaryDocumentUploadDateFilter.jsx new file mode 100644 index 0000000..24a6b2f --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/JudiciaryDocumentUploadDateFilter.jsx @@ -0,0 +1,32 @@ +import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker"; +import { Box } from "@mui/material"; + +const JudiciaryDocumentUploadDateFilter = ({ 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 JudiciaryDocumentUploadDateFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ProvinceFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ProvinceFilter.jsx new file mode 100644 index 0000000..4de3f9e --- /dev/null +++ b/src/components/dashboard/roadSafety/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/roadSafety/reports/map/Filter/Drawer/StepFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/StepFilter.jsx new file mode 100644 index 0000000..b6eca29 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/StepFilter.jsx @@ -0,0 +1,35 @@ +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; + +const StepFilter = ({ value, onChange }) => { + const statusOptions = [ + { value: "", label: "همه وضعیت ها" }, + { value: 1, label: "گام اول (شناسایی)" }, + { value: 2, label: "گام دوم (مستندات قضایی)" }, + { value: 3, label: "گام سوم (برخورد)" }, + ]; + + return ( + + + وضعیت + + + + ); +}; +export default StepFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/index.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/index.jsx new file mode 100644 index 0000000..1a39633 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/index.jsx @@ -0,0 +1,166 @@ +import ScrollBox from "@/core/components/ScrollBox"; +import { yupResolver } from "@hookform/resolvers/yup"; +import CancelIcon from "@mui/icons-material/Cancel"; +import FilterAltIcon from "@mui/icons-material/FilterAlt"; +import { Box, Button, IconButton, Typography } from "@mui/material"; +import { Controller, useForm } from "react-hook-form"; +import * as Yup from "yup"; +import ActivityDateTimeFilter from "./ActivityDateTimeFilter"; +import AxisTypeFilter from "./AxisTypeFilter"; +import EdarehShahriFilter from "./EdarehShahriFilter"; +import InfoFilter from "./InfoFilter"; +import ProvinceFilter from "./ProvinceFilter"; +import StepFilter from "./StepFilter"; +import CreatedAtFilter from "./CreatedAtFilter"; +import JudiciaryDocumentUploadDateFilter from "./JudiciaryDocumentUploadDateFilter"; +import ActionPictureDocumentUploadDateFilter from "./ActionDate"; +import ActionDate from "./ActionDate"; + +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/roadSafety/reports/map/Filter/index.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx new file mode 100644 index 0000000..c12a511 --- /dev/null +++ b/src/components/dashboard/roadSafety/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/roadSafety/reports/map/Legend/index.jsx b/src/components/dashboard/roadSafety/reports/map/Legend/index.jsx new file mode 100644 index 0000000..91bc710 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Legend/index.jsx @@ -0,0 +1,48 @@ +import { Box, Stack, Typography } from "@mui/material"; + +const Legend = () => { + return ( + + + theme.palette.warning.main, + background: (theme) => theme.palette.warning.light, + }} + /> + گام اول (شناسایی) + + + theme.palette.error.main, + background: (theme) => theme.palette.error.light, + }} + /> + گام دوم (مستندات قضایی) + + + theme.palette.success.main, + background: (theme) => theme.palette.success.light, + }} + /> + گام سوم (برخورد) + + + ); +}; +export default Legend; diff --git a/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx new file mode 100644 index 0000000..bcfc03d --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx @@ -0,0 +1,94 @@ +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.edare_shahri_name !== "" && data.edare_shahri_name} + + + + + + + {data.info_fa !== "" && data.info_fa} + + + + + + + {data.axis_type_name !== "" && data.axis_type_name} + + + + + + + {data.step_fa !== "" && data.step_fa} + + + + + + + {data.activity_date_time && + moment(data.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD")} + + + {data.step == 3 && ( + + + + + {data.action_date && moment(data.action_date).locale("fa").format("HH:mm | yyyy/MM/DD")} + + + )} + + + + + recognize_picture + + + + + + action_picture + + + + + ); +}; +export default ContentInfoItem; diff --git a/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/index.jsx b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/index.jsx new file mode 100644 index 0000000..3107c53 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/index.jsx @@ -0,0 +1,51 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_ROAD_SAFETY_DETAILS } 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_SAFETY_DETAILS}/${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/roadSafety/reports/map/PointsOnMap/index.jsx b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/index.jsx new file mode 100644 index 0000000..b965bda --- /dev/null +++ b/src/components/dashboard/roadSafety/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 === 1) return theme.palette.warning.main; + if (status === 2) return theme.palette.error.main; + return theme.palette.success.main; + }; + + const markers = useMemo(() => { + return data.map(({ id, lat, lon, step }) => ( + { + openDialog(); + setSelectedId(id); + }, + }} + key={id} + center={L.latLng(lat, lon)} + radius={8} + color={getMarkerColor(step)} + /> + )); + }, [data, theme]); + + useEffect(() => { + if (data.length === 0) return; + const bounds = L.latLngBounds(data.map(({ lat, lon }) => L.latLng(lat, lon))); + 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/roadSafety/reports/map/index.jsx b/src/components/dashboard/roadSafety/reports/map/index.jsx new file mode 100644 index 0000000..907f90b --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/index.jsx @@ -0,0 +1,160 @@ +"use client"; +import LoadingHardPage from "@/core/components/LoadingHardPage"; +import MapLoading from "@/core/components/MapLayer/Loading"; +import { GET_ROAD_SAFETY_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 ClusterSwitch from "./ClusterSwitch"; +import Filter from "./Filter"; +import Legend from "./Legend"; +import PointsOnMap from "./PointsOnMap"; +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const defaultValues = { + province_id: "", + edarat_id: "", + info_id: "", + axis_type_id: "", + step: "", + created_at: ["", ""], + activity_date_time: ["", ""], + judiciary_document_upload_date: ["", ""], + action_date: ["", ""], +}; + +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 RoadSafetyReportMap = () => { + 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, + info_id, + axis_type_id, + step, + created_at, + activity_date_time, + judiciary_document_upload_date, + action_date, + } = filterData; + + const filterArray = []; + + if (province_id) filterArray.push({ id: "province_id", fn: "equals", datatype: "numeric", value: province_id }); + if (edarat_id) filterArray.push({ id: "edarat_id", fn: "equals", datatype: "numeric", value: edarat_id }); + if (info_id) filterArray.push({ id: "info_id", fn: "equals", datatype: "numeric", value: info_id }); + if (axis_type_id) + filterArray.push({ id: "axis_type_id", fn: "equals", datatype: "numeric", value: axis_type_id }); + if (step) filterArray.push({ id: "step", fn: "equals", datatype: "numeric", value: step }); + if (created_at[0]) filterArray.push({ id: "created_at", fn: "between", datatype: "date", value: created_at }); + if (activity_date_time[0]) + filterArray.push({ id: "activity_date_time", fn: "between", datatype: "date", value: activity_date_time }); + if (judiciary_document_upload_date[0]) + filterArray.push({ + id: "judiciary_document_upload_date", + fn: "between", + datatype: "date", + value: judiciary_document_upload_date, + }); + if (action_date[0]) + filterArray.push({ id: "action_date", fn: "between", datatype: "date", value: action_date }); + + console.log(filterArray); + + params.set("filters", JSON.stringify(filterArray || [])); + return params.toString(); + }, [filterData]); + + useEffect(() => { + console.log("gasgsa"); + + const fetchData = async () => { + try { + setLoading(true); + const query = buildQueryParams(); + console.log("amir"); + + const response = await requestServer(`${GET_ROAD_SAFETY_REPORT_MAP}?${query}`); + const fetchedData = response?.data?.data || []; + + if (fetchedData.length > MAX_POINTS) { + setCluster(true); + } + setData(fetchedData); + } catch (error) { + console.log(error); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [filterData, buildQueryParams]); + + console.log("sfa"); + + return ( + + + + + MAX_POINTS} + max={MAX_POINTS} + /> + + + + + + + + + + + {isLoading && ( + + + + )} + + + + + ); +}; +export default RoadSafetyReportMap; diff --git a/src/components/dashboard/roadSafety/report/ExcelPrint/index.jsx b/src/components/dashboard/roadSafety/reports/reportPage/ExcelPrint/index.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/ExcelPrint/index.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/ExcelPrint/index.jsx diff --git a/src/components/dashboard/roadSafety/report/ReportLists.jsx b/src/components/dashboard/roadSafety/reports/reportPage/ReportLists.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/ReportLists.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/ReportLists.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/FromDateController.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/FromDateController.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/FromDateController.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/FromDateController.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/SearchReportField.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/SearchReportField.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/SearchReportField.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/SearchReportField.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/SelectAxisType.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/SelectAxisType.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/SelectAxisType.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/SelectAxisType.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/SelectProvince.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/SelectProvince.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/SelectProvince.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/SelectProvince.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/ToDateController.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/ToDateController.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/ToDateController.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/ToDateController.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/index.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/index.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/index.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/index.jsx diff --git a/src/components/dashboard/roadSafety/report/Toolbar.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Toolbar.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Toolbar.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Toolbar.jsx diff --git a/src/components/dashboard/roadSafety/report/index.jsx b/src/components/dashboard/roadSafety/reports/reportPage/index.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/index.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/index.jsx diff --git a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx index 755b34c..ba0cd5f 100644 --- a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx +++ b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx @@ -34,7 +34,10 @@ const SidebarSubitems = ({ subitem, dispatch }) => { {subitem.badges.map((badge, i) => ( diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index 2ce4083..a4e1d31 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -301,6 +301,10 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, + badges: [ + { key: "safetyAndPrivacy.step_one", color: "warning" }, + { key: "safetyAndPrivacy.step_two", color: "error" }, + ], Subitems: [ { id: "safetyAndPrivacyManagmentOparationCartable", @@ -319,7 +323,7 @@ export const pageMenu = [ id: "safetyAndPrivacyManagmentMap", label: "پراکندگی بر روی نقشه", type: "page", - route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=safety_and_privacy", + route: "/dashboard/road-safety/reports/map", icon: , permissions: [ "show-safety-and-privacy-operator-cartable", @@ -331,7 +335,7 @@ export const pageMenu = [ id: "safetyAndPrivacyManagmentReport", label: "گزارش ها", type: "page", - route: "/dashboard/road-safety/report", + route: "/dashboard/road-safety/reports", icon: , permissions: [ "show-safety-and-privacy-operator-cartable", @@ -355,7 +359,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: [{ key: "roadObserved.supervise_cnt" }, { key: "roadObserved.complaint_cnt" }], + badges: [{ key: "roadObserved.supervise_cnt" }, { key: "roadObserved.complaint_cnt", color: "info" }], Subitems: [ { id: "roadObservationsManagmentSupervisorCartable", @@ -372,7 +376,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: [{ key: "roadObserved.operation_cnt" }, { key: "roadObserved.complaint_cnt" }], + badges: [{ key: "roadObserved.operation_cnt" }, { key: "roadObserved.complaint_cnt", color: "info" }], Subitems: [ { id: "roadObservationsManagmentOparationCartable", diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 4953100..b91186b 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -140,6 +140,8 @@ export const UPDATE_SAFETY_AND_PRIVACY = api + "/api/v3/safety_and_privacy"; export const EXPORT_OPERATOR_ROAD_SAFETY = api + "/api/v3/safety_and_privacy/excel_report"; export const GET_ROAD_SAFETY_PROVINCE_ACTIVITY_REPORT = api + "/api/v3/safety_and_privacy_report/country_activity"; export const GET_ROAD_SAFETY_CITY_ACTIVITY_REPORT = api + "/api/v3/safety_and_privacy_report/province_activity"; +export const GET_ROAD_SAFETY_REPORT_MAP = api + "/api/v3/safety_and_privacy/map"; +export const GET_ROAD_SAFETY_DETAILS = api + "/api/v3/safety_and_privacy"; // permission table export const GET_PERMISSION_TABLE_LIST = api + "/api/v3/permissions";