Merge branch 'release/v0.15.0'

This commit is contained in:
AmirHossein Mahmoodi
2025-01-26 17:34:04 +03:30
78 changed files with 1445 additions and 1497 deletions

View File

@@ -1,3 +1,4 @@
NEXT_PUBLIC_VERSION="0.14.0" HOST="rms.witel.ir"
NEXT_PUBLIC_VERSION="0.15.0"
NEXT_PUBLIC_API_URL="https://rms.witel.ir" NEXT_PUBLIC_API_URL="https://rms.witel.ir"
NEXT_PUBLIC_MAPTILE_ENDPOINT="https://rmsmap.rmto.ir/141map" NEXT_PUBLIC_MAPTILE_ENDPOINT="https://rmsmap.rmto.ir/141map"

View File

@@ -5,7 +5,7 @@ const nextConfig = {
remotePatterns: [ remotePatterns: [
{ {
protocol: 'https', protocol: 'https',
hostname: 'rms.witel.ir', hostname: process.env.HOST,
}, },
], ],
}, },

View File

@@ -1,12 +0,0 @@
import TestPage from "@/components/dashboard/carDetails";
import WithPermission from "@/core/middlewares/withPermission";
const Page = () => {
return (
<WithPermission permission_name={[""]}>
<TestPage />
</WithPermission>
);
};
export default Page;

View File

@@ -1,180 +0,0 @@
import { Button, Collapse, DialogActions, DialogContent, Grid, Stack, TextField } from "@mui/material";
import MuiDatePicker from "@/core/components/MuiDatePicker";
import { useFormik } from "formik";
import moment from "jalali-moment";
import * as Yup from "yup";
import { useState } from "react";
import ApplicantRequestDetail from "@/core/components/ApplicantRequestDetail";
import DetailMap from "@/core/components/ApplicantRequestDetail/DetailMap";
import FlyToPolyline from "@/core/components/ApplicantRequestDetail/FlyToPolyline";
import { MapContainer } from "react-leaflet";
import dynamic from "next/dynamic";
import MapLoading from "@/core/components/MapLayer/Loading";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => <MapLoading />,
ssr: false,
});
const fakeData = {
id: 1,
shomareh_darkhast: "2124",
ostan: "ostan",
shahr: "shahr",
shahrestan: "shahrestan",
bakhsh: "bakhsh",
roosta: "roosta",
tarikh_darkhast: "tarikh_darkhast",
sazman: "sazman",
masahat_zamin: "masahat_zamin",
masahat_tarh: "masahat_tarh",
gorooh_tarh: "gorooh_tarh",
onvane_tarh: "onvane_tarh",
address: "کرج - فلان جا - جنب پاساژ - نزدیک اونجا - اونور خیابون - زیر رو گذر - روی زیر گذر",
file_mosavab: "file",
polyline: [
[35.6892, 51.389],
[35.7074, 51.3929],
],
};
const CarDetailsContent = ({ setOpenCarDialog }) => {
const [openCarDetailContent, setOpenCarDetailContent] = useState(false);
const initialValues = {
start_date: "",
end_date: "",
vehicle_code: "",
};
const validationSchema = Yup.object().shape({
start_date: Yup.string().required("تاریخ شروع الزامیست!!!"),
end_date: Yup.string()
.test("is-greater", "تاریخ اتمام نمی‌تواند کمتر از تاریخ شروع باشد", function (value) {
const { start_date } = this.parent; // Access other field values
if (!value || !start_date) return true; // Skip validation if either field is empty
return moment(value, "YYYY/MM/DD").isSameOrAfter(moment(start_date, "YYYY/MM/DD"));
})
.required("تاریخ اتمام الزامیست!!!"),
vehicle_code: Yup.mixed()
.test("is-number", "کد خودرو باید عدد باشد!!!", (value) => !isNaN(value))
.test("positive", "کد خودرو باید مثبت باشد!!!", (value) => value >= 0)
.required("کد خودرو الزامیست!!!"),
});
const handleSubmit = async (values, props) => {
props.setSubmitting(true);
setOpenCarDetailContent(true);
};
const formik = useFormik({
initialValues,
validationSchema,
onSubmit: handleSubmit,
});
return (
<>
<DialogContent>
<Stack spacing={2} sx={{ my: 2 }}>
<Stack direction={"row"} spacing={2} sx={{ my: 2 }}>
<MuiDatePicker
formik={formik}
value={formik.values.start_date}
name={"start_date"}
error={formik.errors.start_date}
helperText={formik.touched.start_date && formik.errors.start_date}
touched={formik.touched.start_date}
onBlur={formik.handleBlur("start_date")}
/>
<MuiDatePicker
formik={formik}
name={"end_date"}
disabled={formik.values.start_date === ""}
value={formik.values.end_date}
error={formik.errors.end_date}
helperText={formik.touched.end_date && formik.errors.end_date}
touched={formik.touched.end_date}
onBlur={formik.handleBlur("end_date")}
/>
<TextField
name="vehicle_code"
label={"کد خودرو"}
type="text"
inputProps={{
inputMode: "number",
min: 0,
pattern: "[0-9]*",
}}
variant="outlined"
value={formik.values.vehicle_code}
onChange={(event) => {
if (!isNaN(event.target.value)) {
formik.setFieldValue("vehicle_code", event.target.value);
} else {
formik.setFieldValue("vehicle_code", formik.values.vehicle_code);
}
}}
size={"small"}
error={formik.touched.vehicle_code && Boolean(formik.errors.vehicle_code)}
helperText={formik.touched.vehicle_code && formik.errors.vehicle_code}
onBlur={formik.handleBlur("vehicle_code")}
fullWidth
/>
</Stack>
<Stack>
<Button variant={"contained"} color={"primary"} onClick={formik.handleSubmit}>
نمایش اطلاعات
</Button>
</Stack>
</Stack>
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<Stack
direction="column"
sx={{ height: "100%", justifyContent: "start", alignItems: "center" }}
spacing={5}
>
<TextField
name="vehicle_code"
label={"مصرف سوخت خودرو"}
disabled
variant="outlined"
value={"5 لیتر"}
size={"small"}
fullWidth
/>
<TextField
name="vehicle_code"
label={"مسافت پیموده شده"}
disabled
variant="outlined"
value={"54 کیلومتر"}
size={"small"}
fullWidth
/>
<TextField
name="vehicle_code"
label={"زمان روشن بودن خودرو"}
disabled
variant="outlined"
value={"3 ساعت"}
size={"small"}
fullWidth
/>
</Stack>
</Grid>
<Grid item xs={12} sm={8}>
<MapLayer style={{ height: "400px", width: "100%" }}>
{/*<FlyToPolyline polyline={fakeData.polyline} />*/}
</MapLayer>
</Grid>
</Grid>
</DialogContent>
<DialogActions>
<Button variant={"outlined"} color={"warning"} onClick={() => setOpenCarDialog(false)}>
بستن
</Button>
</DialogActions>
</>
);
};
export default CarDetailsContent;

View File

@@ -1,44 +0,0 @@
"use client";
import PageTitle from "@/core/components/PageTitle";
import { Dialog, DialogTitle, IconButton, Stack, Tooltip } from "@mui/material";
import { useState } from "react";
import DriveEtaIcon from "@mui/icons-material/DriveEta";
import CarDetailsContent from "@/components/dashboard/carDetails/CarDetailsContent";
const TestPage = () => {
const [openCarDialog, setOpenCarDialog] = useState(false);
return (
<Stack spacing={1}>
<PageTitle title={"نمایش اطلاعات خودرو"} />
<>
<Tooltip title="نمایش اطلاعات خودرو">
<IconButton
color="primary"
onClick={() => {
setOpenCarDialog(true);
}}
>
<DriveEtaIcon />
</IconButton>
</Tooltip>
<Dialog
onClose={() => setOpenCarDialog(false)}
fullWidth
open={openCarDialog}
PaperProps={{
sx: {
transition: "all .3s",
boxShadow:
"rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
},
}}
maxWidth={"lg"}
>
<DialogTitle>نمایش اطلاعات خودرو</DialogTitle>
<CarDetailsContent setOpenCarDialog={setOpenCarDialog} />
</Dialog>
</>
</Stack>
);
};
export default TestPage;

View File

