work on patrol form instead sending request

This commit is contained in:
2025-01-06 13:08:47 +03:30
parent 6a7677ec5c
commit 6371630380
18 changed files with 539 additions and 335 deletions

View File

@@ -1,10 +1,9 @@
import React, { useState } from "react"; import React, {useState} from "react";
import { useForm } from "react-hook-form"; import {useForm} from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup"; import {yupResolver} from "@hookform/resolvers/yup";
import { array, mixed, number, object, string } from "yup"; import {array, mixed, number, object, string} from "yup";
import { useTheme } from "@emotion/react"; import {useTheme} from "@emotion/react";
import useRequest from "@/lib/hooks/useRequest"; import {Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery} from "@mui/material";
import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material";
import StyledForm from "@/core/components/StyledForm"; import StyledForm from "@/core/components/StyledForm";
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
import FileCopyIcon from "@mui/icons-material/FileCopy"; import FileCopyIcon from "@mui/icons-material/FileCopy";
@@ -15,10 +14,9 @@ import BeenhereIcon from "@mui/icons-material/Beenhere";
import GetItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm"; import GetItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm";
import GetSubItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm"; import GetSubItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm";
import GetItemInfo from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo"; import GetItemInfo from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo";
import { CREATE_ROAD_ITEMS } from "@/core/utils/routes";
function TabPanel(props) { function TabPanel(props) {
const { children, value, index } = props; const {children, value, index} = props;
return ( return (
<div role="tabpanel" hidden={value !== index}> <div role="tabpanel" hidden={value !== index}>
{value === index && <Box>{children}</Box>} {value === index && <Box>{children}</Box>}
@@ -26,31 +24,32 @@ function TabPanel(props) {
); );
} }
const defaultValues = {
item_id: null,
sub_item_id: null,
amount: "",
activity_time: "",
activity_date: "",
before_image: null,
after_image: null,
cmms_machines: null,
rahdaran_id: null,
start_point: "",
end_point: "",
};
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("وارد کردن کد خودرو الزامیست!"), cmms_machines: array().required("وارد کردن کد خودرو الزامیست!"),
rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), // بررسی حداقل یک آیتم, rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), // بررسی حداقل یک آیتم,
activity_time: string().required("لطفا زمان فعالیت را انتخاب کنید!"), activity_time: string()
activity_date: string().required("لطفاً تاریخ شروع فعالیت را انتخاب کنید!"), .test("activity-time-conditional", "لطفا زمان فعالیت را انتخاب کنید!", function (value) {
const {is_gasht} = this.options.context; // دسترسی به context
if (is_gasht) {
return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد
}
return !!value; // در غیر این صورت مقدار فیلد باید وجود داشته باشد
}),
activity_date: string()
.test("activity-date-conditional", "لطفاً تاریخ شروع فعالیت را انتخاب کنید!", function (value) {
const {is_gasht} = this.options.context; // دسترسی به context
if (is_gasht) {
return true; // اگر is_gasht true باشد، این فیلد نیاز به اعتبارسنجی ندارد
}
return !!value; // در غیر این صورت مقدار فیلد باید وجود داشته باشد
}),
before_image: mixed() before_image: mixed()
.nullable() .nullable()
.test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) { .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) {
const { subItemsList } = this.options.context; const {subItemsList} = this.options.context;
const needsImage = subItemsList?.needs_image === 1; const needsImage = subItemsList?.needs_image === 1;
if (needsImage) { if (needsImage) {
return !!value; return !!value;
@@ -60,7 +59,7 @@ const validationSchema = object({
after_image: mixed() after_image: mixed()
.nullable() .nullable()
.test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) { .test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) {
const { subItemsList } = this.options.context; const {subItemsList} = this.options.context;
const needsImage = subItemsList?.needs_image === 1; const needsImage = subItemsList?.needs_image === 1;
if (needsImage) { if (needsImage) {
return !!value; return !!value;
@@ -73,7 +72,7 @@ const validationSchema = object({
}) })
.required("لطفاً نقطه شروع را مشخص کنید!"), .required("لطفاً نقطه شروع را مشخص کنید!"),
end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) { end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) {
const { subItemsList } = this.options.context; const {subItemsList} = this.options.context;
const needsEndPoint = subItemsList?.needs_end_point === 1; const needsEndPoint = subItemsList?.needs_end_point === 1;
if (needsEndPoint) { if (needsEndPoint) {
return !!value; // چک می‌کند که مقدار موجود است return !!value; // چک می‌کند که مقدار موجود است
@@ -82,7 +81,20 @@ const validationSchema = object({
}), }),
}); });
const CreateFormContent = ({ setOpen, onSubmit }) => { const CreateFormContent = ({setOpen, onSubmit, is_gasht = false}) => {
const defaultValues = {
item_id: null,
sub_item_id: null,
amount: "",
before_image: null,
after_image: null,
cmms_machines: null,
rahdaran_id: null,
start_point: "",
end_point: "",
...(!is_gasht && {activity_time: "", activity_date: ""}),
};
const [tabState, setTabState] = useState(0); const [tabState, setTabState] = useState(0);
const [itemsList, setItemsList] = useState(); const [itemsList, setItemsList] = useState();
const [subItemsList, setSubItemsList] = useState([]); const [subItemsList, setSubItemsList] = useState([]);
@@ -126,15 +138,15 @@ const CreateFormContent = ({ setOpen, onSubmit }) => {
handleSubmit, handleSubmit,
setValue, setValue,
resetField, resetField,
formState: { errors, isSubmitting }, formState: {errors, isSubmitting},
} = useForm({ } = useForm({
defaultValues, defaultValues,
resolver: yupResolver(validationSchema), resolver: yupResolver(validationSchema),
mode: "onBlur", mode: "onBlur",
context: { subItemsList }, context: {subItemsList, is_gasht},
}); });
const onSubmitBase = (data) => { const onSubmitBase = (data) => {
let result = { ...data }; let result = {...data};
if (result.before_image === null) { if (result.before_image === null) {
delete result.before_image; delete result.before_image;
@@ -180,9 +192,10 @@ const CreateFormContent = ({ setOpen, onSubmit }) => {
}, },
}); });
}; };
console.log(errors)
return ( return (
<StyledForm onSubmit={handleSubmit(onSubmitBase)}> <StyledForm onSubmit={handleSubmit(onSubmitBase)} id="road_items">
<Tabs <Tabs
allowScrollButtonsMobile allowScrollButtonsMobile
value={tabState} value={tabState}
@@ -195,12 +208,12 @@ const CreateFormContent = ({ setOpen, onSubmit }) => {
backgroundColor: "#efefef", backgroundColor: "#efefef",
}} }}
> >
<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"}}>
<Box sx={{ flex: 1 }}> <Box sx={{flex: 1}}>
<TabPanel value={tabState} index={0}> <TabPanel value={tabState} index={0}>
<GetItemsForm <GetItemsForm
setItemsList={setItemsList} setItemsList={setItemsList}
@@ -229,17 +242,18 @@ const CreateFormContent = ({ setOpen, onSubmit }) => {
register={register} register={register}
watch={watch} watch={watch}
getValues={getValues} getValues={getValues}
is_gasht={is_gasht}
/> />
</TabPanel> </TabPanel>
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions sx={{ alignItems: "center", justifyContent: "center" }}> <DialogActions sx={{alignItems: "center", justifyContent: "center"}}>
<Button <Button
onClick={handlePrev} onClick={handlePrev}
variant="outlined" variant="outlined"
color="error" color="error"
size="large" size="large"
startIcon={tabState === 0 ? <ExitToAppIcon /> : <KeyboardDoubleArrowRightIcon />} startIcon={tabState === 0 ? <ExitToAppIcon/> : <KeyboardDoubleArrowRightIcon/>}
> >
{tabState === 0 ? "بستن" : "مرحله قبل"} {tabState === 0 ? "بستن" : "مرحله قبل"}
</Button> </Button>
@@ -249,7 +263,8 @@ const CreateFormContent = ({ setOpen, onSubmit }) => {
size="large" size="large"
disabled={isSubmitting} disabled={isSubmitting}
type={"submit"} type={"submit"}
endIcon={<BeenhereIcon />} form="road_items"
endIcon={<BeenhereIcon/>}
> >
{isSubmitting ? "در حال ثبت فعالیت" : "ثبت فعالیت"} {isSubmitting ? "در حال ثبت فعالیت" : "ثبت فعالیت"}
</Button> </Button>

View File

@@ -1,20 +1,20 @@
import { Stack, TextField, Grid } from "@mui/material"; import {Grid, Stack} from "@mui/material";
import ImageUpload from "./ImageUpload"; import ImageUpload from "./ImageUpload";
import MuiDatePicker from "@/core/components/MuiDatePicker"; import MuiDatePicker from "@/core/components/MuiDatePicker";
import MuiTimePicker from "@/core/components/MuiTimePicker"; import MuiTimePicker from "@/core/components/MuiTimePicker";
import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker";
import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
import { Controller } from "react-hook-form"; import {Controller} from "react-hook-form";
import CarCode from "@/core/components/CarCode"; import CarCode from "@/core/components/CarCode";
import RahdarCode from "@/core/components/RahdarCode"; import RahdarCode from "@/core/components/RahdarCode";
import PreviousStatesInfo from "./PreviousStatesInfo"; import PreviousStatesInfo from "./PreviousStatesInfo";
import NumberField from "@/core/components/NumberField"; import NumberField from "@/core/components/NumberField";
const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, errors, watch, getValues }) => { const GetItemInfo = ({register, itemsList, subItemsList, control, setValue, errors, watch, getValues, is_gasht}) => {
return ( return (
<Stack spacing={2}> <Stack spacing={2}>
<Stack> <Stack>
<PreviousStatesInfo itemsList={itemsList} subItemsList={subItemsList} /> <PreviousStatesInfo itemsList={itemsList} subItemsList={subItemsList}/>
</Stack> </Stack>
<Stack> <Stack>
<NumberField <NumberField
@@ -45,7 +45,7 @@ const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, err
<Stack> <Stack>
<Controller <Controller
control={control} control={control}
render={({ field, fieldState: { error } }) => { render={({field, fieldState: {error}}) => {
return ( return (
<CarCode <CarCode
carCode={field.value || []} carCode={field.value || []}
@@ -61,7 +61,7 @@ const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, err
<Stack> <Stack>
<Controller <Controller
control={control} control={control}
render={({ field, fieldState: { error } }) => { render={({field, fieldState: {error}}) => {
return ( return (
<RahdarCode <RahdarCode
rahdarsCode={field.value || []} rahdarsCode={field.value || []}
@@ -75,38 +75,40 @@ const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, err
</Stack> </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}/>
) : null} ) : null}
</Stack> </Stack>
<Stack> {!is_gasht ? (
<Grid container spacing={2}> <Stack>
<Grid item xs={12} md={6}> <Grid container spacing={2}>
<MuiDatePicker <Grid item xs={12} md={6}>
name="activity_date" <MuiDatePicker
error={errors.activity_date ? errors.activity_date.message : null} name="activity_date"
value={watch("activity_date")} error={errors.activity_date ? errors.activity_date.message : null}
placeholder={"تاریخ شروع فعالیت"} value={watch("activity_date")}
setFieldValue={setValue} placeholder={"تاریخ شروع فعالیت"}
helperText={errors.activity_date ? errors.activity_date.message : null} setFieldValue={setValue}
/> helperText={errors.activity_date ? errors.activity_date.message : null}
/>
</Grid>
<Grid item xs={12} md={6}>
<MuiTimePicker
value={watch("activity_time")}
name="activity_time"
error={errors.activity_time ? errors.activity_time.message : null}
placeholder={"زمان فعالیت"}
setFieldValue={setValue}
helperText={errors.activity_time ? errors.activity_time.message : null}
/>
</Grid>
</Grid> </Grid>
<Grid item xs={12} md={6}> </Stack>
<MuiTimePicker ) : null}
value={watch("activity_time")}
name="activity_time"
error={errors.activity_time ? errors.activity_time.message : null}
placeholder={"زمان فعالیت"}
setFieldValue={setValue}
helperText={errors.activity_time ? errors.activity_time.message : null}
/>
</Grid>
</Grid>
</Stack>
<Stack> <Stack>
{subItemsList?.needs_end_point === 1 ? ( {subItemsList?.needs_end_point === 1 ? (
<MapInfoTwoMarker setValue={setValue} errors={errors} /> <MapInfoTwoMarker setValue={setValue} errors={errors}/>
) : ( ) : (
<MapInfoOneMarker setValue={setValue} errors={errors} /> <MapInfoOneMarker setValue={setValue} errors={errors}/>
)} )}
</Stack> </Stack>
</Stack> </Stack>

View File

@@ -1,9 +1,9 @@
import { FormControl, FormHelperText, Grid } from "@mui/material"; import {FormControl, FormHelperText, Grid} from "@mui/material";
import UploadSystem from "@/core/components/UploadSystem"; import UploadSystem from "@/core/components/UploadSystem";
import { Controller } from "react-hook-form"; import {Controller} from "react-hook-form";
import React, { useEffect, useState } from "react"; import React, {useEffect, useState} from "react";
const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null, afterImage = null }) => { const ImageUpload = ({control, setValue, errors, getValues, beforeImage = null, afterImage = null}) => {
const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null); const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null);
const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null); const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null);
const [beforeFileName, setBeforeFileName] = useState(null); const [beforeFileName, setBeforeFileName] = useState(null);
@@ -15,15 +15,31 @@ const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null,
const [showAfterImage, setShowAfterImage] = useState(!afterImage); const [showAfterImage, setShowAfterImage] = useState(!afterImage);
useEffect(() => { useEffect(() => {
if (getValues("before_image")) { const beforeImage = getValues("before_image");
const afterImage = getValues("after_image");
if (beforeImage) {
setShowBeforeImage(false); setShowBeforeImage(false);
setBeforeImg(getValues("before_image"));
setBeforeFileType("image/"); if (typeof beforeImage === "string") {
setBeforeImg(beforeImage);
setBeforeFileType("image/");
} else if (beforeImage instanceof File) {
setBeforeImg(URL.createObjectURL(beforeImage));
setBeforeFileType(beforeImage.type);
}
} }
if (getValues("after_image")) {
if (afterImage) {
setShowAfterImage(false); setShowAfterImage(false);
setAfterImg(getValues("after_image"));
setAfterFileType("image/"); if (typeof afterImage === "string") {
setAfterImg(afterImage);
setAfterFileType("image/");
} else if (afterImage instanceof File) {
setAfterImg(URL.createObjectURL(afterImage));
setAfterFileType(afterImage.type);
}
} }
}, [getValues]); }, [getValues]);
@@ -57,7 +73,7 @@ const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null,
<Controller <Controller
name="before_image" name="before_image"
control={control} control={control}
render={({ field: { value, onChange } }) => { render={({field: {value, onChange}}) => {
return ( return (
<FormControl <FormControl
error={errors.before_image} error={errors.before_image}
@@ -68,7 +84,7 @@ const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null,
justifyContent: "center", justifyContent: "center",
}} }}
> >
<FormHelperText sx={{ fontSize: "large" }} id="before_image"> <FormHelperText sx={{fontSize: "large"}} id="before_image">
عکس قبل از اقدام عکس قبل از اقدام
</FormHelperText> </FormHelperText>
<UploadSystem <UploadSystem
@@ -93,7 +109,7 @@ const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null,
<Controller <Controller
name="after_image" name="after_image"
control={control} control={control}
render={({ field: { value, onChange } }) => { render={({field: {value, onChange}}) => {
return ( return (
<FormControl <FormControl
error={errors.before_image} error={errors.before_image}
@@ -104,7 +120,7 @@ const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null,
justifyContent: "center", justifyContent: "center",
}} }}
> >
<FormHelperText sx={{ fontSize: "large" }} id="before_image"> <FormHelperText sx={{fontSize: "large"}} id="before_image">
عکس بعد از اقدام عکس بعد از اقدام
</FormHelperText> </FormHelperText>
<UploadSystem <UploadSystem

View File

@@ -14,21 +14,34 @@ const EditFormContent = ({setOpenEditDialog, onSubmit, defaultData}) => {
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, cmms_machines: defaultData?.cmms_machines || null,
rahdaran_id: defaultData?.rahdaran || null, rahdaran_id: defaultData?.rahdaran_id || null,
}; };
const onSubmitBase = (data) => { const onSubmitBase = (data) => {
let result = {...data}; let result = {...data};
(result.before_image === null || result.before_image === defaultValues.before_image) && if (result.before_image === null) {
delete result.before_image; delete result.before_image;
(result.after_image === null || result.after_image === defaultValues.after_image) && delete result.after_image; delete data.before_image;
if (subItem.needs_end_point !== 1) { }
if (result.after_image === null) {
delete result.after_image;
delete data.after_image;
}
if (result.end_point === "") {
delete result.end_point; delete result.end_point;
delete data.end_point;
} else { } else {
result.end_point = `${result.end_point.lat},${result.end_point.lng}`; result.end_point = `${result.end_point.lat},${result.end_point.lng}`;
} }
result.start_point = `${result.start_point.lat},${result.start_point.lng}`; if (result.start_point === "") {
delete result.start_point;
delete data.start_point;
} else {
result.start_point = `${result.start_point.lat},${result.start_point.lng}`;
}
result.rahdaran_id.forEach((rahdar, index) => { result.rahdaran_id.forEach((rahdar, index) => {
result[`rahdaran_id[${index}]`] = rahdar.id; result[`rahdaran_id[${index}]`] = rahdar.id;
@@ -45,8 +58,10 @@ const EditFormContent = ({setOpenEditDialog, onSubmit, defaultData}) => {
data: { data: {
...data, ...data,
item_name: subItem.item_str, item_name: subItem.item_str,
sub_item_name: subItemsList.name, sub_item_name: subItem.name,
unit_fa: subItemsList.unit, unit_fa: subItem.unit,
item_id: subItem.item,
sub_item_id: subItem.sub_item,
}, },
}); });
}; };

View File

@@ -1,15 +1,15 @@
import { Button, DialogActions, DialogContent, Stack } from "@mui/material"; import {Button, DialogActions, DialogContent, Stack} from "@mui/material";
import ImageUpload from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload"; import ImageUpload from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload";
import NumberField from "@/core/components/NumberField"; import NumberField from "@/core/components/NumberField";
import { Controller, useForm } from "react-hook-form"; import {Controller, useForm} from "react-hook-form";
import CarCode from "@/core/components/CarCode"; import CarCode from "@/core/components/CarCode";
import RahdarCode from "@/core/components/RahdarCode"; import RahdarCode from "@/core/components/RahdarCode";
import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker";
import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
import StyledForm from "@/core/components/StyledForm"; import StyledForm from "@/core/components/StyledForm";
import React from "react"; import React from "react";
import { yupResolver } from "@hookform/resolvers/yup"; import {yupResolver} from "@hookform/resolvers/yup";
import { array, mixed, object, string } from "yup"; import {array, mixed, object, string} from "yup";
const validationSchema = object({ const validationSchema = object({
amount: string().required("وارد کردن مقدار الزامیست!"), amount: string().required("وارد کردن مقدار الزامیست!"),
@@ -18,7 +18,7 @@ const validationSchema = object({
before_image: mixed() before_image: mixed()
.nullable() .nullable()
.test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) { .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) {
const { subItem } = this.options.context; const {subItem} = this.options.context;
const needsImage = subItem?.needs_image === 1; const needsImage = subItem?.needs_image === 1;
if (needsImage) { if (needsImage) {
return !!value; return !!value;
@@ -28,7 +28,7 @@ const validationSchema = object({
after_image: mixed() after_image: mixed()
.nullable() .nullable()
.test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) { .test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) {
const { subItem } = this.options.context; const {subItem} = this.options.context;
const needsImage = subItem?.needs_image === 1; const needsImage = subItem?.needs_image === 1;
if (needsImage) { if (needsImage) {
return !!value; return !!value;
@@ -41,7 +41,7 @@ const validationSchema = object({
}) })
.required("لطفاً نقطه شروع را مشخص کنید!"), .required("لطفاً نقطه شروع را مشخص کنید!"),
end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) { end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) {
const { subItem } = this.options.context; const {subItem} = this.options.context;
const needsEndPoint = subItem?.needs_end_point === 1; const needsEndPoint = subItem?.needs_end_point === 1;
if (needsEndPoint) { if (needsEndPoint) {
return !!value; return !!value;
@@ -50,7 +50,7 @@ const validationSchema = object({
}), }),
}); });
const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, setOpenEditDialog }) => { const EditFormCreate = ({defaultData, subItem, onSubmitBase, defaultValues, setOpenEditDialog}) => {
const { const {
control, control,
getValues, getValues,
@@ -58,22 +58,23 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set
register, register,
handleSubmit, handleSubmit,
setValue, setValue,
formState: { errors, isSubmitting }, formState: {errors, isSubmitting},
} = useForm({ } = useForm({
defaultValues, defaultValues,
resolver: yupResolver(validationSchema), resolver: yupResolver(validationSchema),
mode: "onBlur", mode: "onBlur",
context: { subItem }, context: {subItem},
}); });
return ( return (
<StyledForm onSubmit={handleSubmit(onSubmitBase)}> <StyledForm onSubmit={handleSubmit(onSubmitBase)} id="edit_road_items">
<DialogContent sx={{ overflowY: "auto", maxHeight: "70vh" }}> <DialogContent sx={{overflowY: "auto", maxHeight: "70vh"}}>
<Stack spacing={2}> <Stack spacing={2}>
<Stack> <Stack>
{subItem.needs_image === 1 ? ( {subItem.needs_image === 1 ? (
<ImageUpload <ImageUpload
afterImage={defaultData?.afterImage} afterImage={defaultData?.after_image}
beforeImage={defaultData?.beforeImage} beforeImage={defaultData?.before_image}
getValues={getValues} getValues={getValues}
setValue={setValue} setValue={setValue}
control={control} control={control}
@@ -110,7 +111,7 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set
<Stack> <Stack>
<Controller <Controller
control={control} control={control}
render={({ field, fieldState: { error } }) => { render={({field, fieldState: {error}}) => {
return ( return (
<CarCode <CarCode
carCode={field.value || []} carCode={field.value || []}
@@ -127,7 +128,7 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set
<Stack> <Stack>
<Controller <Controller
control={control} control={control}
render={({ field, fieldState: { error } }) => { render={({field, fieldState: {error}}) => {
return ( return (
<RahdarCode <RahdarCode
rahdarsCode={field.value || []} rahdarsCode={field.value || []}
@@ -161,7 +162,8 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set
<Button onClick={() => setOpenEditDialog(false)} variant="outlined" color="primary" autoFocus> <Button onClick={() => setOpenEditDialog(false)} variant="outlined" color="primary" autoFocus>
بستن بستن
</Button> </Button>
<Button disabled={isSubmitting} type={"submit"} variant="contained" color="primary"> <Button form="edit_road_items" disabled={isSubmitting} type={"submit"} variant="contained"
color="primary">
{isSubmitting ? "در حال ویرایش" : "ویرایش"} {isSubmitting ? "در حال ویرایش" : "ویرایش"}
</Button> </Button>
</DialogActions> </DialogActions>

View File

@@ -1,18 +1,18 @@
import { Button, CircularProgress } from "@mui/material"; import {Button, CircularProgress} from "@mui/material";
import { useMemo, useState } from "react"; import {useMemo, useState} from "react";
import moment from "jalali-moment"; import moment from "jalali-moment";
import FileSaver from "file-saver"; import FileSaver from "file-saver";
import DescriptionIcon from "@mui/icons-material/Description"; import DescriptionIcon from "@mui/icons-material/Description";
import useRequest from "@/lib/hooks/useRequest"; import useRequest from "@/lib/hooks/useRequest";
import { EXPORT_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes"; import {EXPORT_ROAD_PATROL_OPERATOR_LIST} from "@/core/utils/routes";
const PrintExcel = ({ table, filterData }) => { const PrintExcel = ({table, filterData}) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const requestServer = useRequest(); const requestServer = useRequest();
const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => {
if (filter.value) { if (filter.value) {
// Check if there's an active filter // Check if there's an active filter
acc.push({ id: key, value: filter.value }); acc.push({id: key, value: filter.value});
} }
return acc; return acc;
}, []); }, []);
@@ -20,22 +20,23 @@ const PrintExcel = ({ table, filterData }) => {
const filterParams = useMemo(() => { const filterParams = useMemo(() => {
const params = new URLSearchParams(); const params = new URLSearchParams();
activeFilters.length > 0 && activeFilters.length > 0 &&
activeFilters.map((filter, index) => { activeFilters.map((filter, index) => {
params.set(`${filter.id}`, filter.value); params.set(`${filter.id}`, filter.value);
}); });
return params; return params;
}, [activeFilters]); }, [activeFilters]);
const clickHandler = () => { const clickHandler = () => {
setLoading(true); setLoading(true);
requestServer(`${EXPORT_ROAD_PATROL_OPERATOR_LIST}?${filterParams}`, "get", { requestServer(`${EXPORT_ROAD_PATROL_OPERATOR_LIST}?${filterParams}`, "get", {
requestOptions: { responseType: "blob" }, requestOptions: {responseType: "blob"},
}) })
.then((response) => { .then((response) => {
const filename = `خروجی کارتابل عملیات تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; const filename = `خروجی کارتابل عملیات تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`;
FileSaver.saveAs(response.data, filename); FileSaver.saveAs(response.data, filename);
}) })
.catch(() => {}) .catch(() => {
})
.finally(() => { .finally(() => {
setLoading(false); setLoading(false);
}); });
@@ -45,10 +46,9 @@ const PrintExcel = ({ table, filterData }) => {
<Button <Button
color="primary" color="primary"
variant="contained" variant="contained"
size="small"
disabled={loading} disabled={loading}
sx={{ textTransform: "unset", alignSelf: "center" }} sx={{textTransform: "unset", alignSelf: "center"}}
startIcon={loading ? <CircularProgress size={18} color="inherit" /> : <DescriptionIcon />} startIcon={loading ? <CircularProgress size={18} color="inherit"/> : <DescriptionIcon/>}
onClick={clickHandler} onClick={clickHandler}
> >
خروجی اکسل خروجی اکسل

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, {useState} from "react"; import React 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";
import { import {
@@ -10,21 +10,15 @@ import {
CardContent, CardContent,
CardHeader, CardHeader,
Chip, Chip,
Collapse,
Divider, Divider,
IconButton, IconButton,
Stack, Stack,
Typography Typography
} from "@mui/material"; } from "@mui/material";
import {styled} from '@mui/material/styles';
import HandymanIcon from '@mui/icons-material/Handyman'; import HandymanIcon from '@mui/icons-material/Handyman';
import DeleteIcon from "@mui/icons-material/Delete"; import DeleteIcon from "@mui/icons-material/Delete";
import moment from "jalali-moment";
import WatchLaterIcon from '@mui/icons-material/WatchLater';
import CalendarMonthIcon from "@mui/icons-material/CalendarMonth";
import DirectionsCarIcon from '@mui/icons-material/DirectionsCar'; import DirectionsCarIcon from '@mui/icons-material/DirectionsCar';
import PersonIcon from '@mui/icons-material/Person'; import PersonIcon from '@mui/icons-material/Person';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import DesignServicesIcon from '@mui/icons-material/DesignServices'; import DesignServicesIcon from '@mui/icons-material/DesignServices';
import EditActionDuringPatrol from "./EditActionDuringPatrol"; import EditActionDuringPatrol from "./EditActionDuringPatrol";
@@ -33,38 +27,7 @@ const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
ssr: false, ssr: false,
}); });
const ActionInfo = ({action, index, deleteAction}) => { const ActionInfo = ({action, setActionsList, index, deleteAction}) => {
const [expanded, setExpanded] = useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
const ExpandMore = styled((props) => {
const {expand, ...other} = props;
return <IconButton {...other} />;
})(({theme}) => ({
marginLeft: 'auto',
transition: theme.transitions.create('transform', {
duration: theme.transitions.duration.shortest,
}),
variants: [
{
props: ({expand}) => !expand,
style: {
transform: 'rotate(0deg)',
},
},
{
props: ({expand}) => !!expand,
style: {
transform: 'rotate(180deg)',
},
},
],
}));
return ( return (
<Card sx={{maxWidth: 300}}> <Card sx={{maxWidth: 300}}>
<CardHeader <CardHeader
@@ -76,54 +39,15 @@ const ActionInfo = ({action, index, deleteAction}) => {
/> />
<CardContent> <CardContent>
<Stack sx={{gap: 1}}> <Stack sx={{gap: 1}}>
<Box sx={{display: "flex", alignItems: "center", gap: 1}}>
<CalendarMonthIcon/>
<Typography variant="button">
تاریخ:
</Typography>
<Typography variant="button">
{moment(action.data.activity_date).locale("fa").format("YYYY/MM/DD")}
</Typography>
</Box>
<Box sx={{display: "flex", alignItems: "center", gap: 1}}>
<WatchLaterIcon/>
<Typography variant="button">
ساعت:
</Typography>
<Typography variant="button">
{action.data.activity_time}
</Typography>
</Box>
<Box sx={{display: "flex", alignItems: "center", gap: 1}}> <Box sx={{display: "flex", alignItems: "center", gap: 1}}>
<DesignServicesIcon/> <DesignServicesIcon/>
<Typography variant="button"> <Typography variant="button">
میزان فعالیت انجام شده: میزان فعالیت انجام شده:
</Typography> </Typography>
<Typography variant="button"> <Typography variant="button">
{action.data.amount} {action.data.amount}({action.data.unit_fa})
{/*({action.data.unit_fa})*/}
</Typography> </Typography>
</Box> </Box>
</Stack>
</CardContent>
<CardActions disableSpacing>
<Box>
<IconButton aria-label="حذف فعالیت" color="error" onClick={deleteAction}>
<DeleteIcon/>
</IconButton>
<EditActionDuringPatrol action={action}/>
</Box>
<ExpandMore
expand={expanded}
onClick={handleExpandClick}
aria-expanded={expanded}
aria-label="show more"
>
<ExpandMoreIcon/>
</ExpandMore>
</CardActions>
<Collapse in={expanded} timeout="auto" unmountOnExit sx={{p: 2}}>
<Stack sx={{gap: 1}}>
<Box> <Box>
<Divider sx={{mb: 1}}> <Divider sx={{mb: 1}}>
<Chip size="small" label="خودرو ها" icon={<DirectionsCarIcon/>}/> <Chip size="small" label="خودرو ها" icon={<DirectionsCarIcon/>}/>
@@ -149,7 +73,15 @@ const ActionInfo = ({action, index, deleteAction}) => {
</Stack> </Stack>
</Box> </Box>
</Stack> </Stack>
</Collapse> </CardContent>
<CardActions disableSpacing>
<Box>
<IconButton aria-label="حذف فعالیت" color="error" onClick={deleteAction}>
<DeleteIcon/>
</IconButton>
<EditActionDuringPatrol action={action} index={index} setActionsList={setActionsList}/>
</Box>
</CardActions>
</Card> </Card>
); );
}; };

View File

@@ -7,7 +7,7 @@ import ModalActionsDuringPatrol from "./ModalActionsDuringPatrol";
import ActionInfo from "./ActionInfo"; import ActionInfo from "./ActionInfo";
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
const ActionsDuringPatrol = ({tabState, setTabState}) => { const ActionsDuringPatrol = ({setValue, tabState, setTabState}) => {
const [ActionsList, setActionsList] = useState([]); const [ActionsList, setActionsList] = useState([]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const handleOpen = () => { const handleOpen = () => {
@@ -18,6 +18,13 @@ const ActionsDuringPatrol = ({tabState, setTabState}) => {
setActionsList((prev) => prev.filter((_, index) => index !== indexToRemove)); setActionsList((prev) => prev.filter((_, index) => index !== indexToRemove));
}; };
const SubmitActionDuringPatrol = () => {
setTabState(tabState + 1);
const resultItems = ActionsList.map(item => item.result);
console.log("resultItems", resultItems);
setValue("observed_items", resultItems);
}
return ( return (
<Box> <Box>
<Box sx={{display: "flex", alignItems: "center", justifyContent: "space-between"}}> <Box sx={{display: "flex", alignItems: "center", justifyContent: "space-between"}}>
@@ -40,6 +47,7 @@ const ActionsDuringPatrol = ({tabState, setTabState}) => {
<Grid key={index} item xs={12} sm={6} lg={4}> <Grid key={index} item xs={12} sm={6} lg={4}>
<ActionInfo <ActionInfo
action={action} action={action}
setActionsList={setActionsList}
index={index} index={index}
deleteAction={() => deleteAction(index)} deleteAction={() => deleteAction(index)}
/> />
@@ -55,7 +63,8 @@ const ActionsDuringPatrol = ({tabState, setTabState}) => {
} }
<Box> <Box>
<Box sx={{display: "flex", gap: 1, justifyContent: "center"}}> <Box sx={{display: "flex", gap: 1, justifyContent: "center"}}>
<Button variant="contained" color="primary" onClick={() => setTabState(tabState + 1)} <Button variant="contained" color="primary" onClick={SubmitActionDuringPatrol}
disabled={ActionsList === []}
endIcon={<KeyboardDoubleArrowLeftIcon/>} sx={{mt: 2}}> endIcon={<KeyboardDoubleArrowLeftIcon/>} sx={{mt: 2}}>
نهایی کردن درخواست نهایی کردن درخواست
</Button> </Button>

View File

@@ -0,0 +1,218 @@
"use client";
import React, {useEffect, useState} from "react";
import {
Box,
Button,
Chip,
Divider,
FormControl,
Grid,
InputLabel,
OutlinedInput,
Slide,
Stack,
Typography
} 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";
const CompeleteRequest = ({watch, isSubmitting, setValue, register}) => {
const requestServer = useRequest({auth: true});
const [delayPerRequest, setDelayPerRequest] = useState(false);
const [phoneNumber, setPhoneNumber] = useState("");
const [validPhone, setValidPhone] = useState(false);
const [verificationCode, setVerificationCode] = useState("");
const [counter, setCounter] = useState(120);
const [otpSended, setOtpSended] = useState(false);
const [readyToVerify, setReadyToVerify] = useState(false);
const handleNumericInput = (e) => {
e.target.value = e.target.value.replace(/[^0-9]/g, '');
};
useEffect(() => {
if (phoneNumber.length === 11) {
setValidPhone(true);
} else {
setValidPhone(false);
}
if (verificationCode.length === 6 && phoneNumber.length === 11) {
setReadyToVerify(true);
} else {
setReadyToVerify(false);
}
}, [phoneNumber, verificationCode]);
useEffect(() => {
if (counter === 0) {
setDelayPerRequest(false);
return;
}
if (delayPerRequest && counter > 0) {
const timer = setInterval(() => {
setCounter(prevCounter => prevCounter - 1);
}, 1000);
return () => clearInterval(timer);
}
}, [counter, delayPerRequest]);
const sendOtp = () => {
if (!validPhone) return false;
requestServer(`${GET_OTP_TOKEN}?phone_number=${phoneNumber}`, "get")
.then((response) => {
setOtpSended(true);
setDelayPerRequest(true);
})
.catch(() => {
});
};
return (
<Stack>
<Stack>
<Box sx={{display: "flex", alignItems: "center", justifyContent: "space-between"}}>
<Box>
<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><Chip label="خودرو گشت"/></Divider>
<Box sx={{display: "flex", alignItems: "center", my: 1}}>
<Typography>کد</Typography>
<Divider sx={{flex: 1, mx: 2}}/>
<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 sx={{display: "flex", alignItems: "center", my: 1}}>
<Typography>نام و کد راهدار</Typography>
<Divider sx={{flex: 1, mx: 2}}/>
<Typography>{rahdar.name} | {rahdar.code}</Typography>
</Box>
))}
</Grid>
</Grid>
<Box sx={{display: "flex", gap: 2, my: 2, justifyContent: "space-around"}}>
<Box sx={{display: "flex", alignItems: "center", gap: 2}}>
<FormControl size="small" fullWidth variant="outlined">
<InputLabel htmlFor="phone_number">شماره تلفن مامور گشت</InputLabel>
<OutlinedInput
id="phone_number"
label={"شماره تلفن مامور گشت"}
disabled={delayPerRequest}
autoComplete="off"
size="small"
fullWidth
{...register("phone_number")}
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
onInput={handleNumericInput}
type="text"
inputProps={{
maxLength: 11
}}
/>
</FormControl>
<Button onClick={sendOtp} disabled={!validPhone || delayPerRequest} variant="contained"
color="success"
sx={{textWrap: "nowrap", px: 4}}
endIcon={<ForwardToInboxIcon/>}>
{delayPerRequest ? formatCounter(counter) : "دریافت کد"}
</Button>
</Box>
<Slide in={otpSended}>
<Box>
<FormControl size="small" fullWidth variant="outlined" focused>
<InputLabel htmlFor="verification_code">کد پیامک شده</InputLabel>
<OutlinedInput
id="verification_code"
label={"کد پیامک شده"}
placeholder="------"
autoComplete="off"
size="small"
fullWidth
{...register("verification_code")}
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
onInput={handleNumericInput}
type="text"
inputProps={{
maxLength: 6,
inputMode: "numeric",
style: {
textAlign: "center",
letterSpacing: "10px",
},
}}
/>
</FormControl>
</Box>
</Slide>
</Box>
</Stack>
<Button
variant="contained"
size="large"
disabled={isSubmitting}
type={"submit"}
form="patrol_form"
endIcon={<BeenhereIcon/>}
>
{isSubmitting ? "در حال ثبت فعالیت" : "ثبت فعالیت"}
</Button>
</Stack>
);
};
export default CompeleteRequest;

View File

@@ -12,20 +12,25 @@ const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
ssr: false, ssr: false,
}); });
const EditActionDuringPatrol = ({action}) => { const EditActionDuringPatrol = ({action, index, setActionsList}) => {
console.log("action", action)
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const handleOpen = () => { const handleOpen = () => {
setOpen(true); setOpen(true);
}; };
const HandleSubmit = async (data, result) => { const HandleSubmit = async (newData) => {
setActionsList((prev) => [...prev, data]); setActionsList((prev) => {
const updatedList = [...prev];
updatedList[index] = {
...updatedList[index],
data: newData.data,
result: newData.result,
};
return updatedList;
});
setOpen(false); setOpen(false);
} };
return ( return (
<> <>

View File

@@ -10,7 +10,7 @@ const ModalActionsDuringPatrol = ({open, setOpen, setActionsList}) => {
return ( return (
<Dialog open={open} fullWidth maxWidth="sm"> <Dialog open={open} fullWidth maxWidth="sm">
<CreateFormContent setOpen={setOpen} onSubmit={HandleSubmit}/> <CreateFormContent setOpen={setOpen} onSubmit={HandleSubmit} is_gasht={true}/>
</Dialog> </Dialog>
); );
} }

View File

@@ -175,7 +175,8 @@ const PatrolDetail = ({control, watch, setValue, tabState, setTabState}) => {
<Chip color="success" label="مشخصات گشت"/> <Chip color="success" label="مشخصات گشت"/>
</Divider> </Divider>
{patrolResultStatus === 0 ? {patrolResultStatus === 0 ?
<PatrolDetailInfo patrolData={patrolData} tabState={tabState} setTabState={setTabState}/> : <PatrolDetailInfo patrolData={patrolData} tabState={tabState} setTabState={setTabState}
setValue={setValue}/> :
<PatrolResultCodeErrors ResultCode={patrolResultStatus}/>} <PatrolResultCodeErrors ResultCode={patrolResultStatus}/>}
</Box> </Box>
); );

View File

@@ -1,7 +1,7 @@
"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, Slide, Stack, Typography} from "@mui/material";
import React 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";
import LocalGasStationIcon from '@mui/icons-material/LocalGasStation'; import LocalGasStationIcon from '@mui/icons-material/LocalGasStation';
@@ -15,15 +15,46 @@ import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrow
import QrCode2Icon from '@mui/icons-material/QrCode2'; 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";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => <MapLoading/>, loading: () => <MapLoading/>,
ssr: false, ssr: false,
}); });
const PatrolDetailInfo = ({patrolData, tabState, setTabState}) => { const MapItemViewBound = ({patrolData, bound}) => {
const map = useMap();
useEffect(() => {
if (bound.length !== 0) {
map.fitBounds(bound,
{paddingTopLeft: [16, 16], paddingBottomRight: [16, 130]}
);
}
}, [bound]);
return (
<>
{patrolData.stopPoints.map((stopPoint, index) => (
<React.Fragment key={index}>
<PatrolMapFeatures stopPoint={stopPoint}/>
</React.Fragment>
))}
</>
);
};
const PatrolDetailInfo = ({patrolData, tabState, setTabState, setValue}) => {
const bound = patrolData.stopPoints.map(point => [point.latitude, point.longitude]); const bound = 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%"}}> <Slide in={true} sx={{width: "100%"}}>
<Card> <Card>
@@ -120,13 +151,7 @@ const PatrolDetailInfo = ({patrolData, tabState, setTabState}) => {
}} }}
> >
<MapLayer position={bound}> <MapLayer position={bound}>
{patrolData.stopPoints.map((stopPoint, index) => { <MapItemViewBound patrolData={patrolData} bound={bound}/>
return (
<React.Fragment key={index}>
<PatrolMapFeatures stopPoint={stopPoint}/>
</React.Fragment>
)
})}
</MapLayer> </MapLayer>
</Box> </Box>
</Box> </Box>
@@ -134,7 +159,7 @@ const PatrolDetailInfo = ({patrolData, tabState, setTabState}) => {
</CardContent> </CardContent>
<CardActions sx={{alignItems: "center", justifyContent: "center"}}> <CardActions sx={{alignItems: "center", justifyContent: "center"}}>
<Box sx={{display: "flex", gap: 1}}> <Box sx={{display: "flex", gap: 1}}>
<Button variant="contained" color="primary" onClick={() => setTabState(tabState + 1)} <Button variant="contained" color="primary" onClick={SendPatrolInfo}
endIcon={<KeyboardDoubleArrowLeftIcon/>} sx={{mt: 2}}> endIcon={<KeyboardDoubleArrowLeftIcon/>} sx={{mt: 2}}>
تایید اطلاعات و رفتن به مرحله بعد تایید اطلاعات و رفتن به مرحله بعد
</Button> </Button>

View File

@@ -11,11 +11,10 @@ import PatrolTimeLine from "./PatrolTimeLine";
import PatrolDetail from "./PatrolDetail"; import PatrolDetail from "./PatrolDetail";
import SuperVisorsDetail from "./SuperVisorsDetail"; import SuperVisorsDetail from "./SuperVisorsDetail";
import ActionsDuringPatrol from "./ActionsDurigPatrol"; import ActionsDuringPatrol from "./ActionsDurigPatrol";
import {array, object, string} from "yup";
import {useForm} from "react-hook-form"; import {useForm} from "react-hook-form";
import {yupResolver} from "@hookform/resolvers/yup";
import StyledForm from "@/core/components/StyledForm"; import StyledForm from "@/core/components/StyledForm";
import useRequest from "@/lib/hooks/useRequest"; 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;
@@ -36,26 +35,16 @@ const defaultValues = {
vehicle_runtime: "", vehicle_runtime: "",
fuel_consumption: "", fuel_consumption: "",
distance: "", distance: "",
observed_items: null observed_items: null,
phone_number: "",
otp_token: ""
}; };
const validationSchema = object({ const PatrolForms = ({mutate, setOpen}) => {
road_patrol_rahdaran_id: array().required("حداقل یک راهدار باید وارد کنید!"),
road_patrol_machines_id: array().required("وارد کردن کد خودرو الزامیست!").min(1, "وارد کردن کد خودرو الزامیست!"),
start_time: string().required("لطفا زمان شروع گشت را انتخاب کنید!"),
end_time: string().required("لطفا زمان پایان گشت را انتخاب کنید!"),
stop_points: array().required("وجود نقاط توقف اجباری است!").min(1, "حداقل یک نقطه توقف باید وجود داشته باشد!"),
vehicle_runtime: string().required("وجود میزان زمان روشن بودن خودرو الزامیست!"),
fuel_consumption: string().required("وجود مقدار سوخت مصرفی الزامیست!"),
distance: string().required("وارد کردن مقدار الزامیست!"),
observed_items: array().required("وجود نقاط توقف اجباری است!").min(1, "حداقل یک نقطه توقف باید وجود داشته باشد!"),
});
const PatrolForms = ({mutate}) => {
const requestServer = useRequest({notificationSuccess: true}); const requestServer = useRequest({notificationSuccess: true});
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("md")); const isMobile = useMediaQuery(theme.breakpoints.down("md"));
const [tabState, setTabState] = useState(2); const [tabState, setTabState] = useState(0);
const [activeUpTo, setActiveUpTo] = useState(0); const [activeUpTo, setActiveUpTo] = useState(0);
const handleChangeTab = (event, newValue) => { const handleChangeTab = (event, newValue) => {
@@ -76,37 +65,55 @@ const PatrolForms = ({mutate}) => {
handleSubmit, handleSubmit,
setValue, setValue,
resetField, resetField,
formState: {errors, isSubmitting}, formState: {isSubmitting},
} = useForm({ } = useForm({
defaultValues, defaultValues,
resolver: yupResolver(validationSchema),
mode: "onBlur" mode: "onBlur"
}); });
const onSubmit = async (data) => { const onSubmit = async (data) => {
console.log("data", data); console.log("data", data)
const formData = new FormData(); // const formData = new FormData();
// formData.append("road_patrol_rahdaran_id", myData); // data.road_patrol_rahdaran_id.forEach((rahdar, index) => formData.append(`road_patrol_rahdaran_id[${index}]`, rahdar.id));
// formData.append("road_patrol_machines_id", myData); // formData.append(`road_patrol_machines_id[0]`, data.road_patrol_machines_id.id)
// formData.append("start_time", moment(new Date(myData)).format("YYYY-MM-DD")); // data.stop_points.forEach((stop_point, index) => formData.append(`stop_points[${index}]`, stop_point))
// formData.append("end_time", moment(new Date(myData)).format("YYYY-MM-DD")); // data.observed_items.forEach((observed_item, index) => {
// formData.append("stop_points", myData); // formData.append(`observed_items[${index}][instant_action]`, 1);
// formData.append("vehicle_runtime", myData); // formData.append(`observed_items[${index}][local_name]`, "");
// formData.append("fuel_consumption", myData); // formData.append(`observed_items[${index}][start_point]`, observed_item.start_point);
// formData.append("distance", myData); // formData.append(`observed_items[${index}][start_point]`, observed_item.end_point);
// formData.append("observed_items", myData); // formData.append(`observed_items[${index}][amount]`, observed_item.amount);
// try { // formData.append(`observed_items[${index}][before_image]`, observed_item.before_image);
// await requestServer(CREATE_PATROL, "post", { // formData.append(`observed_items[${index}][after_image]`, observed_item.after_image);
// data: formData, // 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))
// mutate(); // formData.append(`observed_items[${index}][sub_item_id]`, observed_item.sub_item_id);
// handleClose(); // })
// } catch (error) { // 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)}> <StyledForm onSubmit={handleSubmit(onSubmit)} id="patrol_form">
<Tabs <Tabs
allowScrollButtonsMobile allowScrollButtonsMobile
value={tabState} value={tabState}
@@ -131,7 +138,6 @@ const PatrolForms = ({mutate}) => {
control={control} control={control}
setValue={setValue} setValue={setValue}
watch={watch} watch={watch}
errors={errors}
tabState={tabState} tabState={tabState}
setTabState={setTabState} setTabState={setTabState}
/> />
@@ -141,15 +147,24 @@ const PatrolForms = ({mutate}) => {
control={control} control={control}
setValue={setValue} setValue={setValue}
watch={watch} watch={watch}
errors={errors}
tabState={tabState} tabState={tabState}
setTabState={setTabState}/> setTabState={setTabState}/>
</TabPanel> </TabPanel>
<TabPanel value={tabState} index={2}> <TabPanel value={tabState} index={2}>
<ActionsDuringPatrol tabState={tabState} setTabState={setTabState}/> <ActionsDuringPatrol
watch={watch}
setValue={setValue}
tabState={tabState}
setTabState={setTabState}
/>
</TabPanel> </TabPanel>
<TabPanel value={tabState} index={3}> <TabPanel value={tabState} index={3}>
<>taiidie</> <CompeleteRequest
register={register}
setValue={setValue}
watch={watch}
isSubmitting={isSubmitting}
/>
</TabPanel> </TabPanel>
</Box> </Box>
{!isMobile && ( {!isMobile && (

View File

@@ -1,7 +1,6 @@
"use client"; "use client";
import {Box, CircularProgress, Typography} from "@mui/material"; import {Box, CircularProgress, Typography} from "@mui/material";
import TapAndPlayIcon from '@mui/icons-material/TapAndPlay';
import TaxiAlertIcon from '@mui/icons-material/TaxiAlert'; 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';
@@ -17,8 +16,6 @@ import {
} from "@mui/lab"; } from "@mui/lab";
const PatrolTimeLine = ({tabState}) => { const PatrolTimeLine = ({tabState}) => {
console.log("tabState", tabState)
return ( return (
<Box sx={{display: "flex", alignItems: "center"}}> <Box sx={{display: "flex", alignItems: "center"}}>
<Timeline <Timeline
@@ -31,31 +28,13 @@ const PatrolTimeLine = ({tabState}) => {
<TimelineSeparator> <TimelineSeparator>
<TimelineConnector/> <TimelineConnector/>
<TimelineDot sx={{backgroundColor: "#fff"}}> <TimelineDot sx={{backgroundColor: "#fff"}}>
{tabState === 0 ? <CircularProgress size="23px"/> : <TapAndPlayIcon {tabState === 0 ? <CircularProgress size="23px"/> : <TaxiAlertIcon
color={tabState > 0 ? "success" : "error"} color={tabState > 0 ? "success" : "error"}
sx={{width: "1.5rem", height: "1.5rem"}} sx={{width: "1.5rem", height: "1.5rem"}}
/>} />}
</TimelineDot> </TimelineDot>
<TimelineConnector/> <TimelineConnector/>
</TimelineSeparator> </TimelineSeparator>
<TimelineContent sx={{py: "12px", px: 2}}>
<Typography variant="h6">تایید هویت</Typography>
<Typography variant="caption" sx={{color: "#606060"}}>
تایید هویت با شماره همراه
</Typography>
</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineSeparator>
<TimelineConnector/>
<TimelineDot sx={{backgroundColor: "#fff"}}>
{tabState === 1 ? <CircularProgress size="23px"/> : <TaxiAlertIcon
color={tabState > 1 ? "success" : "error"}
sx={{width: "1.5rem", height: "1.5rem"}}
/>}
</TimelineDot>
<TimelineConnector/>
</TimelineSeparator>
<TimelineContent sx={{py: "12px", px: 2}}> <TimelineContent sx={{py: "12px", px: 2}}>
<Typography variant="h6">مشخصات گشت</Typography> <Typography variant="h6">مشخصات گشت</Typography>
<Typography variant="caption" sx={{color: "#606060"}}> <Typography variant="caption" sx={{color: "#606060"}}>
@@ -67,8 +46,8 @@ const PatrolTimeLine = ({tabState}) => {
<TimelineSeparator> <TimelineSeparator>
<TimelineConnector/> <TimelineConnector/>
<TimelineDot sx={{backgroundColor: "#fff"}}> <TimelineDot sx={{backgroundColor: "#fff"}}>
{tabState === 2 ? <CircularProgress size="23px"/> : <EngineeringIcon {tabState === 1 ? <CircularProgress size="23px"/> : <EngineeringIcon
color={tabState > 2 ? "success" : "error"} color={tabState > 1 ? "success" : "error"}
sx={{width: "1.5rem", height: "1.5rem"}} sx={{width: "1.5rem", height: "1.5rem"}}
/>} />}
</TimelineDot> </TimelineDot>
@@ -77,7 +56,7 @@ const PatrolTimeLine = ({tabState}) => {
<TimelineContent sx={{py: "12px", px: 2}}> <TimelineContent sx={{py: "12px", px: 2}}>
<Typography variant="h6">مشخصات راهداران</Typography> <Typography variant="h6">مشخصات راهداران</Typography>
<Typography variant="caption" sx={{color: "#606060"}}> <Typography variant="caption" sx={{color: "#606060"}}>
راهدار / راهداران حاظر در گشت راهدار / راهداران حاضر در گشت
</Typography> </Typography>
</TimelineContent> </TimelineContent>
</TimelineItem> </TimelineItem>
@@ -85,8 +64,8 @@ const PatrolTimeLine = ({tabState}) => {
<TimelineSeparator> <TimelineSeparator>
<TimelineConnector/> <TimelineConnector/>
<TimelineDot sx={{backgroundColor: "#fff"}}> <TimelineDot sx={{backgroundColor: "#fff"}}>
{tabState === 3 ? <CircularProgress size="23px"/> : <HandymanIcon {tabState === 2 ? <CircularProgress size="23px"/> : <HandymanIcon
color={tabState > 3 ? "success" : "error"} color={tabState > 2 ? "success" : "error"}
sx={{width: "1.5rem", height: "1.5rem"}} sx={{width: "1.5rem", height: "1.5rem"}}
/>} />}
</TimelineDot> </TimelineDot>
@@ -103,8 +82,8 @@ const PatrolTimeLine = ({tabState}) => {
<TimelineSeparator> <TimelineSeparator>
<TimelineConnector/> <TimelineConnector/>
<TimelineDot sx={{backgroundColor: "#fff"}}> <TimelineDot sx={{backgroundColor: "#fff"}}>
{tabState === 4 ? <CircularProgress size="23px"/> : <VerifiedIcon {tabState === 3 ? <CircularProgress size="23px"/> : <VerifiedIcon
color={tabState > 4 ? "success" : "error"} color={tabState > 3 ? "success" : "error"}
sx={{width: "1.5rem", height: "1.5rem"}} sx={{width: "1.5rem", height: "1.5rem"}}
/>} />}
</TimelineDot> </TimelineDot>

View File

@@ -1,9 +1,8 @@
"use client"; "use client";
import {Box, Button, FormControl, InputLabel, OutlinedInput, Slide} from "@mui/material"; import {Box, Button, FormControl, InputLabel, OutlinedInput} from "@mui/material";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import ForwardToInboxIcon from '@mui/icons-material/ForwardToInbox'; import ForwardToInboxIcon from '@mui/icons-material/ForwardToInbox';
import QrCodeIcon from '@mui/icons-material/QrCode';
import useRequest from "@/lib/hooks/useRequest"; import useRequest from "@/lib/hooks/useRequest";
import {GET_OTP_TOKEN, VERIFY_OTP} from "@/core/utils/routes"; import {GET_OTP_TOKEN, VERIFY_OTP} from "@/core/utils/routes";
import {formatCounter} from "@/core/utils/formatCounter"; import {formatCounter} from "@/core/utils/formatCounter";
@@ -53,7 +52,6 @@ const PhoneValidation = ({tabState, setTabState}) => {
if (!validPhone) return false; if (!validPhone) return false;
requestServer(`${GET_OTP_TOKEN}?phone_number=${phoneNumber}`, "get") requestServer(`${GET_OTP_TOKEN}?phone_number=${phoneNumber}`, "get")
.then((response) => { .then((response) => {
console.log("request sent");
}) })
.catch(() => { .catch(() => {
}); });
@@ -67,9 +65,6 @@ const PhoneValidation = ({tabState, setTabState}) => {
formData.append("phone_number", phoneNumber); formData.append("phone_number", phoneNumber);
formData.append("verification_code", verificationCode); formData.append("verification_code", verificationCode);
for (var pair of formData.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
setTabState(tabState + 1) setTabState(tabState + 1)
requestServer(VERIFY_OTP, "post", { requestServer(VERIFY_OTP, "post", {
data: formData, data: formData,
@@ -113,37 +108,7 @@ const PhoneValidation = ({tabState, setTabState}) => {
{delayPerRequest ? formatCounter(counter) : "دریافت کد"} {delayPerRequest ? formatCounter(counter) : "دریافت کد"}
</Button> </Button>
</Box> </Box>
<Slide in={otpSended} sx={{my: 5, width: {xs: "100%", sm: "70%"}}}>
<Box>
<FormControl size="small" fullWidth variant="outlined" focused>
<InputLabel htmlFor="otp_code">کد پیامک شده</InputLabel>
<OutlinedInput
id="otp_code"
label={"کد پیامک شده"}
placeholder="------"
autoComplete="off"
size="small"
fullWidth
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
onInput={handleNumericInput}
type="text"
inputProps={{
maxLength: 6,
inputMode: "numeric",
style: {
textAlign: "center",
letterSpacing: "10px",
},
}}
/>
</FormControl>
<Button onClick={phoneRegister} disabled={!readyToVerify} fullWidth variant="contained"
endIcon={<QrCodeIcon/>} sx={{mt: 2}}>
بررسی کد و رفتن به مرحله بعد
</Button>
</Box>
</Slide>
</Box> </Box>
); );
}; };

View File

@@ -27,6 +27,11 @@ const SuperVisorsDetail = ({control, watch, setValue, tabState, setTabState}) =>
setReadyToRequest(watch("road_patrol_rahdaran_id") != null); setReadyToRequest(watch("road_patrol_rahdaran_id") != null);
}, [watch("road_patrol_rahdaran_id")]); }, [watch("road_patrol_rahdaran_id")]);
const SendSuperVisor = () => {
setValue("road_patrol_rahdaran_id", SuperVisorList);
setTabState(tabState + 1);
}
return ( return (
<Box <Box
sx={{ sx={{
@@ -44,7 +49,6 @@ const SuperVisorsDetail = ({control, watch, setValue, tabState, setTabState}) =>
<Controller <Controller
control={control} control={control}
render={({field, fieldState: {error}}) => { render={({field, fieldState: {error}}) => {
console.log("field.value", field.value)
return ( return (
<RahdarCode <RahdarCode
rahdarsCode={field.value} rahdarsCode={field.value}
@@ -107,7 +111,7 @@ const SuperVisorsDetail = ({control, watch, setValue, tabState, setTabState}) =>
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
onClick={() => setTabState(tabState + 1)} onClick={SendSuperVisor}
endIcon={<KeyboardDoubleArrowLeftIcon/>} endIcon={<KeyboardDoubleArrowLeftIcon/>}
sx={{mt: 2}} sx={{mt: 2}}
> >

View File

@@ -36,6 +36,7 @@ export const EXPORT_ROAD_PATROL_SUPERVISOR_LIST = "https://rms.witel.ir/v2/road_
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 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";
// road items // road items
export const GET_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_index"; export const GET_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_index";