Feature/road safety map
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import RoadSafetyReportMap from "@/components/dashboard/roadSafety/reports/map";
|
||||
export const metadata = {
|
||||
title: "پراکندگی بر روی نقشه نگهداری حریم",
|
||||
};
|
||||
|
||||
const Page = () => {
|
||||
return <RoadSafetyReportMap />;
|
||||
};
|
||||
export default Page;
|
||||
@@ -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: "کارتابل گزارش نگهداری حریم",
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 (
|
||||
<Tooltip title={disabled ? `بیشتر از ${max} تا فعالیت باید حالت کلاستر باشد` : ""}>
|
||||
<span>
|
||||
<Chip
|
||||
label={"حالت کلاستر"}
|
||||
variant="outlined"
|
||||
disabled={disabled}
|
||||
color={isCluster ? "success" : "default"}
|
||||
onClick={() => setCluster((c) => !c)}
|
||||
sx={{ borderRadius: 1 }}
|
||||
icon={
|
||||
<Zoom in={isCluster}>
|
||||
<DoneIcon />
|
||||
</Zoom>
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
export default ClusterSwitch;
|
||||
@@ -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 (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ اقدام"}
|
||||
name={`action_date[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ اقدام"}
|
||||
name={`action_date[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default ActionDate;
|
||||
@@ -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 (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بازدید"}
|
||||
name={`activity_date_time[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بازدید"}
|
||||
name={`activity_date_time[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default ActivityDateTimeFilter;
|
||||
@@ -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 (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelaxis`} shrink>
|
||||
نوع محور
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelaxis`}
|
||||
id={"axis"}
|
||||
name={`axis`}
|
||||
value={value}
|
||||
input={<OutlinedInput label="نوع محور" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{axisOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default AxisTypeFilter;
|
||||
@@ -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 (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ ثبت"}
|
||||
name={`activity_date_time[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ ثبت"}
|
||||
name={`activity_date_time[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default CreatedAtFilter;
|
||||
@@ -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 <EdarehShahriController value={value} onChange={onChange} dependencyField={dependencyField} />;
|
||||
};
|
||||
|
||||
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 (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labeledarat_id`} shrink>
|
||||
اداره
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labeledarat_id`}
|
||||
id={"edarat_id"}
|
||||
name={`edarat_id`}
|
||||
value={dependencyField === "" ? "empty" : loadingEdaratList ? "loading" : value}
|
||||
input={<OutlinedInput label="اداره" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default EdarehShahriFilter;
|
||||
@@ -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 (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelinfo_id`} shrink>
|
||||
مورد مشاهده شده
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelinfo_id`}
|
||||
id={"info_id"}
|
||||
name={`info_id`}
|
||||
value={loadingItemsList ? "loading" : value}
|
||||
input={<OutlinedInput label="مورد مشاهده شده" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default InfoFilter;
|
||||
@@ -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 (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بارگذاری مستندات"}
|
||||
name={`judiciary_document_upload_date[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بارگذاری مستندات"}
|
||||
name={`judiciary_document_upload_date[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default JudiciaryDocumentUploadDateFilter;
|
||||
@@ -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 (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelprovince_id`} shrink>
|
||||
استان
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelprovince_id`}
|
||||
id={"province_id"}
|
||||
name={`province_id`}
|
||||
value={loadingProvinces ? "loading" : value}
|
||||
input={<OutlinedInput label="استان" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default ProvinceFilter;
|
||||
@@ -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 (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelstep`} shrink>
|
||||
وضعیت
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelstep`}
|
||||
id={"step"}
|
||||
name={`step`}
|
||||
value={value}
|
||||
input={<OutlinedInput label="وضعیت" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{statusOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default StepFilter;
|
||||
@@ -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 (
|
||||
<>
|
||||
<Box sx={headerSx}>
|
||||
<Box sx={headerTitleSx}>
|
||||
<FilterAltIcon sx={headerIconSx} />
|
||||
<Typography variant="h6" sx={{ color: "#fff" }}>
|
||||
فیلتر
|
||||
</Typography>
|
||||
</Box>
|
||||
<IconButton sx={iconButtonSx} onClick={closeDrawer}>
|
||||
<CancelIcon sx={{ fontSize: "1em" }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<ScrollBox>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Box sx={formContainerSx}>
|
||||
<Controller
|
||||
name="province_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ProvinceFilter value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="edarat_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<EdarehShahriFilter value={value} onChange={onChange} control={control} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="info_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<InfoFilter value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="axis_type_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<AxisTypeFilter value={value} onChange={onChange} control={control} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="step"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<StepFilter value={value} onChange={onChange} control={control} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="created_at"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<CreatedAtFilter value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="activity_date_time"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<ActivityDateTimeFilter value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="judiciary_document_upload_date"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<JudiciaryDocumentUploadDateFilter value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="action_date"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<ActionDate value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={footerSx}>
|
||||
<Button type="submit" variant="contained" size="large" disabled={!isDirty} sx={submitButtonSx}>
|
||||
اعمال فیلتر
|
||||
</Button>
|
||||
</Box>
|
||||
</form>
|
||||
</ScrollBox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default FilterDrawer;
|
||||
@@ -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 (
|
||||
<>
|
||||
<Button onClick={openDrawer} variant="outlined" size="small" endIcon={<FilterListIcon />}>
|
||||
فیلتر
|
||||
</Button>
|
||||
<Drawer open={open} onClose={closeDrawer} sx={drawerSx}>
|
||||
<Box sx={boxSx}>
|
||||
{open && (
|
||||
<FilterDrawer
|
||||
defaultValues={filterData}
|
||||
setFilterData={setFilterData}
|
||||
closeDrawer={closeDrawer}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default Filter;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
|
||||
const Legend = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<Stack direction={"row"} spacing={1} alignItems={"center"}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
border: 1,
|
||||
borderRadius: "50%",
|
||||
borderColor: (theme) => theme.palette.warning.main,
|
||||
background: (theme) => theme.palette.warning.light,
|
||||
}}
|
||||
/>
|
||||
<Typography variant="caption">گام اول (شناسایی)</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} spacing={1} alignItems={"center"}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
border: 1,
|
||||
borderRadius: "50%",
|
||||
borderColor: (theme) => theme.palette.error.main,
|
||||
background: (theme) => theme.palette.error.light,
|
||||
}}
|
||||
/>
|
||||
<Typography variant="caption">گام دوم (مستندات قضایی)</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} spacing={1} alignItems={"center"}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
border: 1,
|
||||
borderRadius: "50%",
|
||||
borderColor: (theme) => theme.palette.success.main,
|
||||
background: (theme) => theme.palette.success.light,
|
||||
}}
|
||||
/>
|
||||
<Typography variant="caption">گام سوم (برخورد)</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default Legend;
|
||||
@@ -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 (
|
||||
<Stack spacing={2}>
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="کد یکتا" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>{data.id !== "" && data.id}</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="استان" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
|
||||
{data.province_fa !== "" && data.province_fa}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="اداره" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
|
||||
{data.edare_shahri_name !== "" && data.edare_shahri_name}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="مورد مشاهده شده" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
|
||||
{data.info_fa !== "" && data.info_fa}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="نوع محور" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
|
||||
{data.axis_type_name !== "" && data.axis_type_name}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="وضعیت" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
|
||||
{data.step_fa !== "" && data.step_fa}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="تاریخ بازدید" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
|
||||
{data.activity_date_time &&
|
||||
moment(data.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
{data.step == 3 && (
|
||||
<Stack direction={"row"} alignItems={"center"}>
|
||||
<Chip size="small" label="تاریخ اقدام" />
|
||||
<Divider sx={{ flex: 1, mx: 2 }} />
|
||||
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
|
||||
{data.action_date && moment(data.action_date).locale("fa").format("HH:mm | yyyy/MM/DD")}
|
||||
</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
<Stack spacing={2}>
|
||||
<Stack sx={{ flex: 1, border: 1, borderColor: "divider", borderRadius: 2, p: 1 }} alignItems={"center"}>
|
||||
<Chip size="small" label="تصویر بازدید" />
|
||||
<Box sx={{ position: "relative", height: 300, width: "100%" }}>
|
||||
<Image
|
||||
src={data.recognize_picture}
|
||||
style={{ objectFit: "contain" }}
|
||||
alt="recognize_picture"
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack sx={{ flex: 1, border: 1, borderColor: "divider", borderRadius: 2, p: 1 }} alignItems={"center"}>
|
||||
<Chip size="small" label="تصویر اقدام" />
|
||||
<Box sx={{ position: "relative", height: 300, width: "100%" }}>
|
||||
<Image
|
||||
src={data.action_picture}
|
||||
style={{ objectFit: "contain" }}
|
||||
alt="action_picture"
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default ContentInfoItem;
|
||||
@@ -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 (
|
||||
<>
|
||||
<DialogTitle>جزئیات فعالیت</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
{loading ? <DialogLoading /> : data && <ContentInfoItem data={data} />}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={closeDialog} variant="outlined" color="secondary" autoFocus>
|
||||
بستن
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default DialogInfoItem;
|
||||
@@ -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 }) => (
|
||||
<CircleMarker
|
||||
eventHandlers={{
|
||||
click: () => {
|
||||
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: `<div class="custom-marker-cluster">${count}</div>`,
|
||||
className: "custom-marker-cluster",
|
||||
iconSize: L.point(40, 40, true),
|
||||
});
|
||||
};
|
||||
|
||||
if (data.length === 0) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCluster ? (
|
||||
<MarkerClusterGroup iconCreateFunction={createClusterCustomIcon} showCoverageOnHover={false}>
|
||||
{markers}
|
||||
</MarkerClusterGroup>
|
||||
) : (
|
||||
markers
|
||||
)}
|
||||
<Dialog open={open} onClose={closeDialog} fullWidth maxWidth="sm">
|
||||
{open && <DialogInfoItem selectedId={selectedId} closeDialog={closeDialog} />}
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PointsOnMap;
|
||||
160
src/components/dashboard/roadSafety/reports/map/index.jsx
Normal file
160
src/components/dashboard/roadSafety/reports/map/index.jsx
Normal file
@@ -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: () => <MapLoading />,
|
||||
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 (
|
||||
<Box sx={containerStyles}>
|
||||
<MapLayer style={{ borderRadius: "4px" }}>
|
||||
<Stack
|
||||
direction="row-reverse"
|
||||
sx={{ position: "absolute", top: 0, right: 0, width: "100%", zIndex: 1000 }}
|
||||
>
|
||||
<Stack direction="row" spacing={1} sx={{ ...controlBarStyles, borderBottomLeftRadius: "4px" }}>
|
||||
<ClusterSwitch
|
||||
isCluster={isCluster}
|
||||
setCluster={setCluster}
|
||||
disabled={data.length > MAX_POINTS}
|
||||
max={MAX_POINTS}
|
||||
/>
|
||||
<Filter filterData={filterData} setFilterData={setFilterData} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack
|
||||
direction="row-reverse"
|
||||
sx={{ position: "absolute", bottom: 0, right: 0, width: "100%", zIndex: 1000 }}
|
||||
>
|
||||
<Stack direction="row" spacing={1} sx={{ ...controlBarStyles, pb: 0, borderTopLeftRadius: "4px" }}>
|
||||
<Legend />
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{isLoading && (
|
||||
<Box sx={{ position: "relative", width: "100%", height: "100%", opacity: 0.7, zIndex: 999 }}>
|
||||
<LoadingHardPage width={80} height={80} loading={isLoading} sx={{ position: "absolute" }} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<PointsOnMap data={data} isCluster={isCluster} />
|
||||
</MapLayer>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default RoadSafetyReportMap;
|
||||
@@ -34,7 +34,10 @@ const SidebarSubitems = ({ subitem, dispatch }) => {
|
||||
<Stack direction={"row"} sx={{ px: 0.5 }} spacing={0.5}>
|
||||
{subitem.badges.map((badge, i) => (
|
||||
<SidebarBadge
|
||||
chipProps={{ color: badge.color || "default" }}
|
||||
chipProps={{
|
||||
color: badge.color || "default",
|
||||
variant: "outlined",
|
||||
}}
|
||||
key={badge.key}
|
||||
badge={badge.key}
|
||||
/>
|
||||
|
||||
@@ -301,6 +301,10 @@ export const pageMenu = [
|
||||
type: "menu",
|
||||
icon: <EngineeringIcon sx={{ width: "inherit", height: "inherit" }} />,
|
||||
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: <MapIcon sx={{ width: "inherit", height: "inherit" }} />,
|
||||
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: <AssessmentIcon sx={{ width: "inherit", height: "inherit" }} />,
|
||||
permissions: [
|
||||
"show-safety-and-privacy-operator-cartable",
|
||||
@@ -355,7 +359,7 @@ export const pageMenu = [
|
||||
type: "menu",
|
||||
icon: <SpeedIcon sx={{ width: "inherit", height: "inherit" }} />,
|
||||
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: <EngineeringIcon sx={{ width: "inherit", height: "inherit" }} />,
|
||||
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",
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user