@@ -27,10 +27,26 @@ function TabPanel(props) {
const validationSchema = object({ const validationSchema = object({
item_id: number().required("نوع آیتم را مشخص کنید!"), item_id: number().required("نوع آیتم را مشخص کنید!"),
sub_item_id: number().required("موضوع مشاهده شده را مشخص کنید!"), sub_item_id: number().required("اقدام انجام شده را مشخص کنید!"),
amount: string().required("وارد کردن مقدار الزامیست!"), amount: string().required("وارد کردن مقدار الزامیست!"),
cmms_machines: array().required("وارد کردن کد خودرو الزامیست!").min(1, "حداقل یک کد خودرو باید وارد شود!"), cmms_machines: array()
rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), // بررسی حداقل یک آیتم, .test("cmms-machines-conditional", "وارد کردن کد خودرو الزامیست!", function (value) {
const { is_gasht } = this.options.context;
if (is_gasht) {
return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد
}
return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد
})
.min(1, "حداقل یک کد خودرو باید وارد شود!"), // پیام خطا برای زمانی که شرط تست برقرار باشد
rahdaran_id: array()
.test("rahdaran-id-conditional", "وارد کردن کد راهداران الزامیست!", function (value) {
const { is_gasht } = this.options.context;
if (is_gasht) {
return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد
}
return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد
})
.min(1, "حداقل یک کد راهدار باید وارد شود!"), // پیام خطا برای زمانی که شرط تست برقرار باشد
activity_time: string().test("activity-time-conditional", "لطفا زمان فعالیت را انتخاب کنید!", function (value) { activity_time: string().test("activity-time-conditional", "لطفا زمان فعالیت را انتخاب کنید!", function (value) {
const { is_gasht } = this.options.context; // دسترسی به context const { is_gasht } = this.options.context; // دسترسی به context
if (is_gasht) { if (is_gasht) {
@@ -91,11 +107,14 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => {
amount: "", amount: "",
before_image: null, before_image: null,
after_image: null, after_image: null,
cmms_machines: null,
rahdaran_id: null,
start_point: "", start_point: "",
end_point: "", end_point: "",
...(!is_gasht && { activity_time: "", activity_date: "" }), ...(!is_gasht && {
activity_time: "",
activity_date: "",
cmms_machines: [],
rahdaran_id: [],
}),
}; };
const [tabState, setTabState] = useState(0); const [tabState, setTabState] = useState(0);
@@ -175,17 +194,24 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => {
result.start_point = `${result.start_point.lat},${result.start_point.lng}`; result.start_point = `${result.start_point.lat},${result.start_point.lng}`;
} }
result.rahdaran_id.forEach((rahdar, index) => { if (!is_gasht) {
result[`rahdaran_id[${index}]`] = rahdar.id; result.rahdaran_id.forEach((rahdar, index) => {
}); result[`rahdaran_id[${index}]`] = rahdar.id;
delete result.rahdaran_id; });
delete result.rahdaran_id;
result.cmms_machines.forEach((cmms_machine, index) => { result.cmms_machines.forEach((cmms_machine, index) => {
result[`machines_id[${index}]`] = cmms_machine.id; result[`machines_id[${index}]`] = cmms_machine.id;
}); });
delete result.cmms_machines; delete result.cmms_machines;
result[`activity_time`] = format(new Date(result.activity_time), "HH:mm"); result[`activity_time`] = format(new Date(result.activity_time), "HH:mm");
} else {
delete result.rahdaran_id;
delete result.cmms_machines;
delete result.activity_date;
delete result.activity_time;
}
await onSubmit({ await onSubmit({
result, result,
@@ -214,7 +240,7 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => {
}} }}
> >
<Tab icon={<InsertDriveFileIcon />} label="انتخاب آیتم" /> <Tab icon={<InsertDriveFileIcon />} label="انتخاب آیتم" />
<Tab disabled={tabState === 0} icon={<FileCopyIcon />} label="انتخاب موضوع مشاهده شده" /> <Tab disabled={tabState === 0} icon={<FileCopyIcon />} label="انتخاب اقدام انجام شده" />
<Tab disabled={tabState === 0 || tabState === 1} icon={<InfoIcon />} label="اطلاعات فعالیت" /> <Tab disabled={tabState === 0 || tabState === 1} icon={<InfoIcon />} label="اطلاعات فعالیت" />
</Tabs> </Tabs>
<DialogContent dividers sx={{ overflowY: "auto", maxHeight: "70vh" }}> <DialogContent dividers sx={{ overflowY: "auto", maxHeight: "70vh" }}>

View File

@@ -43,37 +43,41 @@ const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, err
fullWidth fullWidth
/> />
</Stack> </Stack>
<Stack> {!is_gasht && (
<Controller <Stack>
control={control} <Controller
render={({ field, fieldState: { error } }) => { control={control}
return ( render={({ field, fieldState: { error } }) => {
<CarCode return (
carCode={field.value || []} <CarCode
setCarCode={(value) => field.onChange(value || [])} carCode={field.value || []}
error={error} setCarCode={(value) => field.onChange(value || [])}
multiple={true} error={error}
/> multiple={true}
); />
}} );
name={"cmms_machines"} }}
/> name={"cmms_machines"}
</Stack> />
<Stack> </Stack>
<Controller )}
control={control} {!is_gasht && (
render={({ field, fieldState: { error } }) => { <Stack>
return ( <Controller
<RahdarCode control={control}
rahdarsCode={field.value || []} render={({ field, fieldState: { error } }) => {
setRahdarsCode={(value) => field.onChange(value || [])} return (
error={error} <RahdarCode
/> rahdarsCode={field.value || []}
); setRahdarsCode={(value) => field.onChange(value || [])}
}} error={error}
name={"rahdaran_id"} />
/> );
</Stack> }}
name={"rahdaran_id"}
/>
</Stack>
)}
<Stack> <Stack>
{subItemsList.needs_image ? ( {subItemsList.needs_image ? (
<ImageUpload setValue={setValue} control={control} errors={errors} getValues={getValues} /> <ImageUpload setValue={setValue} control={control} errors={errors} getValues={getValues} />

View File

@@ -9,7 +9,7 @@ const GetSubItemsForm = ({ setValue, watch, setSubItemsList, tabState, setTabSta
<> <>
{errorSubItemsList ? ( {errorSubItemsList ? (
<Typography textAlign={"center"} color={"error"}> <Typography textAlign={"center"} color={"error"}>
خطا در دریافت موضوع مشاهده شده خطا در دریافت اقدام انجام شده
</Typography> </Typography>
) : loadingSubItemsList ? ( ) : loadingSubItemsList ? (
<LinearProgress /> <LinearProgress />

View File

@@ -27,14 +27,14 @@ const PreviousStatesInfo = ({ itemsList, subItemsList }) => {
icon={<FileCopyIcon />} icon={<FileCopyIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
موضوع مشاهدهشده اقدام انجام شده
</Typography> </Typography>
} }
/> />
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{subItemsList?.name || "هیچ موضوعی انتخاب نشده است"} {subItemsList?.name || "هیچ اقدامی انتخاب نشده است"}
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -22,7 +22,7 @@ const OperatorCreateForm = ({ open, setOpen, mutate, rowId }) => {
}; };
return ( return (
<Dialog open={open} fullWidth maxWidth="sm"> <Dialog open={open} fullWidth maxWidth="sm">
<CreateFormContent setOpen={setOpen} mutate={mutate} onSubmit={HandleSubmit} /> {open && <CreateFormContent setOpen={setOpen} mutate={mutate} onSubmit={HandleSubmit} />}
</Dialog> </Dialog>
); );
}; };

View File

@@ -31,7 +31,7 @@ const OperatorCreate = ({ mutate }) => {
ثبت فعالیت ثبت فعالیت
</Button> </Button>
)} )}
{open && <OperatorCreateForm open={open} setOpen={setOpen} mutate={mutate} />} <OperatorCreateForm open={open} setOpen={setOpen} mutate={mutate} />
</> </>
); );
}; };

View File

@@ -6,7 +6,9 @@ const GashtCreate = ({ open, setOpen, mutate, rowId }) => {
const [tabState, setTabState] = useState(0); const [tabState, setTabState] = useState(0);
return ( return (
<Dialog open={open} fullWidth maxWidth={tabState === 0 ? "md" : "sm"}> <Dialog open={open} fullWidth maxWidth={tabState === 0 ? "md" : "sm"}>
<GashtCreateContent mutate={mutate} setOpen={setOpen} setTabState={setTabState} tabState={tabState} /> {open && (
<GashtCreateContent mutate={mutate} setOpen={setOpen} setTabState={setTabState} tabState={tabState} />
)}
</Dialog> </Dialog>
); );
}; };

View File

@@ -104,7 +104,7 @@ const GashtCreateContent = ({ mutate, setOpen, tabState, setTabState }) => {
}} }}
> >
<Tab icon={<InsertDriveFileIcon />} label="انتخاب مورد مشاهده شده" /> <Tab icon={<InsertDriveFileIcon />} label="انتخاب مورد مشاهده شده" />
<Tab disabled={tabState === 0} icon={<FileCopyIcon />} label="اطلاعات مورد مشاهده شده" /> <Tab disabled={tabState === 0} icon={<FileCopyIcon />} label="اطلاعات مورد اقدام شده" />
</Tabs> </Tabs>
<DialogContent dividers sx={{ overflowY: "auto", maxHeight: "70vh" }}> <DialogContent dividers sx={{ overflowY: "auto", maxHeight: "70vh" }}>
<Box sx={{ flex: 1 }}> <Box sx={{ flex: 1 }}>

View File

@@ -36,7 +36,7 @@ const CompleteItem = ({ subItem, itemInfo, setSubmitCompleteForm, mutate, setOpe
const validationSchema = object({ const validationSchema = object({
item_id: number().required("نوع آیتم را مشخص کنید!"), item_id: number().required("نوع آیتم را مشخص کنید!"),
sub_item_id: number().required("موضوع مشاهده شده را مشخص کنید!"), sub_item_id: number().required("اقدام انجام شده را مشخص کنید!"),
amount: string().required("وارد کردن مقدار الزامیست!"), amount: string().required("وارد کردن مقدار الزامیست!"),
cmms_machines: array().required("وارد کردن کد خودرو الزامیست!").min(1, "حداقل یک کد خودرو باید وارد شود!"), cmms_machines: array().required("وارد کردن کد خودرو الزامیست!").min(1, "حداقل یک کد خودرو باید وارد شود!"),
rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), // بررسی حداقل یک آیتم, rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), // بررسی حداقل یک آیتم,

View File

@@ -27,14 +27,14 @@ const PreviousStatesInfo = ({ itemInfo }) => {
icon={<FileCopyIcon />} icon={<FileCopyIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
موضوع مشاهدهشده اقدام انجام شده
</Typography> </Typography>
} }
/> />
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{itemInfo?.sub_item_name || "هیچ موضوعی انتخاب نشده است"} {itemInfo?.sub_item_name || "هیچ اقدامی انتخاب نشده است"}
</Typography> </Typography>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -54,7 +54,7 @@ const TableInfo = ({ specialFilter, setTabState, setItemInfo }) => {
}, },
{ {
accessorKey: "sub_item_name", accessorKey: "sub_item_name",
header: وضوع مشاهده شده", header: "مشاهده انجام شده",
id: "sub_item_name", id: "sub_item_name",
enableColumnFilter: false, enableColumnFilter: false,
datatype: "text", datatype: "text",

View File

@@ -35,7 +35,7 @@ const ObservedGashtCreate = ({ mutate }) => {
ثبت فعالیت برای موارد مشاهده شده در گشت راهداری ثبت فعالیت برای موارد مشاهده شده در گشت راهداری
</Button> </Button>
)} )}
{open && <GashtCreate open={open} setOpen={setOpen} mutate={mutate} />} <GashtCreate open={open} setOpen={setOpen} mutate={mutate} />
</> </>
); );
}; };

View File

@@ -17,6 +17,7 @@ import MachinesCodeDialog from "./RowActions/MachinesCodeForm";
const OperatorList = () => { const OperatorList = () => {
const statusOptions = [ const statusOptions = [
{ value: "", label: "همه وضعیت ها" },
{ value: 0, label: "درحال بررسی" }, { value: 0, label: "درحال بررسی" },
{ value: 1, label: "تایید" }, { value: 1, label: "تایید" },
{ value: 2, label: "عدم تایید" }, { value: 2, label: "عدم تایید" },
@@ -54,7 +55,7 @@ const OperatorList = () => {
return [{ value: "error", label: "خطا در بارگذاری" }]; return [{ value: "error", label: "خطا در بارگذاری" }];
} }
return [ return [
{ value: "", label: props.column.header }, { value: "", label: "همه آیتم ها" },
...itemsList.map((item) => ({ ...itemsList.map((item) => ({
value: item.id, value: item.id,
label: item.name, label: item.name,
@@ -76,7 +77,7 @@ const OperatorList = () => {
}, },
{ {
accessorKey: "sub_item", accessorKey: "sub_item",
header: "موضوع مشاهده شده", header: "اقدام انجام شده",
id: "sub_item", id: "sub_item",
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
@@ -99,7 +100,7 @@ const OperatorList = () => {
return [{ value: "error", label: "خطا در بارگذاری" }]; return [{ value: "error", label: "خطا در بارگذاری" }];
} }
return [ return [
{ value: "", label: props.column.header }, { value: "", label: "همه اقدام ها" },
...subItemsList.map((item) => ({ ...subItemsList.map((item) => ({
value: item.sub_item, value: item.sub_item,
label: item.name, label: item.name,
@@ -246,7 +247,7 @@ const OperatorList = () => {
</Stack> </Stack>
); );
} }
return <Typography variant="body2">کد خودرویی وجود ندارد</Typography>; return <Typography variant="body2">خودرویی وجود ندارد</Typography>;
}, },
}, },
{ {

View File

@@ -3,7 +3,7 @@ import React from "react";
import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems"; import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems";
import EditFormCreate from "@/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate"; import EditFormCreate from "@/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate";
const EditFormContent = ({ setOpenEditDialog, onSubmit, defaultData }) => { const EditFormContent = ({ setOpenEditDialog, onSubmit, defaultData, is_gasht }) => {
const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetISubtems(defaultData?.item_id); const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetISubtems(defaultData?.item_id);
const subItem = subItemsList.find((SubItem) => SubItem.sub_item === defaultData?.sub_item_id); const subItem = subItemsList.find((SubItem) => SubItem.sub_item === defaultData?.sub_item_id);
@@ -13,8 +13,10 @@ const EditFormContent = ({ setOpenEditDialog, onSubmit, defaultData }) => {
amount: defaultData?.amount || null, amount: defaultData?.amount || null,
start_point: defaultData?.start_point || { lat: "", lng: "" }, start_point: defaultData?.start_point || { lat: "", lng: "" },
end_point: defaultData?.end_point || { lat: "", lng: "" }, end_point: defaultData?.end_point || { lat: "", lng: "" },
cmms_machines: defaultData?.cmms_machines || null, ...(!is_gasht && {
rahdaran_id: defaultData?.rahdaran_id || null, cmms_machines: defaultData?.cmms_machines || null,
rahdaran_id: defaultData?.rahdaran_id || null,
}),
}; };
const onSubmitBase = async (data) => { const onSubmitBase = async (data) => {
@@ -43,15 +45,20 @@ const EditFormContent = ({ setOpenEditDialog, onSubmit, defaultData }) => {
result.start_point = `${result.start_point.lat},${result.start_point.lng}`; result.start_point = `${result.start_point.lat},${result.start_point.lng}`;
} }
result.rahdaran_id.forEach((rahdar, index) => { if (!is_gasht) {
result[`rahdaran_id[${index}]`] = rahdar.id; result.rahdaran_id.forEach((rahdar, index) => {
}); result[`rahdaran_id[${index}]`] = rahdar.id;
delete result.rahdaran_id; });
delete result.rahdaran_id;
result.cmms_machines.forEach((cmms_machine, index) => { result.cmms_machines.forEach((cmms_machine, index) => {
result[`machines_id[${index}]`] = cmms_machine.id; result[`machines_id[${index}]`] = cmms_machine.id;
}); });
delete result.cmms_machines; delete result.cmms_machines;
} else {
delete result.rahdaran_id;
delete result.cmms_machines;
}
await onSubmit({ await onSubmit({
result, result,
@@ -81,6 +88,7 @@ const EditFormContent = ({ setOpenEditDialog, onSubmit, defaultData }) => {
defaultData={defaultData} defaultData={defaultData}
defaultValues={defaultValues} defaultValues={defaultValues}
setOpenEditDialog={setOpenEditDialog} setOpenEditDialog={setOpenEditDialog}
is_gasht={is_gasht}
/> />
)} )}
</> </>

View File

@@ -13,8 +13,24 @@ import { array, mixed, object, string } from "yup";
const validationSchema = object({ const validationSchema = object({
amount: string().required("وارد کردن مقدار الزامیست!"), amount: string().required("وارد کردن مقدار الزامیست!"),
cmms_machines: array().required("وارد کردن کد خودرو الزامیست!").min(1, "حداقل یک کد خودرو باید وارد شود!"), cmms_machines: array()
rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), .test("cmms-machines-conditional", "وارد کردن کد خودرو الزامیست!", function (value) {
const { is_gasht } = this.options.context;
if (is_gasht) {
return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد
}
return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد
})
.min(1, "حداقل یک کد خودرو باید وارد شود!"), // پیام خطا برای زمانی که شرط تست برقرار باشد
rahdaran_id: array()
.test("rahdaran-id-conditional", "وارد کردن کد راهداران الزامیست!", function (value) {
const { is_gasht } = this.options.context;
if (is_gasht) {
return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد
}
return Array.isArray(value) && value.length > 0; // حداقل یک کد باید وجود داشته باشد
})
.min(1, "حداقل یک کد راهدار باید وارد شود!"),
before_image: mixed() before_image: mixed()
.nullable() .nullable()
.test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) { .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) {
@@ -50,7 +66,7 @@ const validationSchema = object({
}), }),
}); });
const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, setOpenEditDialog }) => { const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, setOpenEditDialog, is_gasht }) => {
const { const {
control, control,
getValues, getValues,
@@ -63,7 +79,7 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set
defaultValues, defaultValues,
resolver: yupResolver(validationSchema), resolver: yupResolver(validationSchema),
mode: "onBlur", mode: "onBlur",
context: { subItem }, context: { subItem, is_gasht },
}); });
return ( return (
@@ -108,38 +124,42 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set
fullWidth fullWidth
/> />
</Stack> </Stack>
<Stack> {!is_gasht && (
<Controller <Stack>
control={control} <Controller
render={({ field, fieldState: { error } }) => { control={control}
return ( render={({ field, fieldState: { error } }) => {
<CarCode return (
carCode={field.value || []} <CarCode
setCarCode={(value) => field.onChange(value || [])} carCode={field.value || []}
inputValueDefault={defaultData?.cmms_machines} setCarCode={(value) => field.onChange(value || [])}
multiple={true} inputValueDefault={defaultData?.cmms_machines}
error={error} // اگر خطا وجود داشته باشد multiple={true}
/> error={error} // اگر خطا وجود داشته باشد
); />
}} );
name={"cmms_machines"} }}
/> name={"cmms_machines"}
</Stack> />
<Stack> </Stack>
<Controller )}
control={control} {!is_gasht && (
render={({ field, fieldState: { error } }) => { <Stack>
return ( <Controller
<RahdarCode control={control}
rahdarsCode={field.value || []} render={({ field, fieldState: { error } }) => {
setRahdarsCode={(value) => field.onChange(value || [])} return (
error={error} <RahdarCode
/> rahdarsCode={field.value || []}
); setRahdarsCode={(value) => field.onChange(value || [])}
}} error={error}
name={"rahdaran_id"} />
/> );
</Stack> }}
name={"rahdaran_id"}
/>
</Stack>
)}
<Stack> <Stack>
{subItem.needs_end_point === 1 ? ( {subItem.needs_end_point === 1 ? (
<MapInfoTwoMarker <MapInfoTwoMarker

View File

@@ -1,41 +1,44 @@
import { DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material";
import Image from "next/image";
const ImageFormContent = ({ image, title }) => { const ImageFormContent = ({ image, title }) => {
const theme = useTheme(); const theme = useTheme();
return ( return (
<DialogContent> <Stack
<Paper spacing={2}
elevation={3} alignItems="center"
justifyContent="center"
sx={{
my: 2,
padding: 2,
borderRadius: "12px",
border: `1px solid ${theme.palette.primary.main}`,
}}
>
<Typography
variant="h6"
sx={{ sx={{
padding: 2, color: theme.palette.primary.dark,
borderRadius: "12px", fontWeight: "bold",
border: `1px solid ${theme.palette.primary.main}`,
}} }}
> >
<Stack spacing={2} alignItems="center" justifyContent="center"> {title}
<Typography </Typography>
variant="h6" <Box sx={{ width: "100%", position: "relative", aspectRatio: "16/9" }}>
sx={{ <Image
color: theme.palette.primary.dark, src={image}
fontWeight: "bold", alt="Image"
}} fill={true}
> loading="lazy"
{title} style={{
</Typography> objectFit: "contain",
<img marginBottom: "16px",
src={image} borderRadius: "12px",
alt="Image" border: `1px solid ${theme.palette.divider}`,
style={{ }}
maxWidth: "100%", />
maxHeight: "200px", </Box>
marginBottom: "16px", </Stack>
borderRadius: "12px",
border: `1px solid ${theme.palette.divider}`,
}}
/>
</Stack>
</Paper>
</DialogContent>
); );
}; };
export default ImageFormContent; export default ImageFormContent;

View File

@@ -1,4 +1,4 @@
import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Paper, Tooltip } from "@mui/material";
import { useState } from "react"; import { useState } from "react";
import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
import ImageFormContent from "./ImageFormContent"; import ImageFormContent from "./ImageFormContent";
@@ -22,14 +22,18 @@ const ImageDialog = ({ images }) => {
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
}, },
}} }}
dir="rtl" maxWidth={"sm"}
maxWidth={"md"} scroll="paper"
> >
<DialogTitle sx={{ fontSize: "large" }}>تصاویر</DialogTitle> <DialogTitle sx={{ fontSize: "large" }}>تصاویر</DialogTitle>
<ImageFormContent image={images[1]?.full_path_for_fast_react} title={"قبل از اقدام"} /> <DialogContent>
<ImageFormContent image={images[0]?.full_path_for_fast_react} title={"بعد از اقدام"} /> <Paper elevation={0}>
<ImageFormContent image={images[1]?.full_path_for_fast_react} title={"قبل از اقدام"} />
<ImageFormContent image={images[0]?.full_path_for_fast_react} title={"بعد از اقدام"} />
</Paper>
</DialogContent>
<DialogActions> <DialogActions>
<Button onClick={() => setOpenImageDialog(false)} variant="outlined" color="secondary" autoFocus> <Button onClick={() => setOpenImageDialog(false)} variant="outlined" color="secondary">
بستن بستن
</Button> </Button>
</DialogActions> </DialogActions>

View File

@@ -7,7 +7,7 @@ const MachinesCodeDialog = ({ machinesLists }) => {
const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false); const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false);
return ( return (
<> <>
<Tooltip title="کد خودرو"> <Tooltip title="خودرو ها">
<IconButton color="primary" onClick={() => setOpenMachinesCodeDialog(true)}> <IconButton color="primary" onClick={() => setOpenMachinesCodeDialog(true)}>
<DirectionsCarIcon /> <DirectionsCarIcon />
</IconButton> </IconButton>
@@ -24,7 +24,7 @@ const MachinesCodeDialog = ({ machinesLists }) => {
dir="rtl" dir="rtl"
maxWidth={"md"} maxWidth={"md"}
> >
<DialogTitle sx={{ fontSize: "large" }}>کد خودرو</DialogTitle> <DialogTitle sx={{ fontSize: "large" }}>خودرو ها</DialogTitle>
<MachinesCodeContent machinesLists={machinesLists} /> <MachinesCodeContent machinesLists={machinesLists} />
<DialogActions> <DialogActions>
<Button <Button

View File

@@ -84,6 +84,7 @@ const ReportLists = ({ itemsList, data }) => {
data={data.data} data={data.data}
page_name={"roadItemReportByItems"} page_name={"roadItemReportByItems"}
table_name={"roadItemReportByItemsList"} table_name={"roadItemReportByItemsList"}
enablePagination={false}
/> />
</Box> </Box>
); );

View File

@@ -9,6 +9,7 @@ const SubItemsList = ({ data }) => {
data={data.data} data={data.data}
page_name={"roadItemReportBySubItems"} page_name={"roadItemReportBySubItems"}
table_name={"roadItemReportBySubItemsList"} table_name={"roadItemReportBySubItemsList"}
enablePagination={false}
/> />
</Box> </Box>
); );

View File

@@ -22,7 +22,7 @@ const createColumns = (sub_items) => {
columns: [ columns: [
{ {
accessorKey: `sub_item_${sub_item.sub_item}_count`, accessorKey: `sub_item_${sub_item.sub_item}_count`,
header: "تعداد", header: "تعداد اقدام",
id: `sub_item_${sub_item.sub_item}_count`, id: `sub_item_${sub_item.sub_item}_count`,
enableColumnFilter: false, enableColumnFilter: false,
enableSorting: false, enableSorting: false,
@@ -81,7 +81,7 @@ const createColumns = (sub_items) => {
size: 50, size: 50,
}, },
{ {
header: "موارد مشاهده شده فعالیت روزانه", header: "موارد اقدام شده فعالیت روزانه",
enableColumnFilter: false, enableColumnFilter: false,
id: "subItems", id: "subItems",
enableSorting: false, enableSorting: false,
@@ -228,7 +228,7 @@ const SubItemsReports = () => {
}); });
return ( return (
<Stack spacing={1}> <Stack spacing={1}>
<PageTitle title={"گزارش تعداد فعالیت ها به تفکیک موارد مشاهده شده"} /> <PageTitle title={"گزارش تعداد فعالیت ها به تفکیک موارد اقدام شده"} />
<SearchReportList <SearchReportList
control={control} control={control}
hasProvincesPermission={hasProvincesPermission} hasProvincesPermission={hasProvincesPermission}

View File

@@ -40,10 +40,12 @@ const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => {
{...register("description")} {...register("description")}
multiline multiline
rows={8} rows={8}
label="توضیحات کارشناس" label="توضیحات و دلایل تأیید (اختیاری)"
placeholder="لطفاً علت تأیید خود را توضیح دهید"
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{ mt: 1 }}
InputLabelProps={{ shrink: true }}
/> />
</Stack> </Stack>
</Stack> </Stack>
@@ -59,7 +61,7 @@ const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => {
بستن بستن
</Button> </Button>
<Button onClick={handleSubmit(onSubmit)} variant="contained" color="primary" disabled={isSubmitting}> <Button onClick={handleSubmit(onSubmit)} variant="contained" color="primary" disabled={isSubmitting}>
{isSubmitting ? "درحال ارسال اطلاعات..." : "ثبت"} {isSubmitting ? "درحال ثبت تایید..." : "ثبت تایید"}
</Button> </Button>
</DialogActions> </DialogActions>
</> </>

View File

@@ -6,7 +6,7 @@ const ConfirmForm = ({ rowId, mutate }) => {
const [openConfirmDialog, setOpenConfirmDialog] = useState(false); const [openConfirmDialog, setOpenConfirmDialog] = useState(false);
return ( return (
<> <>
<Tooltip title="تایید"> <Tooltip title="تایید فعالیت">
<IconButton color="primary" onClick={() => setOpenConfirmDialog(true)}> <IconButton color="primary" onClick={() => setOpenConfirmDialog(true)}>
<DoneIcon /> <DoneIcon />
</IconButton> </IconButton>
@@ -20,7 +20,7 @@ const ConfirmForm = ({ rowId, mutate }) => {
}, },
}} }}
> >
<DialogTitle>تایید</DialogTitle> <DialogTitle>تایید فعالیت</DialogTitle>
<ConfirmContent mutate={mutate} rowId={rowId} setOpenConfirmDialog={setOpenConfirmDialog} /> <ConfirmContent mutate={mutate} rowId={rowId} setOpenConfirmDialog={setOpenConfirmDialog} />
</Dialog> </Dialog>
</> </>

View File

@@ -22,9 +22,7 @@ const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => {
<> <>
<DialogContent> <DialogContent>
<Stack alignItems="center" spacing={2}> <Stack alignItems="center" spacing={2}>
<Typography textAlign="center" fontWeight="bold" fontSize="16px" mt={2}> <Typography mt={2}>آیا از حذف فعالیت اطمینان دارید؟</Typography>
آیا از حذف فعالیت اطمینان دارید؟
</Typography>
</Stack> </Stack>
</DialogContent> </DialogContent>
<DialogActions sx={{ justifyContent: "center", paddingBottom: 2, width: "100%" }}> <DialogActions sx={{ justifyContent: "center", paddingBottom: 2, width: "100%" }}>
@@ -32,7 +30,7 @@ const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => {
خیر ! خیر !
</Button> </Button>
<Button onClick={handleClick} variant="contained" color="error" disabled={submitting}> <Button onClick={handleClick} variant="contained" color="error" disabled={submitting}>
{submitting ? "درحال ارسال اطلاعات..." : "بله اطمینان دارم"} {submitting ? "درحال حذف..." : "بله اطمینان دارم"}
</Button> </Button>
</DialogActions> </DialogActions>
</> </>

View File

@@ -1,41 +1,44 @@
import { DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material";
import Image from "next/image";
const ImageFormContent = ({ image, title }) => { const ImageFormContent = ({ image, title }) => {
const theme = useTheme(); const theme = useTheme();
return ( return (
<DialogContent> <Stack
<Paper spacing={2}
elevation={3} alignItems="center"
justifyContent="center"
sx={{
my: 2,
padding: 2,
borderRadius: "12px",
border: `1px solid ${theme.palette.primary.main}`,
}}
>
<Typography
variant="h6"
sx={{ sx={{
padding: 2, color: theme.palette.primary.dark,
borderRadius: "12px", fontWeight: "bold",
border: `1px solid ${theme.palette.primary.main}`,
}} }}
> >
<Stack spacing={2} alignItems="center" justifyContent="center"> {title}
<Typography </Typography>
variant="h6" <Box sx={{ width: "100%", position: "relative", aspectRatio: "16/9" }}>
sx={{ <Image
color: theme.palette.primary.dark, src={image}
fontWeight: "bold", alt="Image"
}} fill={true}
> loading="lazy"
{title} style={{
</Typography> objectFit: "contain",
<img marginBottom: "16px",
src={image} borderRadius: "12px",
alt="Image" border: `1px solid ${theme.palette.divider}`,
style={{ }}
maxWidth: "100%", />
maxHeight: "200px", </Box>
marginBottom: "16px", </Stack>
borderRadius: "12px",
border: `1px solid ${theme.palette.divider}`,
}}
/>
</Stack>
</Paper>
</DialogContent>
); );
}; };
export default ImageFormContent; export default ImageFormContent;

View File

@@ -1,4 +1,4 @@
import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Paper, Tooltip } from "@mui/material";
import { useState } from "react"; import { useState } from "react";
import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
import ImageFormContent from "./ImageFormContent"; import ImageFormContent from "./ImageFormContent";
@@ -22,14 +22,18 @@ const ImageDialog = ({ images }) => {
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
}, },
}} }}
dir="rtl" maxWidth={"sm"}
maxWidth={"md"} scroll="paper"
> >
<DialogTitle>تصاویر</DialogTitle> <DialogTitle sx={{ fontSize: "large" }}>تصاویر</DialogTitle>
<ImageFormContent image={images[1].full_path_for_fast_react} title={"قبل از اقدام"} /> <DialogContent>
<ImageFormContent image={images[0].full_path_for_fast_react} title={"بعد از اقدام"} /> <Paper elevation={0}>
<ImageFormContent image={images[1]?.full_path_for_fast_react} title={"قبل از اقدام"} />
<ImageFormContent image={images[0]?.full_path_for_fast_react} title={"بعد از اقدام"} />
</Paper>
</DialogContent>
<DialogActions> <DialogActions>
<Button onClick={() => setOpenImageDialog(false)} variant="outlined" color="secondary" autoFocus> <Button onClick={() => setOpenImageDialog(false)} variant="outlined" color="secondary">
بستن بستن
</Button> </Button>
</DialogActions> </DialogActions>

View File

@@ -7,7 +7,7 @@ const MachinesCodeDialog = ({ machinesLists }) => {
const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false); const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false);
return ( return (
<> <>
<Tooltip title="کد خودرو"> <Tooltip title="خودرو ها">
<IconButton color="primary" onClick={() => setOpenMachinesCodeDialog(true)}> <IconButton color="primary" onClick={() => setOpenMachinesCodeDialog(true)}>
<DirectionsCarIcon /> <DirectionsCarIcon />
</IconButton> </IconButton>
@@ -24,7 +24,7 @@ const MachinesCodeDialog = ({ machinesLists }) => {
dir="rtl" dir="rtl"
maxWidth={"md"} maxWidth={"md"}
> >
<DialogTitle sx={{ fontSize: "large" }}>کد خودرو</DialogTitle> <DialogTitle sx={{ fontSize: "large" }}>خودرو ها</DialogTitle>
<MachinesCodeContent machinesLists={machinesLists} /> <MachinesCodeContent machinesLists={machinesLists} />
<DialogActions> <DialogActions>
<Button <Button

View File

@@ -9,7 +9,7 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => {
const requestServer = useRequest({ notificationSuccess: true }); const requestServer = useRequest({ notificationSuccess: true });
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
description: Yup.string().required("توضیحات الزامیست!!!"), description: Yup.string().required("لطفاً توضیحات را وارد کنید."),
}); });
const { const {
@@ -51,12 +51,14 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => {
{...register("description")} {...register("description")}
multiline multiline
rows={8} rows={8}
label="توضیحات کارشناس" label="توضیحات و دلایل عدم تأیید"
placeholder="لطفاً علت عدم تأیید خود را توضیح دهید"
error={!!errors.description} error={!!errors.description}
helperText={errors.description?.message} helperText={errors.description?.message}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ mt: 1 }} sx={{ mt: 1 }}
InputLabelProps={{ shrink: true }}
/> />
</Stack> </Stack>
</Stack> </Stack>
@@ -72,7 +74,7 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => {
بستن بستن
</Button> </Button>
<Button onClick={handleSubmit(onSubmit)} variant="contained" color="primary" disabled={isSubmitting}> <Button onClick={handleSubmit(onSubmit)} variant="contained" color="primary" disabled={isSubmitting}>
{isSubmitting ? "درحال ارسال اطلاعات..." : "عدم تایید"} {isSubmitting ? "درحال ثبت عدم تایید..." : "ثبت عدم تایید"}
</Button> </Button>
</DialogActions> </DialogActions>
</> </>

View File

@@ -6,7 +6,7 @@ const RejectForm = ({ rowId, mutate }) => {
const [openRejectDialog, setOpenRejectDialog] = useState(false); const [openRejectDialog, setOpenRejectDialog] = useState(false);
return ( return (
<> <>
<Tooltip title="عدم تایید"> <Tooltip title="عدم تایید فعالیت">
<IconButton color="primary" onClick={() => setOpenRejectDialog(true)}> <IconButton color="primary" onClick={() => setOpenRejectDialog(true)}>
<ClearIcon /> <ClearIcon />
</IconButton> </IconButton>
@@ -20,7 +20,7 @@ const RejectForm = ({ rowId, mutate }) => {
}, },
}} }}
> >
<DialogTitle>عدم تایید</DialogTitle> <DialogTitle>عدم تایید فعالیت</DialogTitle>
<RejectContent mutate={mutate} rowId={rowId} setOpenRejectDialog={setOpenRejectDialog} /> <RejectContent mutate={mutate} rowId={rowId} setOpenRejectDialog={setOpenRejectDialog} />
</Dialog> </Dialog>
</> </>

View File

@@ -19,6 +19,7 @@ import { useAuth } from "@/lib/contexts/auth";
const SupervisorList = () => { const SupervisorList = () => {
const statusOptions = [ const statusOptions = [
{ value: "", label: "همه وضعیت ها" },
{ value: 0, label: "درحال بررسی" }, { value: 0, label: "درحال بررسی" },
{ value: 1, label: "تایید" }, { value: 1, label: "تایید" },
{ value: 2, label: "عدم تایید" }, { value: 2, label: "عدم تایید" },
@@ -46,7 +47,7 @@ const SupervisorList = () => {
return [{ value: "error", label: "خطا در بارگذاری" }]; return [{ value: "error", label: "خطا در بارگذاری" }];
} }
return [ return [
{ value: "", label: props.column.header }, { value: "", label: "کل کشور" },
...provinces.map((province) => ({ ...provinces.map((province) => ({
value: province.id, value: province.id,
label: province.name_fa, label: province.name_fa,
@@ -106,7 +107,7 @@ const SupervisorList = () => {
return [{ value: "error", label: "خطا در بارگذاری" }]; return [{ value: "error", label: "خطا در بارگذاری" }];
} }
return [ return [
{ value: "", label: props.column.header }, { value: "", label: "کل ادارات" },
...edaratList.map((edare) => ({ ...edaratList.map((edare) => ({
value: edare.id, value: edare.id,
label: edare.name_fa, label: edare.name_fa,
@@ -155,7 +156,7 @@ const SupervisorList = () => {
return [{ value: "error", label: "خطا در بارگذاری" }]; return [{ value: "error", label: "خطا در بارگذاری" }];
} }
return [ return [
{ value: "", label: props.column.header }, { value: "", label: "همه آیتم ها" },
...itemsList.map((item) => ({ ...itemsList.map((item) => ({
value: item.id, value: item.id,
label: item.name, label: item.name,
@@ -177,7 +178,7 @@ const SupervisorList = () => {
}, },
{ {
accessorKey: "sub_item", accessorKey: "sub_item",
header: "موضوع مشاهده شده", header: "اقدام انجام شده",
id: "sub_item", id: "sub_item",
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "numeric",
@@ -200,7 +201,7 @@ const SupervisorList = () => {
return [{ value: "error", label: "خطا در بارگذاری" }]; return [{ value: "error", label: "خطا در بارگذاری" }];
} }
return [ return [
{ value: "", label: props.column.header }, { value: "", label: "همه اقدام ها" },
...subItemsList.map((item) => ({ ...subItemsList.map((item) => ({
value: item.sub_item, value: item.sub_item,
label: item.name, label: item.name,
@@ -347,7 +348,7 @@ const SupervisorList = () => {
</Stack> </Stack>
); );
} }
return <Typography variant="body2">کد خودرویی وجود ندارد</Typography>; return <Typography variant="body2">خودرویی وجود ندارد</Typography>;
}, },
}, },
{ {

View File

@@ -26,7 +26,7 @@ const OperatorCreate = ({ mutate }) => {
ثبت اقدام ثبت اقدام
</Button> </Button>
)} )}
{open && <CreatePatrol open={open} setOpen={setOpen} mutate={mutate} />} <CreatePatrol open={open} setOpen={setOpen} mutate={mutate} />
</> </>
); );
}; };

View File

@@ -1,75 +1,25 @@
"use client"; "use client";
import React from "react";
import dynamic from "next/dynamic";
import MapLoading from "@/core/components/MapLayer/Loading";
import {
Box,
Card,
CardActions,
CardContent,
CardHeader,
Chip,
Divider,
IconButton,
Stack,
Typography,
} from "@mui/material";
import HandymanIcon from "@mui/icons-material/Handyman";
import DeleteIcon from "@mui/icons-material/Delete"; import DeleteIcon from "@mui/icons-material/Delete";
import DirectionsCarIcon from "@mui/icons-material/DirectionsCar"; import HandymanIcon from "@mui/icons-material/Handyman";
import PersonIcon from "@mui/icons-material/Person"; import { Box, Card, CardActions, CardHeader, IconButton, Typography } from "@mui/material";
import DesignServicesIcon from "@mui/icons-material/DesignServices";
import EditActionDuringPatrol from "./EditActionDuringPatrol"; import EditActionDuringPatrol from "./EditActionDuringPatrol";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => <MapLoading />,
ssr: false,
});
const ActionInfo = ({ action, setActionsList, index, deleteAction }) => { const ActionInfo = ({ action, setActionsList, index, deleteAction }) => {
return ( return (
<Card sx={{ maxWidth: 300 }}> <Card
elevation={0}
sx={{
maxWidth: 300,
border: 1,
borderColor: "divider",
}}
>
<CardHeader <CardHeader
avatar={<HandymanIcon color="primary" sx={{ width: "30px", height: "30px" }} />} avatar={<HandymanIcon color="primary" sx={{ width: "30px", height: "30px" }} />}
title={<Typography sx={{ fontSize: "14px", fontWeight: 500 }}>{action.data.item_name}</Typography>} title={<Typography sx={{ fontSize: "14px", fontWeight: 500 }}>{action.data.item_name}</Typography>}
subheader={action.data.sub_item_name} subheader={action.data.sub_item_name}
/> />
<CardContent>
<Stack sx={{ gap: 1 }}>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<DesignServicesIcon />
<Typography variant="button">میزان فعالیت انجام شده:</Typography>
<Typography variant="button">
{action.data.amount}({action.data.unit_fa})
</Typography>
</Box>
<Box>
<Divider sx={{ mb: 1 }}>
<Chip size="small" label="خودرو ها" icon={<DirectionsCarIcon />} />
</Divider>
<Stack>
{action.data.cmms_machines.map((cmms_machine, index) => (
<Typography variant="button" key={index}>
{index + 1}: {cmms_machine.car_name}
</Typography>
))}
</Stack>
</Box>
<Box>
<Divider sx={{ mb: 1 }}>
<Chip size="small" label="راهداران" icon={<PersonIcon />} />
</Divider>
<Stack>
{action.data.rahdaran_id.map((rahdar, index) => (
<Typography variant="button" key={index}>
{index + 1}: {rahdar.name}
</Typography>
))}
</Stack>
</Box>
</Stack>
</CardContent>
<CardActions disableSpacing> <CardActions disableSpacing>
<Box> <Box>
<IconButton aria-label="حذف فعالیت" color="error" onClick={deleteAction}> <IconButton aria-label="حذف فعالیت" color="error" onClick={deleteAction}>

View File

@@ -1,14 +1,14 @@
"use client"; "use client";
import { Box, Button, Divider, Grid, Typography } from "@mui/material";
import AddCircleIcon from "@mui/icons-material/AddCircle"; import AddCircleIcon from "@mui/icons-material/AddCircle";
import React, { useState } from "react"; import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import ModalActionsDuringPatrol from "./ModalActionsDuringPatrol"; import { Box, Button, Chip, DialogActions, DialogContent, Divider, Grid, Stack, Typography } from "@mui/material";
import { useState } from "react";
import ActionInfo from "./ActionInfo"; import ActionInfo from "./ActionInfo";
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; import ModalActionsDuringPatrol from "./ModalActionsDuringPatrol";
const ActionsDuringPatrol = ({ setValue, tabState, setTabState }) => { const ActionsDuringPatrol = ({ allData, setAllData, handlePrev, setTabState }) => {
const [ActionsList, setActionsList] = useState([]); const [actionsList, setActionsList] = useState(allData.observed_items);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const handleOpen = () => { const handleOpen = () => {
setOpen(true); setOpen(true);
@@ -18,72 +18,77 @@ const ActionsDuringPatrol = ({ setValue, tabState, setTabState }) => {
setActionsList((prev) => prev.filter((_, index) => index !== indexToRemove)); setActionsList((prev) => prev.filter((_, index) => index !== indexToRemove));
}; };
const SubmitActionDuringPatrol = () => { const handleNext = (data) => {
setTabState(tabState + 1); setAllData({ observed_items: data });
const resultItems = ActionsList.map((item) => item.result); setTabState((s) => s + 1);
console.log("resultItems", resultItems);
setValue("observed_items", resultItems);
}; };
return ( return (
<Box> <>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <DialogContent dividers sx={{ display: "flex" }}>
<Typography variant="h6">لیست اقدامات حین گشت</Typography> <Box sx={{ flex: 1 }}>
<Button <Stack
onClick={handleOpen} direction={"row"}
startIcon={<AddCircleIcon />} justifyContent={"center"}
variant="outlined" sx={{
sx={{ mb: 1, alignSelf: "end" }} mt: 1,
> mb: 2,
ثبت اقدام }}
</Button>
<ModalActionsDuringPatrol open={open} setOpen={setOpen} setActionsList={setActionsList} />
</Box>
<Divider />
{ActionsList.length !== 0 ? (
<Grid
container
spacing={2}
sx={{
p: 1,
mt: 1,
maxHeight: "300px",
overflowY: "auto",
}}
>
{ActionsList.map((action, index) => (
<Grid key={index} item xs={12} sm={6} lg={4}>
<ActionInfo
action={action}
setActionsList={setActionsList}
index={index}
deleteAction={() => deleteAction(index)}
/>
</Grid>
))}
</Grid>
) : (
<Box sx={{ my: 5, width: "100%", textAlign: "center" }}>
<Typography variant="h6" sx={{ letterSpacing: "2px", color: "#606060" }}>
فعالیتی ثبت نکرده اید
</Typography>
</Box>
)}
<Box>
<Box sx={{ display: "flex", gap: 1, justifyContent: "center" }}>
<Button
variant="contained"
color="primary"
onClick={SubmitActionDuringPatrol}
disabled={ActionsList === []}
endIcon={<KeyboardDoubleArrowLeftIcon />}
sx={{ mt: 2 }}
> >
نهایی کردن درخواست <Button onClick={handleOpen} startIcon={<AddCircleIcon />} variant="outlined" sx={{ mb: 1 }}>
</Button> ثبت اقدام انجام شده
</Button>
<ModalActionsDuringPatrol open={open} setOpen={setOpen} setActionsList={setActionsList} />
</Stack>
<Divider sx={{ my: 2, width: "100%" }}>
<Chip variant="outlined" label="لیست اقدامات حین گشت" />
</Divider>
{actionsList.length !== 0 ? (
<Grid
container
spacing={2}
sx={{
p: 1,
mt: 1,
maxHeight: "400px",
overflowY: "auto",
}}
>
{actionsList.map((action, index) => (
<Grid key={index} item xs={12} sm={6} lg={4}>
<ActionInfo
action={action}
setActionsList={setActionsList}
index={index}
deleteAction={() => deleteAction(index)}
/>
</Grid>
))}
</Grid>
) : (
<Box sx={{ my: 5, width: "100%", textAlign: "center" }}>
<Typography variant="h6" sx={{ letterSpacing: "2px", color: "#606060" }}>
فعالیتی ثبت نکرده اید
</Typography>
</Box>
)}
</Box> </Box>
</Box> </DialogContent>
</Box> <DialogActions sx={{ alignItems: "center", justifyContent: "center" }}>
<Button
onClick={handlePrev}
variant="outlined"
color="secondary"
size="large"
startIcon={<ExitToAppIcon />}
>
{"صفحه قبل"}
</Button>
<Button variant="contained" size="large" onClick={() => handleNext(actionsList)}>
{"تایید اقدامات و ادامه"}
</Button>
</DialogActions>
</>
); );
}; };

View File

@@ -1,10 +1,20 @@
"use client"; "use client";
import React, { useEffect, useState } from "react"; import ShowPlak from "@/core/components/ShowPlak";
import StyledForm from "@/core/components/StyledForm";
import { formatCounter } from "@/core/utils/formatCounter";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
import { CREATE_PATROL, GET_OTP_TOKEN } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
import { yupResolver } from "@hookform/resolvers/yup";
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import ForwardToInboxIcon from "@mui/icons-material/ForwardToInbox";
import { import {
Box, Box,
Button, Button,
Chip, Chip,
DialogActions,
DialogContent,
Divider, Divider,
FormControl, FormControl,
Grid, Grid,
@@ -14,39 +24,53 @@ import {
Stack, Stack,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import BeenhereIcon from "@mui/icons-material/Beenhere";
import ForwardToInboxIcon from "@mui/icons-material/ForwardToInbox";
import { formatCounter } from "@/core/utils/formatCounter";
import useRequest from "@/lib/hooks/useRequest";
import { GET_OTP_TOKEN } from "@/core/utils/routes";
import moment from "jalali-moment"; import moment from "jalali-moment";
import { useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import * as yup from "yup";
const CompeleteRequest = ({ watch, isSubmitting, setValue, register }) => { const schemaOtp = yup.object().shape({
const requestServer = useRequest({ auth: true }); phoneNumber: yup
.string()
.matches(/^09\d{9}$/, "شماره تماس باید با 09 شروع شود و 11 رقم باشد.")
.required("شماره تماس الزامی است."),
});
const schemaSend = yup.object().shape({
phoneNumber: yup
.string()
.matches(/^09\d{9}$/, "شماره تماس باید با 09 شروع شود و 11 رقم باشد.")
.required("شماره تماس الزامی است."),
code: yup.string().length(6).required("شماره تماس الزامی است."),
});
const CompeleteRequest = ({ allData, handlePrev, mutate, setOpen }) => {
const requestServer = useRequest();
const [delayPerRequest, setDelayPerRequest] = useState(false); const [delayPerRequest, setDelayPerRequest] = useState(false);
const [phoneNumber, setPhoneNumber] = useState("");
const [validPhone, setValidPhone] = useState(false);
const [verificationCode, setVerificationCode] = useState("");
const [counter, setCounter] = useState(120); const [counter, setCounter] = useState(120);
const [otpSended, setOtpSended] = useState(false); const [otpSended, setOtpSended] = useState(false);
const [readyToVerify, setReadyToVerify] = useState(false);
const handleNumericInput = (e) => { const defaultValuesOtp = {
e.target.value = e.target.value.replace(/[^0-9]/g, ""); phoneNumber: "",
}; };
useEffect(() => { const defaultValuesSend = {
if (phoneNumber.length === 11) { phoneNumber: "",
setValidPhone(true); code: "",
} else { };
setValidPhone(false);
} const {
if (verificationCode.length === 6 && phoneNumber.length === 11) { handleSubmit: handleSubmitOtp,
setReadyToVerify(true); register: registerOtp,
} else { formState: { isSubmitting: isSubmittingOtp, isValid: isValidOtp },
setReadyToVerify(false); } = useForm({ defaultValues: defaultValuesOtp, resolver: yupResolver(schemaOtp), mode: "onBlur" });
}
}, [phoneNumber, verificationCode]); const {
handleSubmit: handleSubmitSend,
setValue: setValueSend,
register: registerSend,
formState: { isSubmitting: isSubmittingSend, isValid: isValidSend },
} = useForm({ defaultValues: defaultValuesSend, resolver: yupResolver(schemaSend), mode: "onBlur" });
useEffect(() => { useEffect(() => {
if (counter === 0) { if (counter === 0) {
@@ -62,167 +86,265 @@ const CompeleteRequest = ({ watch, isSubmitting, setValue, register }) => {
} }
}, [counter, delayPerRequest]); }, [counter, delayPerRequest]);
const sendOtp = () => { const sendOtp = async (data) => {
if (!validPhone) return false; try {
requestServer(`${GET_OTP_TOKEN}?phone_number=${phoneNumber}`, "get") const response = await requestServer(`${GET_OTP_TOKEN}?phone_number=${data.phoneNumber}`, "get");
.then((response) => { setOtpSended(true);
setOtpSended(true); setDelayPerRequest(true);
setDelayPerRequest(true); setValueSend("phoneNumber", data.phoneNumber);
}) } catch (error) {}
.catch(() => { });
}; };
const sendData = useCallback(
async (data) => {
const formData = new FormData();
allData.road_patrol_rahdaran_id.forEach((rahdar, index) =>
formData.append(`road_patrol_rahdaran_id[${index}]`, rahdar.id)
);
formData.append(`road_patrol_machines_id[0]`, allData.roadPatrolMachinesId.id);
allData.stopPoints.forEach((stop_point, index) => {
formData.append(`stop_points[${index}][duration]`, stop_point.duration);
formData.append(`stop_points[${index}][latitude]`, stop_point.latitude);
formData.append(`stop_points[${index}][longitude]`, stop_point.longitude);
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, index) =>
formData.append(`observed_items[${index}][road_item_rahdaran_id][]`, rahdar.id)
);
});
formData.append("start_time", allData.start_time);
formData.append("end_time", allData.end_time);
formData.append("vehicle_runtime", allData.accOnDuration);
formData.append("fuel_consumption", +allData.fuelConsumption);
formData.append("distance", allData.mileage);
// formData.append("description", "");
formData.append("phone_number", data.phoneNumber);
formData.append("verification_code", data.code);
try {
await requestServer(CREATE_PATROL, "post", {
data: formData,
});
mutate();
setOpen(false);
} catch (error) {
console.log(error);
}
},
[allData]
);
return ( return (
<Stack> <>
<Stack> <DialogContent dividers sx={{ display: "flex" }}>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ flex: 1 }}>
<Box> <Stack>
<Typography sx={{ color: "#777777", fontWeight: 500 }} variant="body1">
{moment(watch("start_time")).locale("fa").format("YYYY/MM/DD | HH:mm")}
</Typography>
</Box>
<Box>
<Typography sx={{ color: "#777777", fontWeight: 500 }} variant="body1">
{moment(watch("end_time")).locale("fa").format("YYYY/MM/DD | HH:mm")}
</Typography>
</Box>
</Box>
<Grid container spacing={2} sx={{ alignItems: "start", my: 1 }}>
<Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
<Chip label="مدت زمان روشن بودن خودرو" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{watch("vehicle_runtime")}</Typography>
</Grid>
<Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
<Chip label="میزان سوخت مصرفی" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{watch("fuel_consumption")}</Typography>
</Grid>
<Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
<Chip label="تعداد نقاط توقف" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{watch("stop_points").length}</Typography>
</Grid>
<Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
<Chip label="مسافت پیموده شده" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{watch("distance")}</Typography>
</Grid>
<Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
<Chip label="تعداد فعالیت های حین گشت" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{watch("observed_items").length}</Typography>
</Grid>
</Grid>
<Grid container spacing={3} sx={{ alignItems: "start", my: 2 }}>
<Grid item xs={12} lg={6}>
<Divider> <Divider>
<Chip label="خودرو گشت" /> <Chip label="اطلاعات گشت" variant="outlined" />
</Divider> </Divider>
<Box sx={{ display: "flex", alignItems: "center", my: 1 }}> <Grid container spacing={2} sx={{ alignItems: "start", my: 1 }}>
<Typography>کد</Typography> <Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
<Divider sx={{ flex: 1, mx: 2 }} /> <Chip label="تاریخ و زمان شروع" />
<Typography>{watch("road_patrol_machines_id.machine_code")}</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", my: 1 }}>
<Typography>نام</Typography>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{watch("road_patrol_machines_id.car_name")}</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", my: 1 }}>
<Typography>پلاک</Typography>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{watch("road_patrol_machines_id.plak_number")}</Typography>
</Box>
</Grid>
<Grid item xs={12} lg={6}>
<Divider>
<Chip label="راهدار / راهداران حاضر در گشت" />
</Divider>
{watch("road_patrol_rahdaran_id")?.map((rahdar, index) => (
<Box key={index} sx={{ display: "flex", alignItems: "center", my: 1 }}>
<Typography>نام و کد راهدار</Typography>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography> <Typography>
{rahdar.name} | {rahdar.code} {allData.start_time !== "" &&
moment(allData.start_time).locale("fa").format("YYYY/MM/DD | HH:mm")}
</Typography> </Typography>
</Box> </Grid>
))} <Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
</Grid> <Chip label="تاریخ و زمان پایان" />
</Grid> <Divider sx={{ flex: 1, mx: 2 }} />
<Box sx={{ display: "flex", gap: 2, my: 2, justifyContent: "space-around" }}> <Typography>
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}> {allData.start_time !== "" &&
<FormControl size="small" fullWidth variant="outlined"> moment(allData.start_time).locale("fa").format("YYYY/MM/DD | HH:mm")}
<InputLabel htmlFor="phone_number">شماره تلفن مامور گشت</InputLabel> </Typography>
<OutlinedInput </Grid>
id="phone_number" </Grid>
label={"شماره تلفن مامور گشت"} <Grid container spacing={2} sx={{ alignItems: "start", my: 1 }}>
disabled={delayPerRequest} <Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
autoComplete="off" <Chip label="مدت زمان روشن بودن خودرو" />
size="small" <Divider sx={{ flex: 1, mx: 2 }} />
fullWidth <Typography>
{...register("phone_number")} {allData.accOnDuration && formatSecondsToHHMMSS(allData.accOnDuration)}
value={phoneNumber} </Typography>
onChange={(e) => setPhoneNumber(e.target.value)} </Grid>
onInput={handleNumericInput} <Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
type="text" <Chip label="میزان سوخت مصرفی" />
inputProps={{ <Divider sx={{ flex: 1, mx: 2 }} />
maxLength: 11, <Typography>
}} {allData.fuelConsumption && Math.round(allData.fuelConsumption * 10) / 10} لیتر
/> </Typography>
</FormControl> </Grid>
<Button <Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
onClick={sendOtp} <Chip label="تعداد نقاط توقف" />
disabled={!validPhone || delayPerRequest} <Divider sx={{ flex: 1, mx: 2 }} />
variant="contained" <Typography>{allData.stopPoints && allData.stopPoints.length}</Typography>
color="success" </Grid>
sx={{ textWrap: "nowrap", px: 4 }} <Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
endIcon={<ForwardToInboxIcon />} <Chip label="مسافت پیموده شده" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>
{allData.mileage && (allData.mileage / 1000).toFixed(1)} کیلومتر
</Typography>
</Grid>
<Grid item xs={12} lg={6} sx={{ display: "flex", alignItems: "center" }}>
<Chip label="تعداد فعالیت های حین گشت" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>{allData.observed_items && allData.observed_items.length}</Typography>
</Grid>
</Grid>
<Grid container spacing={3} sx={{ alignItems: "start", my: 2 }}>
<Grid item xs={12} lg={6}>
<Divider>
<Chip label="خودرو گشت" />
</Divider>
<Stack direction={"row"} sx={{ alignItems: "center", my: 1 }}>
<Chip label="کد خودرو" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>
{allData.roadPatrolMachinesId && allData.roadPatrolMachinesId.machine_code}
</Typography>
</Stack>
<Stack direction={"row"} sx={{ alignItems: "center", my: 1 }}>
<Chip label="نام خودرو" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography>
{allData.roadPatrolMachinesId && allData.roadPatrolMachinesId.car_name}
</Typography>
</Stack>
<Stack direction={"row"} sx={{ alignItems: "center", my: 1 }}>
<Chip label="پلاک خودرو" />
<Divider sx={{ flex: 1, mx: 2 }} />
<Box sx={{ fontSize: 11 }}>
{allData.roadPatrolMachinesId.plak_number &&
ShowPlak({ plak_number: allData.roadPatrolMachinesId.plak_number })}
</Box>
</Stack>
</Grid>
<Grid item xs={12} lg={6}>
<Divider>
<Chip label="راهدار / راهداران حاضر در گشت" />
</Divider>
{allData.road_patrol_rahdaran_id.map((rahdar, index) => (
<Stack key={rahdar.code} direction={"row"} sx={{ alignItems: "center", my: 1 }}>
<Chip label={`نام و کد راهدار ${index + 1}`} />
<Divider sx={{ flex: 1, mx: 2 }} />
<Box sx={{ fontSize: 11 }}>
<Typography>
{rahdar.name} | {rahdar.code}
</Typography>
</Box>
</Stack>
))}
</Grid>
</Grid>
<Divider>
<Chip label="احراز هویت" variant="outlined" />
</Divider>
<Box
sx={{
display: "flex",
gap: 2,
my: 2,
justifyContent: "space-around",
flexDirection: { xs: "column", md: "row" },
}}
> >
{delayPerRequest ? formatCounter(counter) : "دریافت کد"} <StyledForm onSubmit={handleSubmitOtp(sendOtp)}>
</Button> <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
</Box> <FormControl size="small" fullWidth variant="outlined">
<Slide in={otpSended}> <InputLabel htmlFor="phoneNumber">شماره تلفن مامور گشت</InputLabel>
<Box> <OutlinedInput
<FormControl size="small" fullWidth variant="outlined" focused> id="phoneNumber"
<InputLabel htmlFor="verification_code">کد پیامک شده</InputLabel> label={"شماره تلفن مامور گشت"}
<OutlinedInput disabled={delayPerRequest}
id="verification_code" autoComplete="off"
label={"کد پیامک شده"} size="small"
placeholder="------" fullWidth
autoComplete="off" {...registerOtp("phoneNumber")}
size="small" type="text"
fullWidth inputProps={{
{...register("verification_code")} maxLength: 11,
value={verificationCode} }}
onChange={(e) => setVerificationCode(e.target.value)} />
onInput={handleNumericInput} </FormControl>
type="text" <Button
inputProps={{ disabled={isSubmittingOtp || !isValidOtp || delayPerRequest}
maxLength: 6, variant="contained"
inputMode: "numeric", type="submit"
style: { sx={{ textWrap: "nowrap", width: 200 }}
textAlign: "center", endIcon={<ForwardToInboxIcon />}
letterSpacing: "10px", >
}, {delayPerRequest
}} ? formatCounter(counter)
/> : isSubmittingOtp
</FormControl> ? "درحال دریافت کد ..."
: "دریافت کد"}
</Button>
</Box>
</StyledForm>
<Slide in={otpSended}>
<StyledForm onSubmit={handleSubmitSend(sendData)} id="sendForm">
<Box>
<FormControl size="small" fullWidth variant="outlined">
<InputLabel htmlFor="code">کد تایید پیامک شده</InputLabel>
<OutlinedInput
id="code"
label={"کد تایید پیامک شده"}
placeholder="------"
autoComplete="off"
size="small"
fullWidth
{...registerSend("code")}
type="text"
inputProps={{
maxLength: 6,
inputMode: "numeric",
style: {
textAlign: "center",
letterSpacing: "10px",
},
}}
/>
</FormControl>
</Box>
</StyledForm>
</Slide>
</Box> </Box>
</Slide> </Stack>
</Box> </Box>
</Stack> </DialogContent>
<Button <DialogActions sx={{ alignItems: "center", justifyContent: "center" }}>
variant="contained" <Button
size="large" onClick={handlePrev}
disabled={isSubmitting} variant="outlined"
type={"submit"} color="secondary"
form="patrol_form" size="large"
endIcon={<BeenhereIcon />} startIcon={<ExitToAppIcon />}
> >
{isSubmitting ? "در حال ثبت فعالیت" : "ثبت فعالیت"} {"صفحه قبل"}
</Button> </Button>
</Stack> <Button
variant="contained"
size="large"
type="submit"
form="sendForm"
disabled={isSubmittingSend || !isValidSend}
>
{isSubmittingSend ? "در حال ثبت ..." : "تایید نهایی و ثبت"}
</Button>
</DialogActions>
</>
); );
}; };

View File

@@ -0,0 +1,54 @@
import { IconButton, InputAdornment } from "@mui/material";
import { ClearIcon, MobileDateTimePicker } from "@mui/x-date-pickers";
import moment from "jalali-moment";
import { useWatch } from "react-hook-form";
const DatePickerForEndTime = ({ field, fieldState: { error }, control }) => {
const startTime = useWatch({ control, name: "start_time" });
return (
<MobileDateTimePicker
value={field.value ? new Date(field.value) : null}
ampm={false}
name="تاریخ و ساعت پایان گشت"
closeOnSelect
minDateTime={startTime ? new Date(startTime) : null}
onChange={(end_time) => {
const date = new Date(end_time);
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD HH:mm");
field.onChange(formattedDate);
}}
slotProps={{
textField: {
size: "small",
placeholder: "تاریخ و ساعت وارد کنید",
InputLabelProps: { shrink: true },
sx: { width: { xs: "100%", md: 200 } },
InputProps: {
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
field.onChange("");
}}
sx={{
color: "#bfbfbf",
"&:hover": {
backgroundColor: "rgba(189, 189, 189, 0.1)",
color: "#363434",
},
}}
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
},
}}
label="تاریخ و ساعت پایان گشت"
/>
);
};
export default DatePickerForEndTime;

View File

@@ -0,0 +1,54 @@
import { IconButton, InputAdornment } from "@mui/material";
import { ClearIcon, MobileDateTimePicker } from "@mui/x-date-pickers";
import moment from "jalali-moment";
import { useWatch } from "react-hook-form";
const DatePickerForStartTime = ({ field, fieldState: { error }, control }) => {
const endTime = useWatch({ control, name: "end_time" });
return (
<MobileDateTimePicker
value={field.value ? new Date(field.value) : null}
ampm={false}
name="تاریخ و ساعت شروع گشت"
closeOnSelect
maxDateTime={endTime ? new Date(endTime) : null}
onChange={(start_time) => {
const date = new Date(start_time);
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD HH:mm");
field.onChange(formattedDate);
}}
slotProps={{
textField: {
size: "small",
placeholder: "تاریخ و ساعت وارد کنید",
InputLabelProps: { shrink: true },
sx: { width: { xs: "100%", md: 200 } },
InputProps: {
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
field.onChange("");
}}
sx={{
color: "#bfbfbf",
"&:hover": {
backgroundColor: "rgba(189, 189, 189, 0.1)",
color: "#363434",
},
}}
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
},
}}
label="تاریخ و ساعت شروع گشت"
/>
);
};
export default DatePickerForStartTime;

View File

@@ -48,7 +48,12 @@ const EditActionDuringPatrol = ({ action, index, setActionsList }) => {
}} }}
> >
<DialogTitle>ویرایش اطلاعات</DialogTitle> <DialogTitle>ویرایش اطلاعات</DialogTitle>
<EditFormContent setOpenEditDialog={setOpen} onSubmit={HandleSubmit} defaultData={action.data} /> <EditFormContent
setOpenEditDialog={setOpen}
onSubmit={HandleSubmit}
defaultData={action.data}
is_gasht={true}
/>
</Dialog> </Dialog>
</> </>
); );

View File

@@ -1,202 +1,179 @@
"use client"; "use client";
import { Box, Button, Chip, Divider, IconButton, InputAdornment, Stack } from "@mui/material";
import React, { useEffect, useState } from "react";
import CarCode from "@/core/components/CarCode"; import CarCode from "@/core/components/CarCode";
import StyledForm from "@/core/components/StyledForm";
import { GET_FMS_DATA as GET_VAHICLE_OPRATION } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
import { yupResolver } from "@hookform/resolvers/yup";
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import SearchIcon from "@mui/icons-material/Search"; import SearchIcon from "@mui/icons-material/Search";
import { Box, Button, Chip, DialogActions, DialogContent, Divider, Stack } from "@mui/material";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali"; import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import { faIR } from "@mui/x-date-pickers/locales"; import { faIR } from "@mui/x-date-pickers/locales";
import { LocalizationProvider, MobileDateTimePicker } from "@mui/x-date-pickers";
import ClearIcon from "@mui/icons-material/Clear";
import moment from "jalali-moment"; import moment from "jalali-moment";
import dynamic from "next/dynamic"; import { useState } from "react";
import MapLoading from "@/core/components/MapLayer/Loading"; import { Controller, useForm } from "react-hook-form";
import { Controller } from "react-hook-form"; import * as yup from "yup";
import { GET_FMS_DATA } from "@/core/utils/routes"; import DatePickerForEndTime from "./DatePickerForEndTime";
import useRequest from "@/lib/hooks/useRequest"; import DatePickerForStartTime from "./DatePickerForStartTime";
import PatrolDetailInfo from "./PatrolDetailInfo"; import PatrolDetailInfo from "./PatrolDetailInfo";
import PatrolResultCodeErrors from "./PatrolResultCodeErrors"; import PatrolResultCodeErrors from "./PatrolResultCodeErrors";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { const schema = yup.object().shape({
loading: () => <MapLoading />, roadPatrolMachinesId: yup.object().required("خودرو الزامی است."),
ssr: false, start_time: yup.string().required("زمان شروع الزامی است."),
end_time: yup.string().required("زمان پایان الزامی است."),
}); });
const PatrolDetail = ({ control, watch, setValue, tabState, setTabState }) => { const PatrolDetail = ({ allData, setAllData, handlePrev, setTabState }) => {
const requestServer = useRequest({ notificationSuccess: true }); const defaultValues = {
const [patrolResultStatus, setPatrolResultStatus] = useState(null); has_vehicle_operation: allData.has_vehicle_operation,
const [readyToRequest, setReadyToRequest] = useState(false); roadPatrolMachinesId: allData.roadPatrolMachinesId,
const [patrolData, setPatrolData] = useState(null); start_time: allData.start_time,
end_time: allData.end_time,
stopPoints: allData.stopPoints,
accOnDuration: allData.accOnDuration,
fuelConsumption: allData.fuelConsumption,
mileage: allData.mileage,
};
const requestServer = useRequest();
const [patrolResultStatus, setPatrolResultStatus] = useState(allData.resultCode);
const [patrolData, setPatrolData] = useState(defaultValues);
const requestPatrolInfo = async () => { const {
control,
handleSubmit,
setValue,
formState: { isSubmitting, isValid },
} = useForm({ defaultValues, resolver: yupResolver(schema), mode: "onBlur" });
const requestPatrolInfo = async (data) => {
setPatrolResultStatus(1);
setPatrolData((pd) => ({ ...pd, has_vehicle_operation: false }));
const formData = new FormData(); const formData = new FormData();
formData.append("machineCode", watch("road_patrol_machines_id").machine_code); formData.append("machineCode", data.roadPatrolMachinesId.machine_code);
formData.append("startDT", moment(watch("start_time")).format("YYYY-MM-DDTHH:mm")); formData.append("startDT", moment(data.start_time).format("YYYY-MM-DDTHH:mm"));
formData.append("endDT", moment(watch("end_time")).format("YYYY-MM-DDTHH:mm")); formData.append("endDT", moment(data.end_time).format("YYYY-MM-DDTHH:mm"));
await requestServer(GET_FMS_DATA, "post", { try {
data: formData, const response = await requestServer(GET_VAHICLE_OPRATION, "post", {
}) data: formData,
.then((response) => { });
const data = response.data.data; const result = response.data.data;
setPatrolData({ setPatrolData({
...data, ...result,
roadPatrolMachinesId: watch("road_patrol_machines_id"), roadPatrolMachinesId: data.roadPatrolMachinesId,
start_time: watch("start_time"), start_time: data.start_time,
end_time: watch("end_time"), end_time: data.end_time,
}); has_vehicle_operation: result.resultCode == 0,
setPatrolResultStatus(data.resultCode); });
})
.catch((error) => {}); setValue("stopPoints", result.stopPoints);
setValue("accOnDuration", result.accOnDuration);
setValue("fuelConsumption", result.fuelConsumption);
setValue("mileage", result.mileage);
setValue("has_vehicle_operation", true);
setPatrolResultStatus(result.resultCode);
} catch (error) {}
}; };
useEffect(() => { const handleNext = (data) => {
const roadPatrolMachinesId = watch("road_patrol_machines_id"); setAllData(data);
const startTime = watch("start_time"); setTabState((s) => s + 1);
const endTime = watch("end_time"); };
const isReady = roadPatrolMachinesId !== null && startTime !== "" && endTime !== "";
setReadyToRequest(isReady);
}, [watch("road_patrol_machines_id"), watch("start_time"), watch("end_time")]);
return ( return (
<Box <>
sx={{ <DialogContent dividers sx={{ display: "flex", maxHeight: { xs: "500px", md: "100%" }, overflowY: "auto" }}>
display: "flex", <Box sx={{ flex: 1 }}>
flexDirection: "column", <Box
alignItems: "center", sx={{
justifyContent: "center", display: "flex",
my: 3, flexDirection: "column",
}} mt: 1,
> mb: 2,
<Box sx={{ display: "flex", flexDirection: { xs: "column", lg: "row" }, gap: 2 }}>
<Stack sx={{ minWidth: "200px" }}>
<Controller
control={control}
render={({ field, fieldState: { error } }) => {
return (
<CarCode
carCode={field.value}
setCarCode={(value) => field.onChange(value)}
error={error}
/>
);
}} }}
name={"road_patrol_machines_id"}
/>
</Stack>
<Box sx={{ display: "flex", gap: 2 }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
> >
<MobileDateTimePicker <StyledForm onSubmit={handleSubmit(requestPatrolInfo)}>
value={watch("start_time") ? new Date(watch("start_time")) : null} <Box
ampm={false} sx={{
name="تاریخ و ساعت شروع گشت" display: "flex",
closeOnSelect flexDirection: { xs: "column", md: "row" },
maxDateTime={watch("end_time") ? new Date(watch("end_time")) : null} rowGap: 2,
onChange={(start_time) => { columnGap: 1,
const date = new Date(start_time); }}
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD HH:mm"); >
setValue("start_time", formattedDate); <Stack sx={{ minWidth: "200px" }}>
}} <Controller
slotProps={{ control={control}
textField: { render={({ field, fieldState: { error } }) => {
size: "small", return (
placeholder: "تاریخ و ساعت شروع گشت", <CarCode
InputProps: { carCode={field.value}
endAdornment: ( setCarCode={(value) => field.onChange(value)}
<InputAdornment position="end"> error={error}
<IconButton />
size="small" );
onClick={(event) => { }}
event.stopPropagation(); name={"roadPatrolMachinesId"}
setValue("start_time", ""); />
}} </Stack>
sx={{ <Box sx={{ display: "flex", gap: 1 }}>
color: "#bfbfbf", <LocalizationProvider
"&:hover": { dateAdapter={AdapterDateFnsJalali}
backgroundColor: "rgba(189, 189, 189, 0.1)", localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
color: "#363434", >
}, <Controller
}} control={control}
> render={(props) => <DatePickerForStartTime {...props} control={control} />}
<ClearIcon /> name={"start_time"}
</IconButton> />
</InputAdornment> <Controller
), control={control}
}, render={(props) => <DatePickerForEndTime {...props} control={control} />}
}, name={"end_time"}
}} />
label="تاریخ و ساعت شروع گشت" </LocalizationProvider>
/> </Box>
<MobileDateTimePicker <Button
value={watch("end_time") ? new Date(watch("end_time")) : null} sx={{ flex: 1 }}
ampm={false} variant="contained"
name="تاریخ و ساعت پایان گشت" type="submit"
closeOnSelect disabled={isSubmitting || !isValid}
minDateTime={watch("start_time") ? new Date(watch("start_time")) : null} endIcon={<SearchIcon />}
onChange={(end_time) => { >
const date = new Date(end_time); {isSubmitting ? "درحال دریافت عملکرد خودرو ..." : "دریافت عملکرد خوردو"}
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD HH:mm"); </Button>
setValue("end_time", formattedDate); </Box>
}} </StyledForm>
slotProps={{ <Divider sx={{ my: 2, width: "100%" }}>
textField: { <Chip label="مشخصات گشت" variant="outlined" />
size: "small", </Divider>
placeholder: "تاریخ و ساعت پایان گشت", {patrolResultStatus !== 0 && <PatrolResultCodeErrors ResultCode={patrolResultStatus} />}
InputProps: { <PatrolDetailInfo patrolData={patrolData} />
endAdornment: ( </Box>
<InputAdornment position="end">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
setValue("end_time", "");
}}
sx={{
color: "#bfbfbf",
"&:hover": {
backgroundColor: "rgba(189, 189, 189, 0.1)",
color: "#363434",
},
}}
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
},
}}
label="تاریخ و ساعت پایان گشت"
/>
</LocalizationProvider>
</Box> </Box>
</DialogContent>
<DialogActions sx={{ alignItems: "center", justifyContent: "center" }}>
<Button
onClick={handlePrev}
variant="outlined"
color="secondary"
size="large"
startIcon={<ExitToAppIcon />}
>
{"بستن"}
</Button>
<Button <Button
variant="contained" variant="contained"
onClick={requestPatrolInfo} size="large"
color="success" disabled={!patrolData?.has_vehicle_operation}
disabled={!readyToRequest} onClick={() => handleNext(patrolData)}
endIcon={<SearchIcon />}
> >
درخواست اطلاعات {"تایید مشخصات گشت و ادامه"}
</Button> </Button>
</Box> </DialogActions>
<Divider sx={{ my: 2, width: "100%" }}> </>
<Chip color="success" label="مشخصات گشت" />
</Divider>
{patrolResultStatus === 0 ? (
<PatrolDetailInfo
patrolData={patrolData}
tabState={tabState}
setTabState={setTabState}
setValue={setValue}
/>
) : (
<PatrolResultCodeErrors ResultCode={patrolResultStatus} />
)}
</Box>
); );
}; };

View File

@@ -1,6 +1,18 @@
"use client"; "use client";
import { Box, Button, Card, CardActions, CardContent, Chip, Divider, Slide, Stack, Typography } from "@mui/material"; import {
Box,
Button,
Card,
CardActions,
CardContent,
Chip,
Divider,
Fade,
Slide,
Stack,
Typography,
} from "@mui/material";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import MapLoading from "@/core/components/MapLayer/Loading"; import MapLoading from "@/core/components/MapLayer/Loading";
@@ -16,6 +28,8 @@ import QrCode2Icon from "@mui/icons-material/QrCode2";
import moment from "jalali-moment"; import moment from "jalali-moment";
import PatrolMapFeatures from "./PatrolMapFeatures"; import PatrolMapFeatures from "./PatrolMapFeatures";
import { useMap } from "react-leaflet"; import { useMap } from "react-leaflet";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
import ShowPlak from "@/core/components/ShowPlak";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => <MapLoading />, loading: () => <MapLoading />,
@@ -27,111 +41,109 @@ const MapItemViewBound = ({ patrolData, bound }) => {
useEffect(() => { useEffect(() => {
if (bound.length !== 0) { if (bound.length !== 0) {
map.fitBounds(bound, { paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] }); map.fitBounds(bound, { paddingTopLeft: [64, 64], paddingBottomRight: [64, 64] });
} }
}, [bound]); }, [bound]);
return ( return (
<> <>
{patrolData.stopPoints.map((stopPoint, index) => ( {patrolData?.stopPoints &&
<React.Fragment key={index}> patrolData.stopPoints.map((stopPoint, index) => (
<PatrolMapFeatures stopPoint={stopPoint} /> <React.Fragment key={index}>
</React.Fragment> <PatrolMapFeatures stopPoint={stopPoint} />
))} </React.Fragment>
))}
</> </>
); );
}; };
const PatrolDetailInfo = ({ patrolData, tabState, setTabState, setValue }) => { const PatrolDetailInfo = ({ patrolData }) => {
const bound = patrolData.stopPoints.map((point) => [point.latitude, point.longitude]); const bound = patrolData?.stopPoints
? patrolData?.stopPoints.map((point) => [point.latitude, point.longitude])
const SendPatrolInfo = () => { : [];
setValue("vehicle_runtime", patrolData.accOnDuration);
setValue("fuel_consumption", patrolData.fuelConsumption);
setValue("distance", patrolData.mileage);
setValue("stop_points", patrolData.stopPoints);
setTabState(tabState + 1);
};
return ( return (
<Slide in={true} sx={{ width: "100%" }}> <Fade in={patrolData?.has_vehicle_operation} sx={{ width: "100%" }}>
<Card> <Card elevation={0}>
<CardContent <CardContent
sx={{ sx={{
display: "flex", display: "flex",
flexDirection: { xs: "column", lg: "row" }, flexDirection: { xs: "column", lg: "row" },
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-around",
gap: { xs: 2, lg: 0 }, gap: { xs: 2, lg: 0 },
}} }}
> >
<Stack sx={{ minWidth: { xs: "100%", lg: "300px" }, gap: 2 }}> <Stack sx={{ minWidth: { xs: "100%", md: "350px" }, gap: 2 }}>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="مدت زمان روشن بودن خودرو" icon={<ElectricCarIcon />} /> <Chip size="small" label="مدت زمان روشن بودن خودرو" icon={<ElectricCarIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{Math.floor(patrolData.accOnDuration / 60)} دقیقه {formatSecondsToHHMMSS(patrolData?.accOnDuration)}
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="نام خودرو" icon={<DirectionsCarFilledIcon />} /> <Chip size="small" label="نام خودرو" icon={<DirectionsCarFilledIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{patrolData.roadPatrolMachinesId.car_name} {patrolData?.roadPatrolMachinesId?.car_name}
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="کد خودرو" icon={<QrCode2Icon />} /> <Chip size="small" label="کد خودرو" icon={<QrCode2Icon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{patrolData.roadPatrolMachinesId.machine_code} {patrolData?.roadPatrolMachinesId?.machine_code}
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center" }}>
<Chip size="small" label="پلاک خودرو" icon={<DirectionsCarFilledIcon />} /> <Chip size="small" label="پلاک خودرو" icon={<DirectionsCarFilledIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Box sx={{ fontSize: 11 }}>
{patrolData.roadPatrolMachinesId.plak_number} {patrolData?.roadPatrolMachinesId?.plak_number &&
</Typography> ShowPlak({ plak_number: patrolData?.roadPatrolMachinesId?.plak_number })}
</Box>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="زمان شروع گشت" icon={<QueryBuilderIcon />} /> <Chip size="small" label="زمان شروع گشت" icon={<QueryBuilderIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{moment(patrolData.start_time).locale("fa").format("HH:mm | YYYY/MM/DD")} {patrolData?.start_time !== "" &&
moment(patrolData?.start_time).locale("fa").format("HH:mm | YYYY/MM/DD")}
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="زمان پایان گشت" icon={<WatchLaterIcon />} /> <Chip size="small" label="زمان پایان گشت" icon={<WatchLaterIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{moment(patrolData.end_time).locale("fa").format("HH:mm | YYYY/MM/DD")} {patrolData?.end_time !== "" &&
moment(patrolData?.end_time).locale("fa").format("HH:mm | YYYY/MM/DD")}
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="مسافت" icon={<AddRoadIcon />} /> <Chip size="small" label="مسافت پیموده شده" icon={<AddRoadIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{(patrolData.mileage / 1000).toFixed(1)} کیلومتر {(patrolData?.mileage / 1000).toFixed(1)} کیلومتر
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="میزان مصرف سوخت" icon={<LocalGasStationIcon />} /> <Chip size="small" label="میزان سوخت مصرفی" icon={<LocalGasStationIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{Math.round(patrolData.fuelConsumption * 10) / 10} لیتر {Math.round(patrolData?.fuelConsumption * 10) / 10} لیتر
</Typography> </Typography>
</Box> </Box>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}> <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Chip size="small" label="تعداد توقف در مسیر" icon={<ShareLocationIcon />} /> <Chip size="small" label="تعداد توقف در مسیر" icon={<ShareLocationIcon />} />
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography sx={{ fontSize: "13px", fontWeight: 400 }}> <Typography sx={{ fontSize: "13px", fontWeight: 400 }}>
{patrolData.stopPoints.length} مورد {patrolData?.stopPoints?.length} مورد
</Typography> </Typography>
</Box> </Box>
</Stack> </Stack>
<Divider orientation="vertical" flexItem /> <Divider orientation="vertical" flexItem />
<Stack spacing={1} sx={{ height: "300px", width: { xs: "100%", lg: "350px" } }}> <Stack spacing={1} sx={{ height: "300px", width: { xs: "100%", md: "350px" } }}>
<Box <Box
sx={{ sx={{
p: 1, p: 1,
@@ -149,28 +161,17 @@ const PatrolDetailInfo = ({ patrolData, tabState, setTabState, setValue }) => {
justifyContent: "center", justifyContent: "center",
}} }}
> >
<MapLayer position={bound}> {bound.length > 0 && (
<MapItemViewBound patrolData={patrolData} bound={bound} /> <MapLayer position={bound}>
</MapLayer> <MapItemViewBound patrolData={patrolData} bound={bound} />
</MapLayer>
)}
</Box> </Box>
</Box> </Box>
</Stack> </Stack>
</CardContent> </CardContent>
<CardActions sx={{ alignItems: "center", justifyContent: "center" }}>
<Box sx={{ display: "flex", gap: 1 }}>
<Button
variant="contained"
color="primary"
onClick={SendPatrolInfo}
endIcon={<KeyboardDoubleArrowLeftIcon />}
sx={{ mt: 2 }}
>
تایید اطلاعات و رفتن به مرحله بعد
</Button>
</Box>
</CardActions>
</Card> </Card>
</Slide> </Fade>
); );
}; };

View File

@@ -1,20 +1,17 @@
"use client"; "use client";
import { Box, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material";
import { useTheme } from "@emotion/react"; import { useTheme } from "@emotion/react";
import React, { useEffect, useState } from "react";
import TaxiAlertIcon from "@mui/icons-material/TaxiAlert";
import EngineeringIcon from "@mui/icons-material/Engineering"; import EngineeringIcon from "@mui/icons-material/Engineering";
import HandymanIcon from "@mui/icons-material/Handyman"; import HandymanIcon from "@mui/icons-material/Handyman";
import TaxiAlertIcon from "@mui/icons-material/TaxiAlert";
import VerifiedIcon from "@mui/icons-material/Verified"; import VerifiedIcon from "@mui/icons-material/Verified";
import PatrolTimeLine from "./PatrolTimeLine"; import { Box, Tab, Tabs, useMediaQuery } from "@mui/material";
import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog";
import { useCallback, useEffect, useReducer, useState } from "react";
import ActionsDuringPatrol from "./ActionsDurigPatrol";
import CompeleteRequest from "./CompeleteRequest";
import PatrolDetail from "./PatrolDetail"; import PatrolDetail from "./PatrolDetail";
import SuperVisorsDetail from "./SuperVisorsDetail"; import SuperVisorsDetail from "./SuperVisorsDetail";
import ActionsDuringPatrol from "./ActionsDurigPatrol";
import { useForm } from "react-hook-form";
import StyledForm from "@/core/components/StyledForm";
import useRequest from "@/lib/hooks/useRequest";
import CompeleteRequest from "./CompeleteRequest";
function TabPanel(props) { function TabPanel(props) {
const { children, value, index } = props; const { children, value, index } = props;
@@ -27,21 +24,32 @@ function TabPanel(props) {
} }
const defaultValues = { const defaultValues = {
road_patrol_rahdaran_id: null, has_vehicle_operation: false,
road_patrol_machines_id: null, road_patrol_rahdaran_id: [],
roadPatrolMachinesId: null,
start_time: "", start_time: "",
end_time: "", end_time: "",
stop_points: null, stopPoints: null,
vehicle_runtime: "", accOnDuration: "",
fuel_consumption: "", fuelConsumption: "",
distance: "", mileage: "",
observed_items: null, observed_items: [],
phone_number: "", phone_number: "",
otp_token: "", otp_token: "",
resultCode: null,
};
const reducer = (state, action) => {
switch (action.type) {
case "changeData":
return { ...state, ...action.data };
default:
return state;
}
}; };
const PatrolForms = ({ mutate, setOpen }) => { const PatrolForms = ({ mutate, setOpen }) => {
const requestServer = useRequest({ notificationSuccess: true }); const [allData, dispatch] = useReducer(reducer, defaultValues);
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("md")); const isMobile = useMediaQuery(theme.breakpoints.down("md"));
const [tabState, setTabState] = useState(0); const [tabState, setTabState] = useState(0);
@@ -57,123 +65,68 @@ const PatrolForms = ({ mutate, setOpen }) => {
} }
}, [tabState]); }, [tabState]);
const { const handlePrev = useCallback(() => {
control, if (tabState == 0) {
watch, setOpen(false);
getValues, }
register, setTabState((s) => s - 1);
handleSubmit, }, [tabState]);
setValue,
resetField,
formState: { isSubmitting },
} = useForm({
defaultValues,
mode: "onBlur",
});
const onSubmit = async (data) => {
console.log("data", data);
// const formData = new FormData();
// data.road_patrol_rahdaran_id.forEach((rahdar, index) => formData.append(`road_patrol_rahdaran_id[${index}]`, rahdar.id));
// formData.append(`road_patrol_machines_id[0]`, data.road_patrol_machines_id.id)
// data.stop_points.forEach((stop_point, index) => formData.append(`stop_points[${index}]`, stop_point))
// data.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.start_point);
// formData.append(`observed_items[${index}][start_point]`, observed_item.end_point);
// formData.append(`observed_items[${index}][amount]`, observed_item.amount);
// formData.append(`observed_items[${index}][before_image]`, observed_item.before_image);
// formData.append(`observed_items[${index}][after_image]`, observed_item.after_image);
// observed_item.cmms_machines.forEach((machine) => formData.append(`observed_items[${index}][cmms_machines][]`, machine.id))
// observed_item.rahdaran_id.forEach((rahdar) => formData.append(`observed_items[${index}][rahdaran_id][]`, rahdar.id))
// formData.append(`observed_items[${index}][sub_item_id]`, observed_item.sub_item_id);
// })
// formData.append("start_time", data.start_time);
// formData.append("end_time", data.end_time);
// formData.append("vehicle_runtime", data.vehicle_runtime);
// formData.append("fuel_consumption", +data.fuel_consumption);
// formData.append("distance", data.distance);
// formData.append("description", "");
// formData.append("phone_number", data.phone_number);
// formData.append("verification_code", data.verification_code);
// if (data.phone_number !== "" && data.verification_code !== "") {
// try {
// await requestServer(CREATE_PATROL, "post", {
// data: formData,
// });
// mutate();
// setOpen(false);
// } catch (error) {
// }
// } else {
// console.log("error phone number")
// }
};
return ( return (
<StyledForm onSubmit={handleSubmit(onSubmit)} id="patrol_form"> <>
<Tabs <DialogHeader>
allowScrollButtonsMobile <Tabs
value={tabState} allowScrollButtonsMobile
onChange={handleChangeTab} value={tabState}
variant={`${isMobile ? "scrollable" : "fullWidth"}`} onChange={handleChangeTab}
sx={{ variant={`${isMobile ? "scrollable" : "fullWidth"}`}
display: "flex", sx={{
justifyContent: "space-around", display: "flex",
width: "100%", justifyContent: "space-around",
backgroundColor: "#efefef", width: "100%",
}} backgroundColor: "#efefef",
> }}
<Tab disabled={!(activeUpTo >= 0)} icon={<TaxiAlertIcon />} label="مشخصات گشت"></Tab> >
<Tab disabled={!(activeUpTo >= 1)} icon={<EngineeringIcon />} label="مشخصات راهداران"></Tab> <Tab disabled={!(activeUpTo >= 0)} icon={<TaxiAlertIcon />} label="مشخصات گشت"></Tab>
<Tab disabled={!(activeUpTo >= 2)} icon={<HandymanIcon />} label="اقدامات حین گشت"></Tab> <Tab disabled={!(activeUpTo >= 1)} icon={<EngineeringIcon />} label="مشخصات راهداران"></Tab>
<Tab disabled={!(activeUpTo >= 3)} icon={<VerifiedIcon />} label="تکمیل درخواست"></Tab> <Tab disabled={!(activeUpTo >= 2)} icon={<HandymanIcon />} label="اقدامات حین گشت"></Tab>
</Tabs> <Tab disabled={!(activeUpTo >= 3)} icon={<VerifiedIcon />} label="بررسی نهایی و ثبت"></Tab>
<DialogContent dividers sx={{ display: "flex" }}> </Tabs>
<Box sx={{ flex: 1 }}> </DialogHeader>
<TabPanel value={tabState} index={0}> <TabPanel value={tabState} index={0}>
<PatrolDetail <PatrolDetail
control={control} allData={allData}
setValue={setValue} setAllData={(data) => {
watch={watch} dispatch({ type: "changeData", data });
tabState={tabState} }}
setTabState={setTabState} handlePrev={handlePrev}
/> setTabState={setTabState}
</TabPanel> />
<TabPanel value={tabState} index={1}> </TabPanel>
<SuperVisorsDetail <TabPanel value={tabState} index={1}>
control={control} <SuperVisorsDetail
setValue={setValue} allData={allData}
watch={watch} setAllData={(data) => {
tabState={tabState} dispatch({ type: "changeData", data });
setTabState={setTabState} }}
/> handlePrev={handlePrev}
</TabPanel> setTabState={setTabState}
<TabPanel value={tabState} index={2}> />
<ActionsDuringPatrol </TabPanel>
watch={watch} <TabPanel value={tabState} index={2}>
setValue={setValue} <ActionsDuringPatrol
tabState={tabState} allData={allData}
setTabState={setTabState} setAllData={(data) => {
/> dispatch({ type: "changeData", data });
</TabPanel> }}
<TabPanel value={tabState} index={3}> handlePrev={handlePrev}
<CompeleteRequest setTabState={setTabState}
register={register} />
setValue={setValue} </TabPanel>
watch={watch} <TabPanel value={tabState} index={3}>
isSubmitting={isSubmitting} <CompeleteRequest allData={allData} handlePrev={handlePrev} mutate={mutate} setOpen={setOpen} />
/> </TabPanel>
</TabPanel> </>
</Box>
{!isMobile && (
<Box sx={{ display: "flex", alignItems: "center" }}>
<PatrolTimeLine tabState={tabState} />
</Box>
)}
</DialogContent>
</StyledForm>
); );
}; };
export default PatrolForms; export default PatrolForms;

View File

@@ -5,6 +5,7 @@ import React from "react";
const PatrolResultCodeErrors = ({ ResultCode }) => { const PatrolResultCodeErrors = ({ ResultCode }) => {
const errorMessages = { const errorMessages = {
[1]: "درحال دریافت عملکرد خودرو ...",
[-1]: "نام کاربری یا کلمه عبور صحیح نمیباشد", [-1]: "نام کاربری یا کلمه عبور صحیح نمیباشد",
[-2]: "ردیاب به خودرو انتخابی متصل نیست", [-2]: "ردیاب به خودرو انتخابی متصل نیست",
[-3]: "کاربر دسترسی لازم به اطلاعات خودرو انتخابی را ندارد", [-3]: "کاربر دسترسی لازم به اطلاعات خودرو انتخابی را ندارد",
@@ -14,8 +15,11 @@ const PatrolResultCodeErrors = ({ ResultCode }) => {
const message = errorMessages[ResultCode] || "ابتدا اطلاعات بالا را تکمیل نمایید"; const message = errorMessages[ResultCode] || "ابتدا اطلاعات بالا را تکمیل نمایید";
return ( return (
<Box sx={{ my: 5 }}> <Box sx={{ my: 5, textAlign: "center" }}>
<Typography variant="h6" sx={{ letterSpacing: "2px", color: ResultCode ? "error.main" : "#606060" }}> <Typography
variant="h6"
sx={{ letterSpacing: "2px", color: ResultCode && ResultCode != 1 ? "error.main" : "#606060" }}
>
{message} {message}
</Typography> </Typography>
</Box> </Box>

View File

@@ -8,11 +8,13 @@ import React from "react";
const SuperVisorInfo = ({ superVisor, deleteSuperVisor }) => { const SuperVisorInfo = ({ superVisor, deleteSuperVisor }) => {
return ( return (
<Card <Card
elevation={3} elevation={0}
sx={{ sx={{
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
border: 1,
borderColor: "divider",
px: 1, px: 1,
py: 1, py: 1,
}} }}

View File

@@ -1,138 +1,144 @@
"use client"; "use client";
import { Box, Button, Chip, Divider, Grid, Stack, Typography } from "@mui/material";
import SaveAltIcon from "@mui/icons-material/SaveAlt";
import React, { useEffect, useState } from "react";
import RahdarCode from "@/core/components/RahdarCode";
import SuperVisorInfo from "@/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/SuperVisorInfo"; import SuperVisorInfo from "@/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/SuperVisorInfo";
import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; import RahdarCode from "@/core/components/RahdarCode";
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; import StyledForm from "@/core/components/StyledForm";
import { Controller } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup";
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import { Box, Button, Chip, DialogActions, DialogContent, Divider, Fade, Grid, Stack, Typography } from "@mui/material";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import * as yup from "yup";
const SuperVisorsDetail = ({ control, watch, setValue, tabState, setTabState }) => { const schema = yup.object().shape({
const [SuperVisorList, setSuperVisorList] = useState([]); road_patrol_rahdaran_id: yup.object().required("راهدار الزامی است."),
const [readyToRequest, setReadyToRequest] = useState(false); });
const requestPatrolInfo = () => { const SuperVisorsDetail = ({ allData, setAllData, handlePrev, setTabState }) => {
setSuperVisorList((prev) => [...prev, watch("road_patrol_rahdaran_id")]); const [SuperVisorList, setSuperVisorList] = useState(allData.road_patrol_rahdaran_id);
setReadyToRequest(false);
setValue("road_patrol_rahdaran_id", null); const defaultValues = {
road_patrol_rahdaran_id: null,
};
const {
control,
handleSubmit,
reset,
setValue,
formState: { isSubmitting, isValid },
} = useForm({ defaultValues, resolver: yupResolver(schema), mode: "onBlur" });
const requestPatrolInfo = (data) => {
setSuperVisorList((prev) => [...prev, data.road_patrol_rahdaran_id]);
reset();
}; };
const deleteSuperVisor = (id) => { const deleteSuperVisor = (id) => {
setSuperVisorList((prev) => prev.filter((superVisor) => superVisor.id !== id)); setSuperVisorList((prev) => prev.filter((superVisor) => superVisor.id !== id));
}; };
useEffect(() => { const handleNext = (data) => {
setReadyToRequest(watch("road_patrol_rahdaran_id") != null); setAllData({ road_patrol_rahdaran_id: data });
}, [watch("road_patrol_rahdaran_id")]); setTabState((s) => s + 1);
const SendSuperVisor = () => {
setValue("road_patrol_rahdaran_id", SuperVisorList);
setTabState(tabState + 1);
}; };
return ( return (
<Box <>
sx={{ <DialogContent dividers sx={{ display: "flex" }}>
display: "flex", <Box sx={{ flex: 1 }}>
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
my: 3,
}}
>
<Grid container sx={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
<Grid
item
xs={6}
sx={{ display: "flex", alignItems: "center", flexDirection: { xs: "column", lg: "row" }, gap: 2 }}
>
<Stack sx={{ minWidth: "250px" }}>
<Controller
control={control}
render={({ field, fieldState: { error } }) => {
return (
<RahdarCode
rahdarsCode={field.value}
setRahdarsCode={(value) => field.onChange(value)}
error={error}
multiple={false}
/>
);
}}
name={"road_patrol_rahdaran_id"}
/>
</Stack>
<Button
variant="contained"
onClick={requestPatrolInfo}
color="success"
sx={{ textWrap: "nowrap", px: 3 }}
disabled={!readyToRequest}
endIcon={<SaveAltIcon />}
>
ثبت راهدار
</Button>
</Grid>
</Grid>
<Divider sx={{ my: 2, width: "100%" }}>
<Chip color="success" variant="outlined" label="لیست راهداران انتخاب شده" />
</Divider>
{SuperVisorList.length !== 0 ? (
<>
<Grid
container
spacing={2}
sx={{
p: 1,
mt: 1,
maxHeight: "300px",
overflowY: "auto",
}}
>
{SuperVisorList.map((superVisor) => (
<Grid key={superVisor.id} item xs={12} sm={6} lg={4}>
<SuperVisorInfo superVisor={superVisor} deleteSuperVisor={deleteSuperVisor} />
</Grid>
))}
</Grid>
<Box <Box
sx={{ sx={{
display: "flex", display: "flex",
gap: 1, flexDirection: "column",
mt: 2, mt: 1,
width: "100%", mb: 2,
justifyContent: "center",
}} }}
> >
<Button <StyledForm onSubmit={handleSubmit(requestPatrolInfo)}>
variant="outlined" <Stack direction={{ xs: "column", sm: "row" }} justifyContent={"center"} spacing={2}>
color="error" <Stack sx={{ minWidth: "250px" }}>
startIcon={<KeyboardDoubleArrowRightIcon />} <Controller
sx={{ mt: 2 }} control={control}
> render={({ field, fieldState: { error } }) => {
بازگشت return (
</Button> <RahdarCode
<Button rahdarsCode={field.value}
variant="contained" setRahdarsCode={(value) => field.onChange(value)}
color="primary" error={error}
onClick={SendSuperVisor} multiple={false}
endIcon={<KeyboardDoubleArrowLeftIcon />} />
sx={{ mt: 2 }} );
> }}
تایید اطلاعات و رفتن به مرحله بعد name={"road_patrol_rahdaran_id"}
</Button> />
</Stack>
<Button
variant="contained"
type="submit"
sx={{ textWrap: "nowrap", px: 3 }}
disabled={isSubmitting || !isValid}
>
افزودن راهدار
</Button>
</Stack>
</StyledForm>
<Divider sx={{ my: 2, width: "100%" }}>
<Chip variant="outlined" label="لیست راهداران" />
</Divider>
{SuperVisorList.length === 0 && (
<Box sx={{ my: 4, width: "100%", textAlign: "center" }}>
<Typography variant="h6" sx={{ letterSpacing: "2px", color: "#606060" }}>
ابتدا اطلاعات بالا را تکمیل نمایید
</Typography>
</Box>
)}
<Fade in={SuperVisorList.length !== 0} sx={{ width: "100%" }}>
<Grid
container
spacing={2}
sx={{
p: 1,
mt: 2,
minHeight: 150,
maxHeight: 300,
overflowY: "auto",
}}
>
{SuperVisorList.length !== 0 &&
SuperVisorList.map((superVisor) => (
<Grid key={superVisor.id} item xs={12} sm={6} lg={4}>
<SuperVisorInfo
superVisor={superVisor}
deleteSuperVisor={deleteSuperVisor}
/>
</Grid>
))}
</Grid>
</Fade>
</Box> </Box>
</>
) : (
<Box sx={{ my: 5, width: "100%", textAlign: "center" }}>
<Typography variant="h6" sx={{ letterSpacing: "2px", color: "#606060" }}>
ابتدا اطلاعات بالا را تکمیل نمایید
</Typography>
</Box> </Box>
)} </DialogContent>
</Box> <DialogActions sx={{ alignItems: "center", justifyContent: "center" }}>
<Button
onClick={handlePrev}
variant="outlined"
color="secondary"
size="large"
startIcon={<ExitToAppIcon />}
>
{"صفحه قبل"}
</Button>
<Button
variant="contained"
size="large"
disabled={SuperVisorList.length === 0}
onClick={() => handleNext(SuperVisorList)}
>
{"تایید راهداران و ادامه"}
</Button>
</DialogActions>
</>
); );
}; };

View File

@@ -5,8 +5,8 @@ import PatrolForms from "./PatrolForms";
const CreatePatrol = ({ open, setOpen, mutate }) => { const CreatePatrol = ({ open, setOpen, mutate }) => {
return ( return (
<Dialog open={open} fullWidth maxWidth="lg"> <Dialog open={open} fullWidth maxWidth="md">
<PatrolForms setOpen={setOpen} mutate={mutate} /> {open && <PatrolForms setOpen={setOpen} mutate={mutate} />}
</Dialog> </Dialog>
); );
}; };

View File

@@ -43,10 +43,10 @@ const OperatorList = () => {
}, },
}, },
}, },
Cell: ({ renderedCellValue }) => { Cell: ({ renderedCellValue, row }) => {
return ( return (
<Stack alignItems={"center"} justifyContent={"center"}> <Stack alignItems={"center"} justifyContent={"center"}>
<MachinePerformanceForm /> <MachinePerformanceForm row={row} />
</Stack> </Stack>
); );
}, },

View File

@@ -3,6 +3,7 @@ import AccessAlarmsIcon from "@mui/icons-material/AccessAlarms";
import PinDropIcon from "@mui/icons-material/PinDrop"; import PinDropIcon from "@mui/icons-material/PinDrop";
import LocalGasStationIcon from "@mui/icons-material/LocalGasStation"; import LocalGasStationIcon from "@mui/icons-material/LocalGasStation";
import DirectionsCarIcon from "@mui/icons-material/DirectionsCar"; import DirectionsCarIcon from "@mui/icons-material/DirectionsCar";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
const StopsInfo = ({ selectedPoint, staticData }) => { const StopsInfo = ({ selectedPoint, staticData }) => {
return ( return (
@@ -13,7 +14,7 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
icon={<DirectionsCarIcon />} icon={<DirectionsCarIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
کد ماشین کد خودرو
</Typography> </Typography>
} }
/> />
@@ -29,14 +30,14 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
icon={<LocalGasStationIcon />} icon={<LocalGasStationIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
مصرف سوخت میزان سوخت مصرفی
</Typography> </Typography>
} }
/> />
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{staticData.fuelConsumption || ""} لیتر {Math.round(patrolData?.fuelConsumption * 10) / 10} لیتر
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}> <Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
@@ -45,14 +46,14 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
icon={<DirectionsCarIcon />} icon={<DirectionsCarIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
مسافت طیشده مسافت پیموده شده
</Typography> </Typography>
} }
/> />
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{staticData.mileage || ""} کیلومتر {(patrolData?.mileage / 1000).toFixed(1)} کیلومتر
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}> <Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
@@ -68,7 +69,7 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{(staticData.accOnDuration / 60)?.toFixed(2) || ""} دقیقه {formatSecondsToHHMMSS(patrolData?.accOnDuration)}
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}> <Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>

View File

@@ -3,34 +3,15 @@ import DirectionsCarIcon from "@mui/icons-material/DirectionsCar";
import MachinePerformanceContent from "./MachinePerformanceContent"; import MachinePerformanceContent from "./MachinePerformanceContent";
import { useState } from "react"; import { useState } from "react";
const MachinePerformanceForm = () => { const MachinePerformanceForm = ({ row }) => {
const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false); const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false);
const machineData = { const machineData = {
cmms_machine_code: 245, cmms_machine_code: row.original?.cmms_machines[0]?.machine_code,
resultCode: 0, resultCode: 0,
mileage: 17645, mileage: row.original?.distance,
accOnDuration: 3599, accOnDuration: row.original?.vehicle_runtime,
fuelConsumption: 6.1757503, fuelConsumption: row.original?.fuel_consumption,
stopPoints: [ stopPoints: row.original?.stop_points,
{
startDT: "2024-12-23T13:55:40",
duration: 114,
latitude: 37.521317,
longitude: 46.103584,
},
{
startDT: "2024-12-23T14:31:11",
duration: 14,
latitude: 37.566532,
longitude: 46.17688,
},
{
startDT: "2024-12-23T14:35:36",
duration: 55,
latitude: 37.569958,
longitude: 46.18166,
},
],
}; };
return ( return (
<> <>

View File

@@ -1,14 +1,14 @@
import { Tooltip, IconButton } from "@mui/material"; import { GET_ROAD_PATROL_OPERATOR_REPORT } from "@/core/utils/routes";
import PrintIcon from "@mui/icons-material/Print"; import PrintIcon from "@mui/icons-material/Print";
import { IconButton, Tooltip } from "@mui/material";
const ReportForm = ({ rowId }) => { const ReportForm = ({ rowId }) => {
const reportUrl = `https://rms.rmto.ir/v2/road_patrols/operator/report/${rowId}`;
return ( return (
<Tooltip title="گزارش" arrow placement="right"> <Tooltip title="گزارش" arrow placement="right">
<IconButton <IconButton
color="primary" color="primary"
component="a" component="a"
href={reportUrl} href={`${GET_ROAD_PATROL_OPERATOR_REPORT}/${rowId}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
sx={{ textTransform: "unset", alignSelf: "center" }} sx={{ textTransform: "unset", alignSelf: "center" }}

View File

@@ -3,6 +3,8 @@ import AccessAlarmsIcon from "@mui/icons-material/AccessAlarms";
import PinDropIcon from "@mui/icons-material/PinDrop"; import PinDropIcon from "@mui/icons-material/PinDrop";
import LocalGasStationIcon from "@mui/icons-material/LocalGasStation"; import LocalGasStationIcon from "@mui/icons-material/LocalGasStation";
import DirectionsCarIcon from "@mui/icons-material/DirectionsCar"; import DirectionsCarIcon from "@mui/icons-material/DirectionsCar";
import { formatSecondsToHHMMSS } from "@/core/utils/formatSecondsToHHMMSS";
const StopsInfo = ({ selectedPoint, staticData }) => { const StopsInfo = ({ selectedPoint, staticData }) => {
return ( return (
<Grid container spacing={3} direction="row" sx={{ justifyContent: "space-between", alignItems: "center" }}> <Grid container spacing={3} direction="row" sx={{ justifyContent: "space-between", alignItems: "center" }}>
@@ -12,7 +14,7 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
icon={<DirectionsCarIcon />} icon={<DirectionsCarIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
کد ماشین کد خودرو
</Typography> </Typography>
} }
/> />
@@ -28,14 +30,14 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
icon={<LocalGasStationIcon />} icon={<LocalGasStationIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
مصرف سوخت میزان سوخت مصرفی
</Typography> </Typography>
} }
/> />
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{staticData.fuelConsumption || ""} لیتر {Math.round(staticData?.fuelConsumption * 10) / 10} لیتر
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}> <Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
@@ -44,14 +46,14 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
icon={<DirectionsCarIcon />} icon={<DirectionsCarIcon />}
label={ label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
مسافت طیشده مسافت پیموده شده
</Typography> </Typography>
} }
/> />
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{staticData.mileage || ""} کیلومتر {(staticData?.mileage / 1000).toFixed(1)} کیلومتر
</Typography> </Typography>
</Grid> </Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}> <Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
@@ -67,62 +69,59 @@ const StopsInfo = ({ selectedPoint, staticData }) => {
</Box> </Box>
<Divider sx={{ flex: 1, mx: 2 }} /> <Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}> <Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{(staticData.accOnDuration / 60)?.toFixed(2) || ""} دقیقه {formatSecondsToHHMMSS(staticData?.accOnDuration)}
</Typography>
</Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<PinDropIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
طول جغرافیایی
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{selectedPoint ? selectedPoint.longitude : "نقطه مورد نظر را انتخاب کنید"}
</Typography>
</Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<PinDropIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
عرض جغرافیایی
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{selectedPoint ? selectedPoint.latitude : "نقطه مورد نظر را انتخاب کنید"}
</Typography>
</Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<AccessAlarmsIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
مدت زمان توقف
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{selectedPoint
? `${formatSecondsToHHMMSS(selectedPoint.duration)} ثانیه`
: "نقطه مورد نظر را انتخاب کنید"}
</Typography> </Typography>
</Grid> </Grid>
{selectedPoint && (
<>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<PinDropIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
طول جغرافیایی
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{selectedPoint.longitude || ""}
</Typography>
</Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<PinDropIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
عرض جغرافیایی
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{selectedPoint.latitude || ""}
</Typography>
</Grid>
<Grid item xs={12} sm={12} sx={{ display: "flex", alignItems: "center" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Chip
icon={<AccessAlarmsIcon />}
label={
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.700" }}>
مدت زمان توقف
</Typography>
}
/>
</Box>
<Divider sx={{ flex: 1, mx: 2 }} />
<Typography variant="body2" sx={{ fontWeight: 600, color: "grey.800" }}>
{selectedPoint.duration || ""} ثانیه
</Typography>
</Grid>
</>
)}
</Grid> </Grid>
); );
}; };

View File

@@ -3,34 +3,15 @@ import DirectionsCarIcon from "@mui/icons-material/DirectionsCar";
import MachinePerformanceContent from "./MachinePerformanceContent"; import MachinePerformanceContent from "./MachinePerformanceContent";
import { useState } from "react"; import { useState } from "react";
const MachinePerformanceForm = () => { const MachinePerformanceForm = ({ row }) => {
const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false); const [openMachinesCodeDialog, setOpenMachinesCodeDialog] = useState(false);
const machineData = { const machineData = {
cmms_machine_code: 245, cmms_machine_code: row.original?.cmms_machines[0]?.machine_code,
resultCode: 0, resultCode: 0,
mileage: 17645, mileage: row.original?.distance,
accOnDuration: 3599, accOnDuration: row.original?.vehicle_runtime,
fuelConsumption: 6.1757503, fuelConsumption: row.original?.fuel_consumption,
stopPoints: [ stopPoints: row.original?.stop_points,
{
startDT: "2024-12-23T13:55:40",
duration: 114,
latitude: 37.521317,
longitude: 46.103584,
},
{
startDT: "2024-12-23T14:31:11",
duration: 14,
latitude: 37.566532,
longitude: 46.17688,
},
{
startDT: "2024-12-23T14:35:36",
duration: 55,
latitude: 37.569958,
longitude: 46.18166,
},
],
}; };
return ( return (
<> <>

View File

@@ -1,14 +1,14 @@
import { Tooltip, IconButton } from "@mui/material"; import { GET_ROAD_PATROL_SUPERVISOR_REPORT } from "@/core/utils/routes";
import PrintIcon from "@mui/icons-material/Print"; import PrintIcon from "@mui/icons-material/Print";
import { IconButton, Tooltip } from "@mui/material";
const ReportForm = ({ rowId }) => { const ReportForm = ({ rowId }) => {
const reportUrl = `https://rms.rmto.ir/v2/road_patrols/operator/report/${rowId}`;
return ( return (
<Tooltip title="گزارش" arrow placement="right"> <Tooltip title="گزارش" arrow placement="right">
<IconButton <IconButton
color="primary" color="primary"
component="a" component="a"
href={reportUrl} href={`${GET_ROAD_PATROL_SUPERVISOR_REPORT}/${rowId}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
sx={{ textTransform: "unset", alignSelf: "center" }} sx={{ textTransform: "unset", alignSelf: "center" }}

View File

@@ -147,9 +147,9 @@ const SupervisorList = () => {
}, },
}, },
}, },
Cell: () => ( Cell: ({ row }) => (
<Stack alignItems={"center"} justifyContent={"center"}> <Stack alignItems={"center"} justifyContent={"center"}>
<MachinePerformanceForm /> <MachinePerformanceForm row={row} />
</Stack> </Stack>
), ),
}, },

View File

@@ -10,36 +10,37 @@ const CarCode = ({ carCode, setCarCode, inputValueDefault = "", error, multiple
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const requestServer = useRequest(); const requestServer = useRequest();
const fetchCarCodes = (inputValue, controller) => {
const debouncer = debounce((query) => {
if (!query || query?.length < 3) {
setOptions([]);
return;
}
setLoading(true);
requestServer(`${GET_CAR_LIST_SEARCH}?machine_code=${query}`, "get", {
requestOptions: { signal: controller.signal },
})
.then((response) => {
const combinedArray = carCode ? [...carCode, ...response.data.data] : [...response.data.data];
const uniqueArray = Array.from(new Map(combinedArray.map((item) => [item.id, item])).values());
setOptions(uniqueArray || []);
})
.catch(() => {
setOptions([]);
})
.finally(() => {
setLoading(false);
});
}, 500);
debouncer(inputValue);
};
useEffect(() => { useEffect(() => {
const fetchCarCodes = (inputValue, controller) => {
const debouncer = debounce((query) => {
if (!query || query?.length < 3) {
setOptions([]);
return;
}
setLoading(true);
requestServer(`${GET_CAR_LIST_SEARCH}?machine_code=${query}`, "get", {
requestOptions: { signal: controller.signal },
})
.then((response) => {
const combinedArray = carCode ? [...carCode, ...response.data.data] : [...response.data.data];
const uniqueArray = Array.from(new Map(combinedArray.map((item) => [item.id, item])).values());
setOptions(uniqueArray || []);
})
.catch(() => {
setOptions([]);
})
.finally(() => {
setLoading(false);
});
}, 500);
debouncer(inputValue);
};
const controller = new AbortController(); const controller = new AbortController();
fetchCarCodes(inputValue, controller); fetchCarCodes(inputValue, controller);
return () => controller.abort(); return () => controller.abort();
}, [inputValue]); }, [inputValue]);
return ( return (
<Autocomplete <Autocomplete
multiple={multiple} multiple={multiple}
@@ -50,7 +51,7 @@ const CarCode = ({ carCode, setCarCode, inputValueDefault = "", error, multiple
}} }}
inputValue={inputValue} inputValue={inputValue}
onInputChange={(event, newInputValue) => { onInputChange={(event, newInputValue) => {
setInputValue(newInputValue || ""); // Prevent inputValue from being null or undefined setInputValue(newInputValue || "");
}} }}
options={options} options={options}
getOptionLabel={(option) => { getOptionLabel={(option) => {

View File

@@ -61,7 +61,8 @@ const DataTable_Main = (props) => {
} }
params.set("sorting", JSON.stringify(sortData)); params.set("sorting", JSON.stringify(sortData));
return `${table_url}?${params}`; return `${table_url}?${params}`;
}, [table_url, filterData, pagination, columns, sortData, specialFilter]); }, [table_url, filterData, pagination, sortData, specialFilter]);
const fetcher = async (url) => { const fetcher = async (url) => {
try { try {
const response = await request(url); const response = await request(url);

View File

@@ -84,16 +84,12 @@ function FilterBody({ columns, drawerState, setDrawerState }) {
}; };
return ( return (
<Drawer <>
open={drawerState}
onClose={() => setDrawerState(false)}
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%", zIndex: "1300" }}
>
<FilterHeader setDrawerState={setDrawerState} /> <FilterHeader setDrawerState={setDrawerState} />
{Object.keys(filterData).length > 0 && ( {Object.keys(filterData).length > 0 && (
<ScrollBox> <ScrollBox>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<Box sx={{ px: 1, py: 2 }}> <Box sx={{ px: 1, py: 3 }}>
{columns.map((column) => {columns.map((column) =>
column.enableColumnFilter ? ( column.enableColumnFilter ? (
column.dependencyId ? ( column.dependencyId ? (
@@ -144,7 +140,7 @@ function FilterBody({ columns, drawerState, setDrawerState }) {
</form> </form>
</ScrollBox> </ScrollBox>
)} )}
</Drawer> </>
); );
} }

View File

@@ -1,8 +1,8 @@
"use client"; "use client";
import { Box, IconButton, Typography } from "@mui/material";
import CancelIcon from "@mui/icons-material/Cancel"; import CancelIcon from "@mui/icons-material/Cancel";
import FilterAltIcon from "@mui/icons-material/FilterAlt"; import FilterAltIcon from "@mui/icons-material/FilterAlt";
import { Box, IconButton, Typography } from "@mui/material";
function FilterHeader({ setDrawerState }) { function FilterHeader({ setDrawerState }) {
return ( return (

View File

@@ -30,6 +30,7 @@ function MuiDatePicker({ label, value, setFieldValue, name, minDate, maxDate, he
maxDate={maxDate ? new Date(maxDate) : null} maxDate={maxDate ? new Date(maxDate) : null}
slotProps={{ slotProps={{
textField: { textField: {
size: "small",
error: error, error: error,
placeholder: placeholder, placeholder: placeholder,
InputProps: { InputProps: {
@@ -60,9 +61,9 @@ function MuiDatePicker({ label, value, setFieldValue, name, minDate, maxDate, he
}, },
}} }}
/> />
<FormHelperText id="component-helper-text" sx={{ ml: 2, color: error ? "#d32f2f" : "unset" }}> {/* <FormHelperText id="component-helper-text" variant="caption" sx={{ ml: 2, color: error ? "#d32f2f" : "unset", fontWeight: 'bold' }}>
{helperText ? helperText : ""} {helperText ? helperText : ""}
</FormHelperText> </FormHelperText> */}
</LocalizationProvider> </LocalizationProvider>
</Box> </Box>
); );

View File

@@ -1,46 +1,28 @@
"use client"; "use client";
import { FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select, Typography } from "@mui/material"; import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
import { useEffect, useMemo } from "react"; function CustomSelect({ column, filterParameters, handleChange }) {
function CustomSelect({ column, dependencyFieldValue, filterParameters, defaultFilterTranslation, handleChange }) {
const columnSelectOption = useMemo(() => {
return column?.columnSelectOption?.(dependencyFieldValue) || [];
}, [dependencyFieldValue]);
useEffect(() => {
const isValidValue = columnSelectOption.some((option) => option.value === filterParameters.value);
if (!isValidValue) {
handleChange({ ...filterParameters, value: "" });
}
}, [columnSelectOption, filterParameters.value, handleChange]);
const isValidValue = columnSelectOption.some((option) => option.value === filterParameters.value);
const selectedValue = isValidValue ? filterParameters.value : "";
return ( return (
<FormControl fullWidth sx={{ marginY: 1 }}> <FormControl fullWidth sx={{ marginY: 1 }} size="small">
<InputLabel id={`label${column.id}`}>{column.header}</InputLabel> <InputLabel id={`label${column.id}`} shrink>
{column.header}
</InputLabel>
<Select <Select
labelId={`label${column.id}`} labelId={`label${column.id}`}
id={column.id} id={column.id}
name={`${column.id}.value`} name={`${column.id}.value`}
value={selectedValue} value={filterParameters.value}
input={<OutlinedInput label={column.header} />} input={<OutlinedInput label={column.header} />}
size="small"
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })} onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
displayEmpty
> >
<MenuItem value="">{column.header}</MenuItem> {column.columnSelectOption().map((option) => (
{columnSelectOption.map((option) => (
<MenuItem key={option.value} value={option.value}> <MenuItem key={option.value} value={option.value}>
{option.label} {option.label}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
<FormHelperText>
<Typography variant="button" sx={{ color: "#155175" }}>
نوع فیلتر: {defaultFilterTranslation}
</Typography>
</FormHelperText>
</FormControl> </FormControl>
); );
} }

View File

@@ -12,15 +12,19 @@ function CustomSelectByDependency({
columnSelectOption, columnSelectOption,
}) { }) {
return ( return (
<FormControl fullWidth sx={{ marginY: 1 }}> <FormControl fullWidth sx={{ my: 1 }} size="small">
<InputLabel id={`label${column.id}`}>{column.header}</InputLabel> <InputLabel id={`label${column.id}`} shrink>
{column.header}
</InputLabel>
<Select <Select
labelId={`label${column.id}`} labelId={`label${column.id}`}
id={column.id} id={column.id}
name={`${column.id}.value`} name={`${column.id}.value`}
value={value} value={value}
size="small"
input={<OutlinedInput label={column.header} />} input={<OutlinedInput label={column.header} />}
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })} onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
displayEmpty
> >
{columnSelectOption.map((option) => ( {columnSelectOption.map((option) => (
<MenuItem key={option.value} value={option.value}> <MenuItem key={option.value} value={option.value}>
@@ -28,11 +32,11 @@ function CustomSelectByDependency({
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
<FormHelperText> {/* <FormHelperText>
<Typography variant="button" sx={{ color: "#155175" }}> <Typography variant="caption" sx={{ color: "#155175", fontWeight: 'bold' }}>
نوع فیلتر: {defaultFilterTranslation} نوع فیلتر: {defaultFilterTranslation}
</Typography> </Typography>
</FormHelperText> </FormHelperText> */}
</FormControl> </FormControl>
); );
} }

View File

@@ -21,7 +21,7 @@ function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslati
}; };
return ( return (
<FormControl fullWidth sx={{ marginY: 1 }}> <FormControl fullWidth sx={{ marginY: 1 }} size="small">
<InputLabel htmlFor="uncontrolled-native" id={`label${column.id}`}> <InputLabel htmlFor="uncontrolled-native" id={`label${column.id}`}>
{column.header} {column.header}
</InputLabel> </InputLabel>
@@ -30,6 +30,7 @@ function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslati
id={column.id} id={column.id}
value={filterParameters.value} value={filterParameters.value}
multiple multiple
size="small"
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })} onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
renderValue={(selected) => ( renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}> <Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
@@ -39,6 +40,7 @@ function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslati
</Box> </Box>
)} )}
input={<OutlinedInput label={column.header} />} input={<OutlinedInput label={column.header} />}
InputLabelProps={{ shrink: true }}
> >
{columnSelectOption.map((option) => ( {columnSelectOption.map((option) => (
<MenuItem key={option.value} value={option.value}> <MenuItem key={option.value} value={option.value}>
@@ -46,11 +48,11 @@ function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslati
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
<FormHelperText> {/* <FormHelperText>
<Typography variant="button" sx={{ color: "#155175" }}> <Typography variant="caption" sx={{ color: "#155175", fontWeight: 'bold' }}>
نوع فیلتر: {defaultFilterTranslation} نوع فیلتر: {defaultFilterTranslation}
</Typography> </Typography>
</FormHelperText> </FormHelperText> */}
</FormControl> </FormControl>
); );
} }

View File

@@ -18,14 +18,14 @@ function CustomTextField({
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })} onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
onBlur={handleBlur} onBlur={handleBlur}
fullWidth fullWidth
helperText={ // helperText={
<Typography variant="button" sx={{ color: "#155175" }}> // <Typography variant="caption" sx={{ color: "#155175", fontWeight: 'bold' }}>
نوع فیلتر: {defaultFilterTranslation} // نوع فیلتر: {defaultFilterTranslation}
</Typography> // </Typography>
} // }
variant="outlined" variant="outlined"
size="normal" size="small"
sx={{ marginY: 1 }} sx={{ my: 1 }}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}> <InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}>
@@ -33,6 +33,7 @@ function CustomTextField({
</InputAdornment> </InputAdornment>
), ),
}} }}
InputLabelProps={{ shrink: true }}
/> />
); );
} }

View File

@@ -24,13 +24,13 @@ function CustomTextFieldRange({
fullWidth fullWidth
error={touched?.[`${column.id}`]?.value && Boolean(errors?.[`${column.id}`]?.value)} error={touched?.[`${column.id}`]?.value && Boolean(errors?.[`${column.id}`]?.value)}
helperText={ helperText={
<Typography variant="button" sx={{ color: "#155175" }}> <Typography variant="caption" sx={{ color: "#155175", fontWeight: "bold" }}>
نوع فیلتر: {defaultFilterTranslation} نوع فیلتر: {defaultFilterTranslation}
</Typography> </Typography>
} }
variant="outlined" variant="outlined"
size="normal" size="small"
sx={{ marginY: 1, marginRight: 1 }} sx={{ my: 1, marginRight: 1 }}
/> />
<TextField <TextField
id={`${column.id}.value[1]`} id={`${column.id}.value[1]`}
@@ -45,8 +45,8 @@ function CustomTextFieldRange({
value={filterParameters.value[1]} value={filterParameters.value[1]}
fullWidth fullWidth
variant="outlined" variant="outlined"
size="normal" size="small"
sx={{ marginY: 1 }} sx={{ my: 1 }}
InputProps={{ InputProps={{
endAdornment: ( endAdornment: (
<InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}> <InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}>

View File

@@ -2,6 +2,7 @@
import { useState } from "react"; import { useState } from "react";
import FilterButton from "@/core/components/DataTable/filter/FilterButton"; import FilterButton from "@/core/components/DataTable/filter/FilterButton";
import FilterBody from "@/core/components/DataTable/filter/FilterBody"; import FilterBody from "@/core/components/DataTable/filter/FilterBody";
import { Drawer } from "@mui/material";
function FilterColumn({ columns, user_id, page_name, table_name }) { function FilterColumn({ columns, user_id, page_name, table_name }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -9,7 +10,11 @@ function FilterColumn({ columns, user_id, page_name, table_name }) {
return ( return (
<> <>
<FilterButton drawerState={open} setDrawerState={setOpen} /> <FilterButton drawerState={open} setDrawerState={setOpen} />
{open && ( <Drawer
open={open}
onClose={() => setOpen(false)}
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%", zIndex: "1300" }}
>
<FilterBody <FilterBody
columns={columns} columns={columns}
drawerState={open} drawerState={open}
@@ -18,7 +23,7 @@ function FilterColumn({ columns, user_id, page_name, table_name }) {
page_name={page_name} page_name={page_name}
table_name={table_name} table_name={table_name}
/> />
)} </Drawer>
</> </>
); );
} }

View File

@@ -122,8 +122,8 @@ const MapInteraction = ({ setValue, startLat, startLng }) => {
zIndex: 1000, zIndex: 1000,
}} }}
> >
<Button variant="contained" onClick={handleUnlockMarker}> <Button variant="contained" color="warning" onClick={handleUnlockMarker}>
ویرایش تغییر مکان
</Button> </Button>
</Box> </Box>
)} )}

View File

@@ -73,7 +73,7 @@ const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => {
{ lat: startLat, lng: startLng }, { lat: startLat, lng: startLng },
{ lat: endLat, lng: endLng }, { lat: endLat, lng: endLng },
], ],
{ paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] } { paddingTopLeft: [64, 64], paddingBottomRight: [64, 64] }
); );
} }
}, [startLat, map, endLng]); }, [startLat, map, endLng]);
@@ -142,7 +142,7 @@ const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => {
zIndex: 1000, zIndex: 1000,
}} }}
> >
<Button variant={"contained"} onClick={handleUnlockStart}> <Button variant={"contained"} color="warning" onClick={handleUnlockStart}>
ویرایش نقطه شروع ویرایش نقطه شروع
</Button> </Button>
</Box> </Box>
@@ -184,8 +184,8 @@ const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => {
setIsEndLocked(true); setIsEndLocked(true);
setEndIconColor("#D13131"); setEndIconColor("#D13131");
map.fitBounds([startPosition, map.getCenter()], { map.fitBounds([startPosition, map.getCenter()], {
paddingTopLeft: [16, 16], paddingTopLeft: [64, 64],
paddingBottomRight: [16, 16], paddingBottomRight: [64, 64],
}); });
} }
}, },
@@ -202,7 +202,7 @@ const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => {
zIndex: 1000, zIndex: 1000,
}} }}
> >
<Button variant={"contained"} onClick={handleUnlockEnd}> <Button variant={"contained"} color="warning" onClick={handleUnlockEnd}>
ویرایش نقاط ویرایش نقاط
</Button> </Button>
</Box> </Box>

View File

@@ -22,7 +22,7 @@ const ChangePass = ({ open, setOpen }) => {
<LoadingButton <LoadingButton
loading={loading} loading={loading}
variant="outlined" variant="outlined"
color="error" color="secondary"
size="large" size="large"
onClick={handleCloseForm} onClick={handleCloseForm}
> >
@@ -38,7 +38,7 @@ const ChangePass = ({ open, setOpen }) => {
loadingPosition="end" loadingPosition="end"
endIcon={<SaveIcon />} endIcon={<SaveIcon />}
> >
ثبت تغییر
</LoadingButton> </LoadingButton>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View File

@@ -22,7 +22,7 @@ const Update = ({ open, setOpen }) => {
<Form handleCloseForm={handleCloseForm} setLoading={setLoading} /> <Form handleCloseForm={handleCloseForm} setLoading={setLoading} />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button autoFocus variant="outlined" color="error" size="large" onClick={handleCloseForm}> <Button variant="outlined" color="secondary" size="large" onClick={handleCloseForm}>
بستن بستن
</Button> </Button>
<LoadingButton <LoadingButton
@@ -35,7 +35,7 @@ const Update = ({ open, setOpen }) => {
loadingPosition="end" loadingPosition="end"
endIcon={<SaveIcon />} endIcon={<SaveIcon />}
> >
ثبت ویرایش
</LoadingButton> </LoadingButton>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View File

@@ -11,34 +11,34 @@ const RahdarCode = ({ rahdarsCode, setRahdarsCode, error, multiple = true }) =>
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const requestServer = useRequest(); const requestServer = useRequest();
const fetchRahdarCodes = (inputValue, controller) => {
const debouncer = debounce((query) => {
if ((query || "").length < 3) {
setOptions([]);
return;
}
setLoading(true);
requestServer(`${GET_RAHDARANS_LIST_SEARCH}?code=${query}`, "get", {
requestOptions: { signal: controller.signal },
})
.then((response) => {
const combinedArray = rahdarsCode
? [...rahdarsCode, ...response.data.data]
: [...response.data.data];
const uniqueArray = Array.from(new Map(combinedArray.map((item) => [item.id, item])).values());
setOptions(uniqueArray || []);
})
.catch(() => {
setOptions([]);
})
.finally(() => {
setLoading(false);
});
}, 500);
debouncer(inputValue);
};
useEffect(() => { useEffect(() => {
const fetchRahdarCodes = (inputValue, controller) => {
const debouncer = debounce((query) => {
if ((query || "").length < 3) {
setOptions([]);
return;
}
setLoading(true);
requestServer(`${GET_RAHDARANS_LIST_SEARCH}?code=${query}`, "get", {
requestOptions: { signal: controller.signal },
})
.then((response) => {
const combinedArray = rahdarsCode
? [...rahdarsCode, ...response.data.data]
: [...response.data.data];
const uniqueArray = Array.from(new Map(combinedArray.map((item) => [item.id, item])).values());
setOptions(uniqueArray || []);
})
.catch(() => {
setOptions([]);
})
.finally(() => {
setLoading(false);
});
}, 500);
debouncer(inputValue);
};
const controller = new AbortController(); const controller = new AbortController();
fetchRahdarCodes(inputValue, controller); fetchRahdarCodes(inputValue, controller);
return () => controller.abort(); return () => controller.abort();

View File

@@ -16,13 +16,14 @@ const ShowPlak = ({ plak_number }) => {
const plakParts = processPlak(plak_number); const plakParts = processPlak(plak_number);
return ( return (
<Stack sx={{ border: 1, borderColor: "divider", borderRadius: 1, maxWidth: "150px" }} direction={"row"}> <Stack sx={{ border: 1, borderColor: "divider", borderRadius: 1 }} direction={"row"}>
<Stack <Stack
sx={{ sx={{
borderRight: 1, borderRight: 1,
borderColor: "divider", borderColor: "divider",
textAlign: "center", textAlign: "center",
width: "100%", width: "100%",
px: 1,
}} }}
> >
{plakParts.region} {plakParts.mainNumber} {plakParts.region} {plakParts.mainNumber}

View File

@@ -0,0 +1,10 @@
import moment from "jalali-moment";
export const formatSecondsToHHMMSS = (seconds) => {
const duration = moment.duration(seconds, "seconds");
const hours = Math.floor(duration.asHours());
const minutes = duration.minutes();
const secs = duration.seconds();
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
};

View File

@@ -1,3 +1,5 @@
const api = process.env.NEXT_PUBLIC_API_URL;
export const headerMenu = [ export const headerMenu = [
{ {
title: "تصادفات", title: "تصادفات",
@@ -5,7 +7,7 @@ export const headerMenu = [
[ [
{ {
title: "تصادفات روزانه", title: "تصادفات روزانه",
href: "https://rms.witel.ir/v2/daily_accidents/create", href: api + "/v2/daily_accidents/create",
}, },
], ],
], ],
@@ -16,11 +18,11 @@ export const headerMenu = [
[ [
{ {
title: "گزارش محوری", title: "گزارش محوری",
href: "https://rms.witel.ir/v2/axis_reports", href: api + "/v2/axis_reports",
}, },
{ {
title: "هوش تجاری (BI)", title: "هوش تجاری (BI)",
href: "https://rms.witel.ir/v2/axis_reports", href: api + "/v2/axis_reports",
}, },
], ],
], ],
@@ -31,17 +33,17 @@ export const headerMenu = [
[ [
{ {
title: "پراکندگی بر روی نقشه راهدارخانه ها", title: "پراکندگی بر روی نقشه راهدارخانه ها",
href: "https://rms.witel.ir/v2/map?type=rahdar", href: api + "/v2/map?type=rahdar",
}, },
{ {
title: "راهدارخانه ها", title: "راهدارخانه ها",
href: "https://rms.witel.ir/v2/rahdari_points", href: api + "/v2/rahdari_points",
}, },
], ],
[ [
{ {
title: "ممیزی ماشین آلات راهداری کشور", title: "ممیزی ماشین آلات راهداری کشور",
href: "https://rms.witel.ir/cmms-audit", href: api + "/cmms-audit",
}, },
], ],
], ],
@@ -52,7 +54,7 @@ export const headerMenu = [
[ [
{ {
title: "تحقیق و توسعه", title: "تحقیق و توسعه",
href: "https://rms.witel.ir/RandD", href: api + "/RandD",
}, },
{ {
title: "سامانه مدیریت یکپارچه زیرساخت های راه (PMS,BMS,SMS)", title: "سامانه مدیریت یکپارچه زیرساخت های راه (PMS,BMS,SMS)",

View File

@@ -165,7 +165,7 @@ export const pageMenu = [
}, },
{ {
id: "roadItemManagmentReportItems", id: "roadItemManagmentReportItems",
label: "تعداد و حجم فعالیت ها به تفکیک موارد مشاهده شده", label: "تعداد و حجم فعالیت ها به تفکیک موارد اقدام شده",
type: "page", type: "page",
route: "/dashboard/road-items/reports/sub-items-report", route: "/dashboard/road-items/reports/sub-items-report",
permissions: [ permissions: [

View File

@@ -30,10 +30,12 @@ export const UPDATE_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/update";
//road patrol //road patrol
export const GET_ROAD_PATROL_OPERATOR_LIST = api + "/api/v3/road_patrols/operator_index"; export const GET_ROAD_PATROL_OPERATOR_LIST = api + "/api/v3/road_patrols/operator_index";
export const EXPORT_ROAD_PATROL_OPERATOR_LIST = "https://rms.witel.ir/v2/road_patrols/operator/cartable/report"; export const EXPORT_ROAD_PATROL_OPERATOR_LIST = api + "/v2/road_patrols/operator/cartable/report";
export const GET_ROAD_PATROL_SUPERVISOR_LIST = api + "/api/v3/road_patrols/supervisor_index"; export const GET_ROAD_PATROL_SUPERVISOR_LIST = api + "/api/v3/road_patrols/supervisor_index";
export const EXPORT_ROAD_PATROL_SUPERVISOR_LIST = "https://rms.witel.ir/v2/road_patrols/supervisor/cartable/report"; export const EXPORT_ROAD_PATROL_SUPERVISOR_LIST = api + "/v2/road_patrols/supervisor/cartable/report";
export const DELETE_ROAD_PATROL_SUPERVISOR = api + "/api/v3/road_patrols/delete"; 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 CREATE_PATROL = api + "/api/v3/road_patrols/store";
export const GET_FMS_DATA = api + "/api/v3/fms_vehicle/get_activity"; 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_OTP_TOKEN = api + "/v2/get_otp_token";
@@ -45,9 +47,9 @@ export const GET_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervis
export const EXPORT_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_report"; 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 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_ROAD_ITEMS_OPERATOR_LIST = api + "/api/v3/road_items/operator_report";
export const GET_ROAD_ITEMS_ITEM = "https://rms.witel.ir/v2/items"; export const GET_ROAD_ITEMS_ITEM = api + "/v2/items";
export const GET_ROAD_ITEMS_SUB_ITEM = "https://rms.witel.ir/v2/sub_items"; export const GET_ROAD_ITEMS_SUB_ITEM = api + "/v2/sub_items";
export const GET_EDARAT_LISTS = "https://rms.witel.ir/public/contents/edarate_shahri_by_province"; export const GET_EDARAT_LISTS = api + "/public/contents/edarate_shahri_by_province";
export const CREATE_ROAD_ITEMS = api + "/api/v3/road_items/store"; export const CREATE_ROAD_ITEMS = api + "/api/v3/road_items/store";
export const UPDATE_ROAD_ITEMS = api + "/api/v3/road_items/update"; export const UPDATE_ROAD_ITEMS = api + "/api/v3/road_items/update";
export const VERIFY_BY_SUPERVISOR = api + "/api/v3/road_items/verify_by_supervisor"; export const VERIFY_BY_SUPERVISOR = api + "/api/v3/road_items/verify_by_supervisor";