Feature/abniye fani
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import WithPermission from "@/core/middlewares/withPermission";
|
||||
import ActivityCodeLog from "@/core/components/ActivityCodeLog";
|
||||
import OperatorPage from "@/components/dashboard/technicalBuildingDamage/operator";
|
||||
const Page = () => {
|
||||
return (
|
||||
<WithPermission permission_name={["all"]}>
|
||||
<OperatorPage />
|
||||
{/*<ActivityCodeLog activity_code={1152} /> change permission */}
|
||||
</WithPermission>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,28 @@
|
||||
import SelectBox from "@/core/components/SelectBox";
|
||||
import { Controller, useWatch } from "react-hook-form";
|
||||
import useEdaratLists from "@/lib/hooks/useEdaratLists";
|
||||
|
||||
const CityController = ({ control }) => {
|
||||
const provinceID = useWatch({ control, name: "province_id" });
|
||||
const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(provinceID);
|
||||
return (
|
||||
<Controller
|
||||
name="city_id"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<SelectBox
|
||||
value={!loadingEdaratList ? field.value || "" : ""}
|
||||
label={provinceID ? "شهرستان" : "ابتدا استان را انتخاب کنید"}
|
||||
selectors={edaratList}
|
||||
schema={{ name: "name_fa", value: "id" }}
|
||||
error={error}
|
||||
onChange={field.onChange}
|
||||
helperText={error?.message}
|
||||
isLoading={loadingEdaratList}
|
||||
errorEcured={errorEdaratList}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default CityController;
|
||||
@@ -0,0 +1,232 @@
|
||||
import React, { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { array, mixed, number, object, string } from "yup";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material";
|
||||
import StyledForm from "@/core/components/StyledForm";
|
||||
import FileCopyIcon from "@mui/icons-material/FileCopy";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
||||
import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight";
|
||||
import BeenhereIcon from "@mui/icons-material/Beenhere";
|
||||
import MinorCrashIcon from "@mui/icons-material/MinorCrash";
|
||||
import DamageInfo from "./DamageInfo";
|
||||
import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft";
|
||||
import DamageReport from "./DamageReport";
|
||||
import DamageItem from "./DamageItem";
|
||||
|
||||
function TabPanel(props) {
|
||||
const { children, value, index } = props;
|
||||
return (
|
||||
<div role="tabpanel" hidden={value !== index}>
|
||||
{value === index && <Box>{children}</Box>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const validationSchema = object({
|
||||
accident_date: string().required("لطفا تاریخ تصادف را وارد کنید!!!"),
|
||||
accident_time: string().required("لطفا زمان تصادف را وارد کنید!!!"),
|
||||
accident_type: string().required("لطفا نوع تصادف را وارد کنید!!!"),
|
||||
province_id: number().required("لطفا استان را وارد کنید!!!"),
|
||||
city_id: number().required("لطفا شهرستان را وارد کنید!!!"),
|
||||
axis_name: string().required("لطفا نام محور را وارد کنید!!!"),
|
||||
driver_name: string().required("لطفا نام و نام خانوادگی را وارد کنید!!!"),
|
||||
phone_number: mixed()
|
||||
.test("max", "تلفن همراه باید شامل 11 رقم باشد", (value) => {
|
||||
const stringValue = String(value);
|
||||
return stringValue.length === 11;
|
||||
})
|
||||
.required("لطفا تلفن همراه را وارد کنید!!!"),
|
||||
national_code: mixed()
|
||||
.test("max", "کد ملی باید شامل 10 رقم باشد", (value) => value.toString().length === 10)
|
||||
.required("لطفا کد ملی را وارد کنید!!!"),
|
||||
start_point: mixed()
|
||||
.test("start-point-required", "لطفاً نقطه تصادف را مشخص کنید!", function (value) {
|
||||
return !!value; // چک میکند که مقدار موجود است
|
||||
})
|
||||
.required("لطفاً نقطه تصادف را مشخص کنید!"),
|
||||
plate_part1: mixed()
|
||||
.test("max", "2رقم", (value) => value.toString().length === 2)
|
||||
.required("plate_part1 الزامیست"),
|
||||
plate_part2: mixed().required("plate_part2 الزامیست"),
|
||||
plate_part3: mixed()
|
||||
.test("max", "3رقم", (value) => value.toString().length === 3)
|
||||
.required("plate_part3الزامیست"),
|
||||
plate_part4: mixed()
|
||||
.test("max", "2رقم", (value) => value.toString().length === 2)
|
||||
.required("plate_part4الزامیست"),
|
||||
damage_picture1: mixed().nullable().required("لطفا عکس تصادف را بارگذاری کنید!"),
|
||||
damage_picture2: mixed().nullable().required("لطفا عکس تصادف را بارگذاری کنید!"),
|
||||
police_serial: string().when("police_file_checkbox", {
|
||||
is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد
|
||||
then: (schema) => schema.required("شماره کروکی یا نامه پلیس راه الزامی است"),
|
||||
}),
|
||||
police_file_date: string().when("police_file_checkbox", {
|
||||
is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد
|
||||
then: (schema) => schema.required("تاریخ کروکی یا نامه پلیس راه الزامی است"),
|
||||
}),
|
||||
police_file: string().when("police_file_checkbox", {
|
||||
is: "police_file_checkbox", // زمانی که گزینه "police_file_checkbox" انتخاب شده باشد
|
||||
then: (schema) => schema.required("تصویر کروکی یا نامه پلیس راه الزامی است"),
|
||||
}),
|
||||
items_damage: array().min(1, "حداقل یک ایتم خسارت ضروریست!!!"),
|
||||
});
|
||||
|
||||
const CreateFormContent = ({ setOpen, mutate }) => {
|
||||
const defaultValues = {
|
||||
accident_date: "",
|
||||
accident_time: "",
|
||||
accident_type: "",
|
||||
province_id: null,
|
||||
city_id: null,
|
||||
axis_name: "",
|
||||
driver_name: "",
|
||||
phone_number: "",
|
||||
national_code: "",
|
||||
plate_part1: "",
|
||||
plate_part2: "الف",
|
||||
plate_part3: "",
|
||||
plate_part4: "",
|
||||
damage_picture1: null,
|
||||
damage_picture2: null,
|
||||
report_base: "",
|
||||
police_file_checkbox: "",
|
||||
police_file: "",
|
||||
police_file_date: "",
|
||||
police_serial: "",
|
||||
start_point: "",
|
||||
items_damage: [],
|
||||
};
|
||||
|
||||
const [tabState, setTabState] = useState(1);
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||
const handleClose = () => setOpen(false);
|
||||
const handleChangeTab = (event, newValue) => setTabState(newValue);
|
||||
const handlePrev = () => {
|
||||
if (tabState === 0) {
|
||||
handleClose();
|
||||
} else {
|
||||
setTabState(tabState - 1);
|
||||
}
|
||||
};
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
trigger,
|
||||
formState: { errors, isSubmitting, isValid },
|
||||
} = useForm({
|
||||
defaultValues,
|
||||
resolver: yupResolver(validationSchema),
|
||||
mode: "all",
|
||||
});
|
||||
const onSubmitBase = async (data) => {
|
||||
console.log(data);
|
||||
};
|
||||
const handleNext = async () => {
|
||||
let fieldsToValidate = [];
|
||||
if (tabState === 0) {
|
||||
fieldsToValidate = [
|
||||
"phone_number",
|
||||
"start_point",
|
||||
"plate_part1",
|
||||
"plate_part2",
|
||||
"plate_part3",
|
||||
"plate_part4",
|
||||
"accident_date",
|
||||
"accident_type",
|
||||
"national_code",
|
||||
"accident_time",
|
||||
"province_id",
|
||||
"city_id",
|
||||
"axis_name",
|
||||
"driver_name",
|
||||
"damage_picture1",
|
||||
"damage_picture2",
|
||||
];
|
||||
} else if (tabState === 1) {
|
||||
fieldsToValidate = ["report_base", "police_file", "police_file_date", "police_serial"];
|
||||
} else if (tabState === 2) {
|
||||
fieldsToValidate = ["items_damage"];
|
||||
}
|
||||
const isValid = await trigger(fieldsToValidate);
|
||||
if (isValid) {
|
||||
setTabState(tabState + 1);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<StyledForm onSubmit={handleSubmit(onSubmitBase)}>
|
||||
<Tabs
|
||||
allowScrollButtonsMobile
|
||||
value={tabState}
|
||||
onChange={handleChangeTab}
|
||||
variant={`${isMobile ? "scrollable" : "fullWidth"}`}
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
backgroundColor: "#efefef",
|
||||
}}
|
||||
>
|
||||
<Tab icon={<InfoIcon />} label="اطلاعات تصادف" />
|
||||
<Tab disabled={tabState === 0} icon={<FileCopyIcon />} label="گزارش میزان خسارت" />
|
||||
<Tab disabled={tabState === 0 || tabState === 1} icon={<MinorCrashIcon />} label="ایتم خسارت" />
|
||||
</Tabs>
|
||||
<DialogContent dividers sx={{ overflowY: "auto", maxHeight: "70vh" }}>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<TabPanel value={tabState} index={0}>
|
||||
<DamageInfo setValue={setValue} errors={errors} control={control} />
|
||||
</TabPanel>
|
||||
<TabPanel value={tabState} index={1}>
|
||||
<DamageReport setValue={setValue} control={control} />
|
||||
</TabPanel>
|
||||
<TabPanel value={tabState} index={2}>
|
||||
<Controller
|
||||
name={"items_damage"}
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return <DamageItem baseOnChange={field.onChange} />;
|
||||
}}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ alignItems: "center", justifyContent: "center" }}>
|
||||
<Button
|
||||
onClick={handlePrev}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="large"
|
||||
startIcon={tabState === 0 ? <ExitToAppIcon /> : <KeyboardDoubleArrowRightIcon />}
|
||||
>
|
||||
{tabState === 0 ? "بستن" : "مرحله قبل"}
|
||||
</Button>
|
||||
{tabState !== 2 ? (
|
||||
<Button
|
||||
onClick={handleNext}
|
||||
variant="contained"
|
||||
size="large"
|
||||
endIcon={<KeyboardDoubleArrowLeftIcon />}
|
||||
>
|
||||
مرحله بعد
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
disabled={!isValid || isSubmitting}
|
||||
type={"submit"}
|
||||
endIcon={!isValid ? null : <BeenhereIcon />}
|
||||
>
|
||||
{!isValid ? "حداقل یک ایتم خسارت ضروریست" : isSubmitting ? "در حال ثبت خسارت" : "ثبت خسارت"}
|
||||
</Button>
|
||||
)}
|
||||
</DialogActions>
|
||||
</StyledForm>
|
||||
);
|
||||
};
|
||||
export default CreateFormContent;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Controller, useWatch } from "react-hook-form";
|
||||
import { Grid } from "@mui/material";
|
||||
import NumberField from "@/core/components/NumberField";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const DamageAmount = ({ control, damageItemList, setValue }) => {
|
||||
const itemDamage = useWatch({ control, name: "items_damge_id" });
|
||||
const itemValue = useWatch({ control, name: "items_damge_value" });
|
||||
const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage);
|
||||
const isDisabled = itemDamageId?.base_price ? itemDamageId?.base_price !== 0 : false;
|
||||
const calculatedValue = itemValue * (itemDamageId?.base_price ? itemDamageId?.base_price : 0);
|
||||
useEffect(() => {
|
||||
if (isDisabled) {
|
||||
setValue("items_damge_amount", calculatedValue);
|
||||
}
|
||||
}, [itemValue, itemDamageId, isDisabled, setValue]);
|
||||
return (
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller
|
||||
name="items_damge_amount"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<NumberField
|
||||
value={isDisabled ? calculatedValue : field.value || ""}
|
||||
variant="outlined"
|
||||
name="items_damge_amount"
|
||||
inputProps={{
|
||||
inputMode: "number",
|
||||
min: 0,
|
||||
pattern: "[0-9]*",
|
||||
}}
|
||||
onChange={(event) => {
|
||||
const inputValue = event.target.value;
|
||||
if (isNaN(Number(inputValue))) {
|
||||
return;
|
||||
}
|
||||
if (!isDisabled) {
|
||||
field.onChange(inputValue);
|
||||
}
|
||||
}}
|
||||
label={`هزینه خسارت (ریال)`}
|
||||
fullWidth
|
||||
size="small"
|
||||
disabled={isDisabled || itemDamage === null}
|
||||
error={error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
export default DamageAmount;
|
||||
@@ -0,0 +1,273 @@
|
||||
import { Controller } from "react-hook-form";
|
||||
import { Grid, Stack, TextField } from "@mui/material";
|
||||
import MuiDatePicker from "@/core/components/MuiDatePicker";
|
||||
import MuiTimePicker from "@/core/components/MuiTimePicker";
|
||||
import SelectBox from "@/core/components/SelectBox";
|
||||
import useProvinces from "@/lib/hooks/useProvince";
|
||||
import PlateNumber from "@/core/components/PlateNumber";
|
||||
import ImageUpload from "./ImageUpload";
|
||||
import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
|
||||
import CityController from "@/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CityController";
|
||||
|
||||
const DamageInfo = ({ control, setValue, errors }) => {
|
||||
const { provinces, errorProvinces, loadingProvinces } = useProvinces();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Grid sx={{ mt: 1 }} container spacing={2}>
|
||||
<Grid item xs={12} md={4}>
|
||||
<Controller
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<MuiDatePicker
|
||||
name="accident_date"
|
||||
error={error}
|
||||
value={field.value}
|
||||
placeholder={"تاریخ تصادف را وارد کنید"}
|
||||
label={"تاریخ تصادف"}
|
||||
setFieldValue={(name, value) => field.onChange(value || [])}
|
||||
helperText={error ? error.message : null}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
name={"accident_date"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<Controller
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<MuiTimePicker
|
||||
name="accident_time"
|
||||
error={error}
|
||||
value={field.value}
|
||||
placeholder={"زمان تصادف را وارد کنید"}
|
||||
label={"زمان تصادف"}
|
||||
setFieldValue={(name, value) => field.onChange(value || null)}
|
||||
helperText={error ? error.message : null}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
name={"accident_time"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<Controller
|
||||
name="accident_type"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<SelectBox
|
||||
value={field.value}
|
||||
label="نوع خسارت"
|
||||
selectors={[
|
||||
{
|
||||
id: 1,
|
||||
value: "1",
|
||||
name: "خسارتی",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
value: "2",
|
||||
name: "جرحی",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
value: "3",
|
||||
name: "فوتی",
|
||||
},
|
||||
]}
|
||||
schema={{ name: "name", value: "value" }}
|
||||
error={error}
|
||||
onChange={field.onChange}
|
||||
helperText={error?.message}
|
||||
isLoading={false}
|
||||
errorEcured={false}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid sx={{ mt: 2 }} container spacing={2}>
|
||||
<Grid item xs={12} md={4}>
|
||||
<Controller
|
||||
name="province_id"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<SelectBox
|
||||
value={!loadingProvinces ? field.value || "" : ""}
|
||||
label="اداره کل(استان)"
|
||||
selectors={provinces}
|
||||
schema={{ name: "name_fa", value: "id" }}
|
||||
error={error}
|
||||
onChange={field.onChange}
|
||||
helperText={error?.message}
|
||||
isLoading={loadingProvinces}
|
||||
errorEcured={errorProvinces}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<CityController control={control} />
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<Controller
|
||||
name="axis_name"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
value={field.value}
|
||||
variant="outlined"
|
||||
name="axis_name"
|
||||
onChange={field.onChange}
|
||||
label="نام محور"
|
||||
placeholder={"نام محور را وارد کنید"}
|
||||
fullWidth
|
||||
size="small"
|
||||
error={error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid sx={{ mt: 2 }} container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Controller
|
||||
name="driver_name"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
value={field.value}
|
||||
variant="outlined"
|
||||
name="driver_name"
|
||||
onChange={field.onChange}
|
||||
label="نام و نام خانوادگی راننده"
|
||||
placeholder={"نام و نام خانوادگی راننده را وارد کنید"}
|
||||
fullWidth
|
||||
size="small"
|
||||
error={error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Controller
|
||||
name="national_code"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
value={field.value}
|
||||
variant="outlined"
|
||||
name="national_code"
|
||||
type={"tel"}
|
||||
inputProps={{
|
||||
inputMode: "number",
|
||||
min: 0,
|
||||
pattern: "[0-9]*",
|
||||
}}
|
||||
onChange={(event) => {
|
||||
const inputValue = event.target.value;
|
||||
if (isNaN(Number(inputValue))) {
|
||||
return;
|
||||
}
|
||||
field.onChange(inputValue);
|
||||
}}
|
||||
label="کد ملی"
|
||||
placeholder={"کد ملی را وارد کنید"}
|
||||
fullWidth
|
||||
size="small"
|
||||
error={error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid sx={{ mt: 2 }} container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Controller
|
||||
name="phone_number"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
value={field.value}
|
||||
variant="outlined"
|
||||
name="phone_number"
|
||||
type={"tel"}
|
||||
inputProps={{
|
||||
inputMode: "number",
|
||||
min: 0,
|
||||
pattern: "[0-9]*",
|
||||
}}
|
||||
onChange={(event) => {
|
||||
const inputValue = event.target.value;
|
||||
if (isNaN(Number(inputValue))) {
|
||||
return;
|
||||
}
|
||||
field.onChange(inputValue);
|
||||
}}
|
||||
label="شماره تماس"
|
||||
placeholder={"شماره تماس را وارد کنید"}
|
||||
fullWidth
|
||||
size="small"
|
||||
error={error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<PlateNumber control={control} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid sx={{ mt: 2 }} container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Controller
|
||||
name="damage_picture1"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<ImageUpload
|
||||
name={field.name}
|
||||
value={field.value}
|
||||
error={error}
|
||||
onChange={field.onChange}
|
||||
title={"تصویر تصادف"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Controller
|
||||
name="damage_picture2"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<ImageUpload
|
||||
name={field.name}
|
||||
value={field.value}
|
||||
error={error}
|
||||
onChange={field.onChange}
|
||||
title={"تصویر تصادف"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Stack sx={{ mt: 2 }}>
|
||||
<MapInfoOneMarker
|
||||
setValue={setValue}
|
||||
errors={errors}
|
||||
title={"برای ثبت محل تصادف، لطفاً نقشه را بیشتر زوم کنید."}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default DamageInfo;
|
||||
@@ -0,0 +1,136 @@
|
||||
import { Autocomplete, Box, Button, Chip, Divider, Fade, Grid, Stack, TextField, Typography } from "@mui/material";
|
||||
import { Controller, useController, useForm } from "react-hook-form";
|
||||
import LinearProgress from "@mui/material/LinearProgress";
|
||||
import { useEffect, useState } from "react";
|
||||
import DamageItemInfo from "./DamageItemInfo";
|
||||
import { number, object } from "yup";
|
||||
import useDamageItemList from "@/lib/hooks/useDamageItemList";
|
||||
import DamagePrice from "./DamagePrice";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
|
||||
const schema = object({
|
||||
items_damge_id: number().required("لطفا نوع خسارت را وارد کنید!!!"),
|
||||
items_damge_value: number().required("لطفا میزان خسارت را وارد کنید!!!"),
|
||||
items_damge_amount: number().required("لطفا هزینه خسارت را وارد کنید!!!"),
|
||||
});
|
||||
const defaultValues = {
|
||||
items_damge_id: null,
|
||||
items_damge_value: null,
|
||||
items_damge_amount: null,
|
||||
};
|
||||
const DamageItem = ({ baseOnChange, getValues }) => {
|
||||
const [selectedDamageItemList, setSelectedDamageItemList] = useState([]);
|
||||
const { damageItemList, loadingDamageItemList, errorDamageItemList } = useDamageItemList();
|
||||
const { control, handleSubmit, reset, setValue } = useForm({
|
||||
defaultValues,
|
||||
resolver: yupResolver(schema),
|
||||
mode: "onBlur",
|
||||
});
|
||||
const deleteDamageItem = (id) => {
|
||||
setSelectedDamageItemList((prev) => prev.filter((DamageItem) => DamageItem.items_damge_id !== id));
|
||||
};
|
||||
useEffect(() => {
|
||||
baseOnChange(selectedDamageItemList);
|
||||
}, [selectedDamageItemList]);
|
||||
const handleCreateDamage = (data) => {
|
||||
setSelectedDamageItemList((prev) => {
|
||||
const isDuplicate = prev.some((item) => item.items_damge_id === data.items_damge_id);
|
||||
if (!isDuplicate) {
|
||||
return [...prev, data];
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
});
|
||||
reset();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack spacing={2}>
|
||||
{loadingDamageItemList ? (
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<LinearProgress />
|
||||
</Box>
|
||||
) : (
|
||||
<>
|
||||
<Stack sx={{ minWidth: "200px" }}>
|
||||
<Controller
|
||||
name="items_damge_id"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<Autocomplete
|
||||
options={damageItemList || []}
|
||||
size={"small"}
|
||||
getOptionLabel={(option) => option.title}
|
||||
isOptionEqualToValue={(option, value) => option.id === value.id}
|
||||
loading={loadingDamageItemList}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="نوع خسارت"
|
||||
error={!!error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
)}
|
||||
onChange={(_, value) => field.onChange(value?.id)}
|
||||
value={damageItemList.find((item) => item.id === field.value) || null}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Grid container columnSpacing={1}>
|
||||
<DamagePrice setValue={setValue} control={control} damageItemList={damageItemList} />
|
||||
</Grid>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
onClick={handleSubmit(handleCreateDamage)}
|
||||
sx={{ textWrap: "nowrap" }}
|
||||
>
|
||||
افزودن آیتم خسارت
|
||||
</Button>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
<Divider sx={{ my: 2, width: "100%" }}>
|
||||
<Chip variant="outlined" label="لیست ایتم های خسارت" />
|
||||
</Divider>
|
||||
{selectedDamageItemList.length === 0 && (
|
||||
<Box sx={{ my: 4, width: "100%", textAlign: "center" }}>
|
||||
<Typography variant="h6" sx={{ letterSpacing: "2px", color: "#606060" }}>
|
||||
ابتدا اطلاعات بالا را تکمیل نمایید
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<Fade in={selectedDamageItemList.length !== 0} sx={{ width: "100%" }}>
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{
|
||||
p: 1,
|
||||
mt: 2,
|
||||
minHeight: 150,
|
||||
maxHeight: 300,
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
{selectedDamageItemList.length !== 0 &&
|
||||
selectedDamageItemList.map((selectedDamageItem, index) => (
|
||||
<Grid key={index} item xs={12}>
|
||||
<DamageItemInfo
|
||||
damageItemList={damageItemList}
|
||||
selectedDamageItem={selectedDamageItem}
|
||||
deleteDamageItem={deleteDamageItem}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Fade>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default DamageItem;
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Box, Card, IconButton, Stack, Typography } from "@mui/material";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import React from "react";
|
||||
import MinorCrashIcon from "@mui/icons-material/MinorCrash";
|
||||
|
||||
const DamageItemInfo = ({ deleteDamageItem, selectedDamageItem, damageItemList }) => {
|
||||
const itemDamageId = damageItemList.find((item) => item.id == selectedDamageItem.items_damge_id);
|
||||
return (
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
border: 1,
|
||||
borderColor: "divider",
|
||||
px: 1,
|
||||
py: 1,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex" }}>
|
||||
<MinorCrashIcon color="primary" sx={{ width: "50px", height: "50px" }} />
|
||||
<Stack sx={{ ml: 1, justifyContent: "center" }}>
|
||||
<Typography variant="body1" sx={{ letterSpacing: "1px", fontWeight: 500 }}>
|
||||
{itemDamageId.title}
|
||||
</Typography>
|
||||
<Typography variant="caption">
|
||||
میزان خسارت ({itemDamageId.unit}): {selectedDamageItem.items_damge_value}
|
||||
</Typography>
|
||||
<Typography variant="caption">
|
||||
هزینه خسارت (ریال): {selectedDamageItem.items_damge_amount}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box>
|
||||
<IconButton color="error" onClick={() => deleteDamageItem(selectedDamageItem.items_damge_id)}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
export default DamageItemInfo;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Controller, useWatch } from "react-hook-form";
|
||||
import { Grid, TextField } from "@mui/material";
|
||||
import DamageAmount from "./DamageAmount";
|
||||
|
||||
const DamagePrice = ({ damageItemList, control, setValue }) => {
|
||||
const itemDamage = useWatch({ control, name: "items_damge_id" });
|
||||
const itemDamageId = damageItemList.find((damageItem) => damageItem.id === itemDamage);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller
|
||||
name="items_damge_value"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<TextField
|
||||
value={field.value || ""}
|
||||
variant="outlined"
|
||||
disabled={itemDamage === null}
|
||||
inputProps={{
|
||||
inputMode: "number",
|
||||
min: 0,
|
||||
pattern: "[0-9]*",
|
||||
}}
|
||||
name="items_damge_value"
|
||||
onChange={(event) => {
|
||||
const inputValue = event.target.value;
|
||||
if (isNaN(Number(inputValue))) {
|
||||
return;
|
||||
}
|
||||
field.onChange(inputValue);
|
||||
}}
|
||||
label={`میزان خسارت ${itemDamageId?.unit ? `(${itemDamageId?.unit})` : ""}`}
|
||||
fullWidth
|
||||
size="small"
|
||||
error={error}
|
||||
helperText={error?.message}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<DamageAmount setValue={setValue} damageItemList={damageItemList} control={control} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default DamagePrice;
|
||||
@@ -0,0 +1,53 @@
|
||||
import { FormControlLabel, RadioGroup, Stack } from "@mui/material";
|
||||
import { Controller } from "react-hook-form";
|
||||
import { useState } from "react";
|
||||
import PoliceFileReport from "./PoliceFileReport";
|
||||
import Radio from "@mui/material/Radio";
|
||||
|
||||
const DamageReport = ({ control, setValue }) => {
|
||||
const [selectedOption, setSelectedOption] = useState("report_base");
|
||||
|
||||
const handleOptionChange = (e) => {
|
||||
setSelectedOption(e.target.value);
|
||||
if (e.target.value === "report_base") {
|
||||
setValue("police_file_checkbox", "");
|
||||
setValue("report_base", "1");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<RadioGroup
|
||||
aria-label="file-options"
|
||||
name="file-options"
|
||||
value={selectedOption}
|
||||
onChange={handleOptionChange}
|
||||
>
|
||||
<Stack direction={"row"} alignItems={"center"} justifyContent={"center"}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Controller
|
||||
name="police_file_checkbox"
|
||||
control={control}
|
||||
render={({ field }) => <Radio {...field} value="police_file_checkbox" />}
|
||||
/>
|
||||
}
|
||||
label="کروکی یا نامه پلیس راه (معرفی فرد به اداره)"
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Controller
|
||||
name="report_base"
|
||||
control={control}
|
||||
render={({ field }) => <Radio {...field} value="report_base" />}
|
||||
/>
|
||||
}
|
||||
label="بازدید عوامل و کارشناسان راهداری"
|
||||
/>
|
||||
</Stack>
|
||||
</RadioGroup>
|
||||
{selectedOption === "police_file_checkbox" && <PoliceFileReport control={control} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default DamageReport;
|
||||
@@ -0,0 +1,77 @@
|
||||
import { FormControl, FormHelperText } from "@mui/material";
|
||||
import UploadSystem from "@/core/components/UploadSystem";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
const ImageUpload = ({ name, value, onChange, error, beforeImage = null, title }) => {
|
||||
const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null);
|
||||
const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null);
|
||||
const [beforeFileName, setBeforeFileName] = useState(null);
|
||||
const [showBeforeImage, setShowBeforeImage] = useState(!beforeImage);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
setShowBeforeImage(false);
|
||||
if (typeof value === "string") {
|
||||
setBeforeImg(value);
|
||||
setBeforeFileType("image/");
|
||||
} else if (value instanceof File) {
|
||||
setBeforeImg(URL.createObjectURL(value));
|
||||
setBeforeFileType(value.type);
|
||||
}
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const uploadedFile = event.target?.files?.[0];
|
||||
if (uploadedFile) {
|
||||
const fileType = event.target?.files?.[0].type;
|
||||
const fileName = event.target?.files?.[0].name;
|
||||
setBeforeImg(URL.createObjectURL(uploadedFile));
|
||||
setBeforeFileType(fileType);
|
||||
setBeforeFileName(fileName);
|
||||
onChange(uploadedFile);
|
||||
setShowBeforeImage(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
error={error}
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<FormHelperText
|
||||
sx={{
|
||||
fontSize: 14,
|
||||
border: 1,
|
||||
borderBottom: 0,
|
||||
borderColor: "divider",
|
||||
px: 2,
|
||||
py: 1,
|
||||
borderRadius: 2,
|
||||
borderBottomRightRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
}}
|
||||
id={name}
|
||||
>
|
||||
{title}
|
||||
</FormHelperText>
|
||||
<UploadSystem
|
||||
selectedImage={beforeImg}
|
||||
handleUploadChange={handleFileChange}
|
||||
fileType={beforeFileType}
|
||||
fileName={beforeFileName}
|
||||
setSelectedImage={setBeforeImg}
|
||||
imageSize={[250, 150]}
|
||||
setShowAddIcon={setShowBeforeImage}
|
||||
showAddIcon={showBeforeImage}
|
||||
/>
|
||||
<FormHelperText id={name}>{error ? error.message : null}</FormHelperText>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default ImageUpload;
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Controller } from "react-hook-form";
|
||||
import { Grid, Stack, TextField } from "@mui/material";
|
||||
import MuiDatePicker from "@/core/components/MuiDatePicker";
|
||||
import ImageUpload from "./ImageUpload";
|
||||
|
||||
const PoliceFileReport = ({ control }) => {
|
||||
return (
|
||||
<Stack spacing={3} mt={2}>
|
||||
<Stack>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller
|
||||
name="police_serial"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
label="شماره کروکی یا نامه پلیس راه"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
size="small"
|
||||
error={error}
|
||||
helperText={error && error.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<Controller
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<MuiDatePicker
|
||||
name="police_file_date"
|
||||
error={error}
|
||||
value={field.value}
|
||||
placeholder={"تاریخ کروکی یا نامه پلیس راه"}
|
||||
label={"تاریخ کروکی یا نامه پلیس راه"}
|
||||
setFieldValue={(name, value) => field.onChange(value || [])}
|
||||
helperText={error ? error.message : null}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
name={"police_file_date"}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Stack>
|
||||
<Controller
|
||||
name="police_file"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<ImageUpload
|
||||
name={field.name}
|
||||
value={field.value}
|
||||
error={error}
|
||||
title={"تصویر کروکی یا نامه پلیس راه"}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default PoliceFileReport;
|
||||
@@ -0,0 +1,12 @@
|
||||
"use client";
|
||||
import { Dialog } from "@mui/material";
|
||||
import CreateFormContent from "@/components/dashboard/technicalBuildingDamage/operator/Actions/create/Forms/CreateFormContent";
|
||||
|
||||
const OperatorCreateForm = ({ open, setOpen, mutate }) => {
|
||||
return (
|
||||
<Dialog open={open} fullWidth maxWidth="md">
|
||||
<CreateFormContent setOpen={setOpen} mutate={mutate} />
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
export default OperatorCreateForm;
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
import { Button, IconButton, useMediaQuery } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useState } from "react";
|
||||
import { AddCircle } from "@mui/icons-material";
|
||||
import OperatorCreateForm from "./Forms";
|
||||
|
||||
const OperatorCreate = ({ mutate }) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
<IconButton aria-label="ثبت خسارت" color="primary" onClick={handleOpen}>
|
||||
<AddCircle sx={{ fontSize: "25px" }} />
|
||||
</IconButton>
|
||||
) : (
|
||||
<Button
|
||||
size={"small"}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<AddCircle />}
|
||||
onClick={handleOpen}
|
||||
>
|
||||
ثبت خسارت
|
||||
</Button>
|
||||
)}
|
||||
<OperatorCreateForm open={open} setOpen={setOpen} mutate={mutate} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default OperatorCreate;
|
||||
@@ -0,0 +1,356 @@
|
||||
"use client";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
|
||||
import Toolbar from "./Toolbar";
|
||||
import { GET_TECHNICAL_DAMAGE_OPERATOR_LIST } from "@/core/utils/routes";
|
||||
import moment from "jalali-moment";
|
||||
import RowActions from "./RowActions";
|
||||
import useProvinces from "@/lib/hooks/useProvince";
|
||||
import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency";
|
||||
import useEdaratLists from "@/lib/hooks/useEdaratLists";
|
||||
import ImageDialog from "./RowActions/ImageDialog";
|
||||
import LocationForm from "./RowActions/LocationForm";
|
||||
import ShowPlate from "./RowActions/ShowPlate";
|
||||
|
||||
const OperatorList = () => {
|
||||
const statusOptions = [
|
||||
{ value: 0, label: "بدون اقدام" },
|
||||
{ value: 1, label: "صدور نامه بیمه و کارشناسی داغی" },
|
||||
{ value: 2, label: "فیش ها ثبت شده است" },
|
||||
{ value: 3, label: "فاکتور صادر شده است" },
|
||||
{ value: 4, label: "فاکتور پرداخت شده است" },
|
||||
{ value: 5, label: "نامه پلیس راه صادر شده است" },
|
||||
];
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: "کد یکتا", // Unique Code
|
||||
id: "id",
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterMode: "equals",
|
||||
sortDescFirst: false,
|
||||
columnFilterModeOptions: ["equals", "contains"],
|
||||
grow: false,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessorKey: "province_id",
|
||||
header: "استان",
|
||||
id: "province_id",
|
||||
enableColumnFilter: true,
|
||||
datatype: "numeric",
|
||||
filterMode: "equals",
|
||||
grow: false,
|
||||
size: 130,
|
||||
ColumnSelectComponent: (props) => {
|
||||
const { provinces, errorProvinces, loadingProvinces } = useProvinces();
|
||||
const getColumnSelectOptions = useMemo(() => {
|
||||
if (loadingProvinces) {
|
||||
return [{ value: "loading", label: "در حال بارگذاری..." }];
|
||||
}
|
||||
if (errorProvinces) {
|
||||
return [{ value: "error", label: "خطا در بارگذاری" }];
|
||||
}
|
||||
return [
|
||||
{ value: "", label: "کل کشور" },
|
||||
...provinces.map((province) => ({
|
||||
value: province.id,
|
||||
label: province.name_fa,
|
||||
})),
|
||||
];
|
||||
}, [provinces, errorProvinces, loadingProvinces]);
|
||||
return (
|
||||
<CustomSelectByDependency
|
||||
{...props}
|
||||
value={loadingProvinces ? "loading" : props.filterParameters.value}
|
||||
columnSelectOption={getColumnSelectOptions}
|
||||
/>
|
||||
);
|
||||
},
|
||||
Cell: ({ row }) => <>{row.original.province_fa}</>,
|
||||
},
|
||||
{
|
||||
accessorKey: "city_id",
|
||||
header: "شهرستان", // Office
|
||||
id: "city_id",
|
||||
enableColumnFilter: true,
|
||||
datatype: "numeric",
|
||||
filterMode: "equals",
|
||||
dependencyId: "province_id",
|
||||
grow: false,
|
||||
size: 120,
|
||||
ColumnSelectComponent: (props) => {
|
||||
const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(
|
||||
props.dependencyFieldValue.value
|
||||
);
|
||||
const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value);
|
||||
|
||||
const getColumnSelectOptions = useMemo(() => {
|
||||
if (props.dependencyFieldValue.value === "") {
|
||||
return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }];
|
||||
}
|
||||
if (loadingEdaratList) {
|
||||
return [{ value: "loading", label: "در حال بارگذاری..." }];
|
||||
}
|
||||
if (errorEdaratList) {
|
||||
return [{ value: "error", label: "خطا در بارگذاری" }];
|
||||
}
|
||||
return [
|
||||
{ value: "", label: "شهر" },
|
||||
...edaratList.map((edare) => ({
|
||||
value: edare.id,
|
||||
label: edare.name_fa,
|
||||
})),
|
||||
];
|
||||
}, [edaratList, loadingEdaratList, errorEdaratList]);
|
||||
useEffect(() => {
|
||||
if (prevDependency === props.dependencyFieldValue?.value) return;
|
||||
props.handleChange({ ...props.filterParameters, value: "" });
|
||||
setPrevDependency(props.dependencyFieldValue?.value);
|
||||
}, [props.dependencyFieldValue?.value]);
|
||||
return (
|
||||
<CustomSelectByDependency
|
||||
{...props}
|
||||
value={
|
||||
props.dependencyFieldValue?.value === ""
|
||||
? "empty"
|
||||
: loadingEdaratList
|
||||
? "loading"
|
||||
: props.filterParameters.value
|
||||
}
|
||||
columnSelectOption={getColumnSelectOptions}
|
||||
/>
|
||||
);
|
||||
},
|
||||
Cell: ({ row }) => <>{row.original.city_fa}</>,
|
||||
},
|
||||
{
|
||||
accessorKey: "axis_name",
|
||||
header: "نام محور",
|
||||
id: "axis_name",
|
||||
enableColumnFilter: true,
|
||||
enableSorting: true,
|
||||
datatype: "text",
|
||||
filterMode: "equals",
|
||||
columnFilterModeOptions: ["equals", "contains"],
|
||||
grow: false,
|
||||
size: 120,
|
||||
},
|
||||
{
|
||||
accessorKey: "accident_type",
|
||||
header: "نوع تصادف", // Value
|
||||
id: "accident_type",
|
||||
enableColumnFilter: true,
|
||||
enableSorting: true,
|
||||
datatype: "text",
|
||||
filterMode: "equals",
|
||||
columnFilterModeOptions: ["equals", "contains"],
|
||||
grow: false,
|
||||
size: 120,
|
||||
Cell: ({ row }) => <>{row.original.accident_type_fa}</>,
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => {
|
||||
const formattedDate = moment(row.accident_date, "YYYY-MM-DD"); // تنظیم فرمت صحیح تاریخ
|
||||
const formattedTime = row.accident_time || "00:00:00"; // در صورت خالی بودن مقدار، مقدار پیشفرض داده شود
|
||||
return moment(`${formattedDate.format("YYYY-MM-DD")}T${formattedTime}`)
|
||||
.locale("fa")
|
||||
.format("HH:mm | YYYY/MM/DD");
|
||||
},
|
||||
header: "تاریخ تصادف",
|
||||
id: "accident_date",
|
||||
enableColumnFilter: true,
|
||||
datatype: "date",
|
||||
filterMode: "between",
|
||||
grow: false,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessorKey: "files",
|
||||
header: "تصاویر",
|
||||
id: "files",
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
datatype: "array",
|
||||
grow: false,
|
||||
size: 100,
|
||||
muiTableBodyCellProps: {
|
||||
sx: {
|
||||
borderLeft: "1px solid #e1e1e1",
|
||||
py: 0,
|
||||
"&:first-of-type": {
|
||||
borderLeft: "unset",
|
||||
},
|
||||
},
|
||||
},
|
||||
Cell: ({ row }) => {
|
||||
if (row.original.damage_picture1) {
|
||||
const imagesList = [row.original.damage_picture1, row.original.damage_picture2];
|
||||
return (
|
||||
<Stack alignItems={"center"} justifyContent={"center"}>
|
||||
<ImageDialog images={imagesList} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Typography textAlign={"center"} variant="body2">
|
||||
بدون تصویر
|
||||
</Typography>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "location",
|
||||
header: "موقعیت",
|
||||
id: "location",
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
datatype: "array",
|
||||
grow: false,
|
||||
size: 100,
|
||||
muiTableBodyCellProps: {
|
||||
sx: {
|
||||
borderLeft: "1px solid #e1e1e1",
|
||||
py: 0,
|
||||
"&:first-of-type": {
|
||||
borderLeft: "unset",
|
||||
},
|
||||
},
|
||||
},
|
||||
Cell: ({ row }) => {
|
||||
return (
|
||||
<Stack alignItems={"center"} justifyContent={"center"}>
|
||||
<LocationForm start_lat={row.original.lat} start_lng={row.original.lng} />
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "sum",
|
||||
header: "مبلغ کل خسارت (ریال)",
|
||||
id: "sum",
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterMode: "equals",
|
||||
columnFilterModeOptions: ["equals", "contains"],
|
||||
sortDescFirst: true,
|
||||
grow: false,
|
||||
size: 100,
|
||||
Cell: ({ row }) => (
|
||||
<Typography variant={"body2"} sx={{ margin: 1 }} component="span">
|
||||
{(row.original.sum / 1).toLocaleString()}
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "plaque",
|
||||
header: "پلاک",
|
||||
id: "plaque",
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterMode: "equals",
|
||||
sortDescFirst: true,
|
||||
grow: false,
|
||||
size: 100,
|
||||
Cell: ({ renderedCellValue }) => <ShowPlate plate={renderedCellValue} />,
|
||||
},
|
||||
{
|
||||
accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
|
||||
header: "تاریخ ثبت",
|
||||
id: "created_at",
|
||||
enableColumnFilter: true,
|
||||
datatype: "date",
|
||||
filterMode: "between",
|
||||
grow: false,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "اخرین وضعیت",
|
||||
id: "status",
|
||||
enableColumnFilter: true,
|
||||
datatype: "numeric",
|
||||
filterMode: "equals",
|
||||
grow: false,
|
||||
size: 100,
|
||||
columnSelectOption: () => {
|
||||
return statusOptions.map((status) => ({
|
||||
value: status.value,
|
||||
label: status.label,
|
||||
}));
|
||||
},
|
||||
Cell: ({ row }) => {
|
||||
return <Typography variant={"body2"}>{row.original.status_fa}</Typography>;
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "اطلاعات واریز",
|
||||
id: "items",
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
grow: false,
|
||||
size: 50,
|
||||
columns: [
|
||||
{
|
||||
header: "مبلغ فیش (ریال)",
|
||||
id: "final_amount",
|
||||
enableColumnFilter: false,
|
||||
grow: false,
|
||||
size: 100,
|
||||
Cell: ({ row }) => (
|
||||
<Typography variant={"body2"} sx={{ margin: 1 }} component="span">
|
||||
{(row.original.final_amount / 1).toLocaleString()}
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "مبلغ بیمه (ریال)",
|
||||
id: "deposit_insurance_amount",
|
||||
enableColumnFilter: false,
|
||||
grow: false,
|
||||
size: 100,
|
||||
Cell: ({ row }) => (
|
||||
<Typography variant={"body2"} sx={{ margin: 1 }} component="span">
|
||||
{(row.original.deposit_insurance_amount / 1).toLocaleString()}
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "مبلغ داغی (ریال)",
|
||||
id: "deposit_daghi_amount",
|
||||
enableColumnFilter: false,
|
||||
grow: false,
|
||||
size: 100,
|
||||
Cell: ({ row }) => (
|
||||
<Typography variant={"body2"} sx={{ margin: 1 }} component="span">
|
||||
{(row.original.deposit_daghi_amount / 1).toLocaleString()}
|
||||
</Typography>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ p: 1, border: 1, borderColor: "divider", borderRadius: 1 }}>
|
||||
<DataTableWithAuth
|
||||
need_filter={true}
|
||||
columns={columns}
|
||||
table_url={GET_TECHNICAL_DAMAGE_OPERATOR_LIST}
|
||||
page_name={"operatorTechnicalBuildingDamage"}
|
||||
table_name={"operatorList"}
|
||||
TableToolbar={Toolbar}
|
||||
enableRowActions
|
||||
positionActionsColumn={"last"}
|
||||
RowActions={RowActions}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default OperatorList;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material";
|
||||
import Image from "next/image";
|
||||
|
||||
const ImageFormContent = ({ image, title }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Stack
|
||||
spacing={2}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
sx={{
|
||||
my: 2,
|
||||
padding: 2,
|
||||
borderRadius: "12px",
|
||||
border: `1px solid ${theme.palette.primary.main}`,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
color: theme.palette.primary.dark,
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<Box sx={{ width: "100%", position: "relative", aspectRatio: "16/9" }}>
|
||||
<Image
|
||||
src={image}
|
||||
alt="Image"
|
||||
fill={true}
|
||||
loading="lazy"
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
marginBottom: "16px",
|
||||
borderRadius: "12px",
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default ImageFormContent;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Paper, Tooltip } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary";
|
||||
import ImageFormContent from "./ImageFormContent";
|
||||
|
||||
const ImageDialog = ({ images }) => {
|
||||
const [openImageDialog, setOpenImageDialog] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="تصاویر">
|
||||
<IconButton color="primary" onClick={() => setOpenImageDialog(true)}>
|
||||
<PhotoLibraryIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Dialog
|
||||
fullWidth
|
||||
open={openImageDialog}
|
||||
onClose={() => setOpenImageDialog(false)}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
|
||||
},
|
||||
}}
|
||||
maxWidth={"sm"}
|
||||
scroll="paper"
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: "large" }}>تصاویر</DialogTitle>
|
||||
<DialogContent>
|
||||
<Paper elevation={0}>
|
||||
<ImageFormContent image={images[1]} title={"تصویر تصادف"} />
|
||||
<ImageFormContent image={images[0]} title={"تصویر تصادف"} />
|
||||
</Paper>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setOpenImageDialog(false)} variant="outlined" color="secondary">
|
||||
بستن
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default ImageDialog;
|
||||
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material";
|
||||
import React from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import MapLoading from "@/core/components/MapLayer/Loading";
|
||||
import ShowLocationMarker from "@/core/components/ShowLocationMarker";
|
||||
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
|
||||
loading: () => <MapLoading />,
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_lat, end_lng }) => {
|
||||
return (
|
||||
<>
|
||||
<DialogContent>
|
||||
<Box sx={{ width: "400px", height: "400px" }}>
|
||||
<MapLayer>
|
||||
<ShowLocationMarker
|
||||
start_lat={start_lat}
|
||||
start_lng={start_lng}
|
||||
end_lat={end_lat}
|
||||
end_lng={end_lng}
|
||||
/>
|
||||
</MapLayer>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ justifyContent: "center", paddingBottom: 2, width: "100%" }}>
|
||||
<Button onClick={() => setOpenLocationDialog(false)} variant="outlined" color="secondary">
|
||||
بستن
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default LocationFormContent;
|
||||
@@ -0,0 +1,38 @@
|
||||
import React, { useState } from "react";
|
||||
import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material";
|
||||
import ExploreIcon from "@mui/icons-material/Explore";
|
||||
import LocationFormContent from "./LocationFormContent";
|
||||
const LocationForm = ({ start_lat, start_lng, end_lat, end_lng }) => {
|
||||
const [openLocationDialog, setOpenLocationDialog] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="موقعیت">
|
||||
<IconButton color="primary" onClick={() => setOpenLocationDialog(true)}>
|
||||
<ExploreIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Dialog
|
||||
open={openLocationDialog}
|
||||
onClose={() => setOpenLocationDialog(false)}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
|
||||
},
|
||||
}}
|
||||
dir="rtl"
|
||||
maxWidth={"md"}
|
||||
>
|
||||
<DialogTitle>موقعیت</DialogTitle>
|
||||
<LocationFormContent
|
||||
start_lat={start_lat}
|
||||
start_lng={start_lng}
|
||||
end_lat={end_lat}
|
||||
end_lng={end_lng}
|
||||
setOpenLocationDialog={setOpenLocationDialog}
|
||||
/>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default LocationForm;
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Stack } from "@mui/material";
|
||||
|
||||
const ShowPlate = ({ plate }) => {
|
||||
return (
|
||||
<Stack sx={{ border: 1, borderColor: "divider", borderRadius: 1 }} direction={"row"}>
|
||||
<Stack sx={{ borderRight: 1, borderColor: "divider", textAlign: "center", width: "40%" }}>
|
||||
{plate.split("-")[3]}
|
||||
</Stack>
|
||||
<Stack direction={"row"} sx={{ width: "100%", px: 0.5 }}>
|
||||
<Stack
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{plate.split("-")[2]}
|
||||
</Stack>
|
||||
<Stack
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{plate.split("-")[1]}
|
||||
</Stack>
|
||||
<Stack
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{plate.split("-")[0]}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default ShowPlate;
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const RowActions = ({ row }) => {
|
||||
return <Box sx={{ display: "flex", gap: 1 }}></Box>;
|
||||
};
|
||||
export default RowActions;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Box } from "@mui/material";
|
||||
import OperatorCreate from "./Actions/create";
|
||||
|
||||
const Toolbar = ({ table, filterData, mutate }) => {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<OperatorCreate mutate={mutate} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default Toolbar;
|
||||
@@ -0,0 +1,14 @@
|
||||
"use client";
|
||||
import PageTitle from "@/core/components/PageTitle";
|
||||
import { Stack } from "@mui/material";
|
||||
import OperatorList from "./OperatorList";
|
||||
|
||||
const OperatorPage = () => {
|
||||
return (
|
||||
<Stack spacing={1}>
|
||||
<PageTitle title={"خسارات وارده بر ابنیه فنی و تاسیسات راه"} />
|
||||
<OperatorList />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
export default OperatorPage;
|
||||
@@ -37,7 +37,7 @@ const createCustomIcon = (size, iconUrl, labelText, color) => {
|
||||
};
|
||||
const MAX_ZOOM_FOR_MARKER = 13;
|
||||
|
||||
const MapInteraction = ({ setValue, startLat, startLng }) => {
|
||||
const MapInteraction = ({ setValue, startLat, startLng, title }) => {
|
||||
const [isMarkerLocked, setIsMarkerLocked] = useState(!!(startLat && startLng));
|
||||
const [enableSend, setEnableSend] = useState(false);
|
||||
const [startIconColor, setStartIconColor] = useState(startLat ? "#1CAC66" : "#003d4f");
|
||||
@@ -109,7 +109,7 @@ const MapInteraction = ({ setValue, startLat, startLng }) => {
|
||||
>
|
||||
<Zoom in={!enableSend}>
|
||||
<Alert sx={{ mt: 1, py: 0.5, px: 2, borderRadius: 2, border: 1 }} icon={false} color={"warning"}>
|
||||
برای ثبت محل فعالیت، لطفاً نقشه را بیشتر زوم کنید.
|
||||
{title}
|
||||
</Alert>
|
||||
</Zoom>
|
||||
</Stack>
|
||||
@@ -131,7 +131,12 @@ const MapInteraction = ({ setValue, startLat, startLng }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const MapInfoOneMarker = ({ setValue, errors, StartPoint = null }) => {
|
||||
const MapInfoOneMarker = ({
|
||||
setValue,
|
||||
errors,
|
||||
StartPoint = null,
|
||||
title = "برای ثبت محل فعالیت، لطفاً نقشه را بیشتر زوم کنید.",
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<Box sx={{ width: "100%", height: "400px", marginBottom: "16px" }}>
|
||||
@@ -140,6 +145,7 @@ const MapInfoOneMarker = ({ setValue, errors, StartPoint = null }) => {
|
||||
setValue={setValue}
|
||||
startLat={StartPoint ? StartPoint.lat : null}
|
||||
startLng={StartPoint ? StartPoint.lng : null}
|
||||
title={title}
|
||||
/>
|
||||
</MapLayer>
|
||||
</Box>
|
||||
|
||||
@@ -8,67 +8,69 @@ import moment from "jalali-moment";
|
||||
|
||||
function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error, label }) {
|
||||
return (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
|
||||
<LocalizationProvider
|
||||
dateAdapter={AdapterDateFnsJalali}
|
||||
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
|
||||
>
|
||||
<MobileDatePicker
|
||||
value={value ? new Date(value) : null}
|
||||
sx={{ width: "100%" }}
|
||||
id={name}
|
||||
name={name}
|
||||
closeOnSelect
|
||||
disableFuture
|
||||
label={label}
|
||||
aria-describedby="component-helper-text"
|
||||
onChange={(value) => {
|
||||
const date = new Date(value);
|
||||
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD");
|
||||
setFieldValue(name, formattedDate);
|
||||
}}
|
||||
minDate={minDate ? new Date(minDate) : null}
|
||||
maxDate={maxDate ? new Date(maxDate) : null}
|
||||
slotProps={{
|
||||
textField: {
|
||||
error: error,
|
||||
size: "small",
|
||||
placeholder: placeholder,
|
||||
InputLabelProps: {
|
||||
shrink: true,
|
||||
<>
|
||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
|
||||
<LocalizationProvider
|
||||
dateAdapter={AdapterDateFnsJalali}
|
||||
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
|
||||
>
|
||||
<MobileDatePicker
|
||||
value={value ? new Date(value) : null}
|
||||
sx={{ width: "100%" }}
|
||||
id={name}
|
||||
name={name}
|
||||
closeOnSelect
|
||||
disableFuture
|
||||
label={label}
|
||||
aria-describedby="component-helper-text"
|
||||
onChange={(value) => {
|
||||
const date = new Date(value);
|
||||
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD");
|
||||
setFieldValue(name, formattedDate);
|
||||
}}
|
||||
minDate={minDate ? new Date(minDate) : null}
|
||||
maxDate={maxDate ? new Date(maxDate) : null}
|
||||
slotProps={{
|
||||
textField: {
|
||||
error: error,
|
||||
size: "small",
|
||||
placeholder: placeholder,
|
||||
InputLabelProps: {
|
||||
shrink: true,
|
||||
},
|
||||
InputProps: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setFieldValue(name, "");
|
||||
}}
|
||||
sx={{
|
||||
color: "#bfbfbf",
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(189, 189, 189, 0.1)",
|
||||
color: "#363434",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
},
|
||||
InputProps: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setFieldValue(name, "");
|
||||
}}
|
||||
sx={{
|
||||
color: "#bfbfbf",
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(189, 189, 189, 0.1)",
|
||||
color: "#363434",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{helperText ? (
|
||||
<FormHelperText id="component-helper-text" sx={{ color: error ? "#d32f2f" : "unset" }}>
|
||||
{helperText}
|
||||
</FormHelperText>
|
||||
) : null}
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
{helperText && (
|
||||
<FormHelperText id="component-helper-text" sx={{ color: error ? "#d32f2f" : "unset", ml: 2 }}>
|
||||
{helperText}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,66 +13,68 @@ function MuiTimePicker({ value, setFieldValue, name, minTime, maxTime, helperTex
|
||||
const parsedMaxTime = maxTime || null;
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
|
||||
<LocalizationProvider
|
||||
dateAdapter={AdapterDateFnsJalali}
|
||||
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
|
||||
>
|
||||
<MobileTimePicker
|
||||
value={parsedValue}
|
||||
sx={{ width: "100%" }}
|
||||
id={name}
|
||||
closeOnSelect
|
||||
label={label}
|
||||
ampm={false}
|
||||
timeSteps={{ hours: 1, minutes: 5 }}
|
||||
views={["hours", "minutes"]}
|
||||
name={name}
|
||||
onChange={(newValue) => {
|
||||
setFieldValue(name, newValue);
|
||||
}}
|
||||
minTime={parsedMinTime}
|
||||
maxTime={parsedMaxTime}
|
||||
slotProps={{
|
||||
textField: {
|
||||
placeholder: placeholder,
|
||||
size: "small",
|
||||
error: error,
|
||||
InputLabelProps: {
|
||||
shrink: true,
|
||||
<>
|
||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
|
||||
<LocalizationProvider
|
||||
dateAdapter={AdapterDateFnsJalali}
|
||||
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
|
||||
>
|
||||
<MobileTimePicker
|
||||
value={parsedValue}
|
||||
sx={{ width: "100%" }}
|
||||
id={name}
|
||||
closeOnSelect
|
||||
label={label}
|
||||
ampm={false}
|
||||
timeSteps={{ hours: 1, minutes: 5 }}
|
||||
views={["hours", "minutes"]}
|
||||
name={name}
|
||||
onChange={(newValue) => {
|
||||
setFieldValue(name, newValue);
|
||||
}}
|
||||
minTime={parsedMinTime}
|
||||
maxTime={parsedMaxTime}
|
||||
slotProps={{
|
||||
textField: {
|
||||
placeholder: placeholder,
|
||||
size: "small",
|
||||
error: error,
|
||||
InputLabelProps: {
|
||||
shrink: true,
|
||||
},
|
||||
InputProps: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setFieldValue(name, "");
|
||||
}}
|
||||
sx={{
|
||||
color: "#bfbfbf",
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(189, 189, 189, 0.1)",
|
||||
color: "#363434",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
},
|
||||
InputProps: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setFieldValue(name, "");
|
||||
}}
|
||||
sx={{
|
||||
color: "#bfbfbf",
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(189, 189, 189, 0.1)",
|
||||
color: "#363434",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{helperText ? (
|
||||
<FormHelperText id="component-helper-text" sx={{ color: error ? "#d32f2f" : "unset" }}>
|
||||
{helperText}
|
||||
</FormHelperText>
|
||||
) : null}
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
{helperText && (
|
||||
<FormHelperText id="component-helper-text" sx={{ color: error ? "#d32f2f" : "unset", ml: 2 }}>
|
||||
{helperText}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
197
src/core/components/PlateNumber.jsx
Normal file
197
src/core/components/PlateNumber.jsx
Normal file
@@ -0,0 +1,197 @@
|
||||
import { Box, Button, Drawer, FormHelperText, Stack, TextField } from "@mui/material";
|
||||
import AccessibleIcon from "@mui/icons-material/Accessible";
|
||||
import { Controller, useFormState, useWatch } from "react-hook-form";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const plate_words = [
|
||||
{ id: 1, value: "الف", name: "الف" },
|
||||
{ id: 2, value: "ب", name: "ب" },
|
||||
{ id: 3, value: "پ", name: "پ" },
|
||||
{ id: 4, value: "ت", name: "ت" },
|
||||
{ id: 5, value: "ث", name: "ث" },
|
||||
{ id: 6, value: "ج", name: "ج" },
|
||||
{ id: 7, value: "د", name: "د" },
|
||||
{ id: 8, value: "ز", name: "ز" },
|
||||
{ id: 9, value: "س", name: "س" },
|
||||
{ id: 10, value: "ش", name: "ش" },
|
||||
{ id: 11, value: "ص", name: "ص" },
|
||||
{ id: 12, value: "ط", name: "ط" },
|
||||
{ id: 13, value: "ع", name: "ع" },
|
||||
{ id: 14, value: "ف", name: "ف" },
|
||||
{ id: 15, value: "ق", name: "ق" },
|
||||
{ id: 16, value: "ک", name: "گ" },
|
||||
{ id: 17, value: "ل", name: "ل" },
|
||||
{ id: 18, value: "م", name: "م" },
|
||||
{ id: 19, value: "ن", name: "ن" },
|
||||
{ id: 20, value: "و", name: "و" },
|
||||
{ id: 21, value: "ه", name: "ه" },
|
||||
{ id: 22, value: "ی", name: "ی" },
|
||||
{ id: 23, value: "*", name: <AccessibleIcon /> },
|
||||
];
|
||||
|
||||
const PlateNumber = ({ control }) => {
|
||||
const platePart2 = useWatch({ control, name: "plate_part2" });
|
||||
const [plateDrawer, setPlateDrawer] = useState(false);
|
||||
const { errors } = useFormState({ control });
|
||||
const plateErrors = ["plate_part1", "plate_part2", "plate_part3", "plate_part4"].some((part) => errors[part]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
sx={{
|
||||
border: 1,
|
||||
borderColor: plateErrors ? "error.main" : "divider",
|
||||
overflow: "hidden",
|
||||
borderRadius: 1,
|
||||
}}
|
||||
>
|
||||
<Controller
|
||||
name="plate_part4"
|
||||
control={control}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="tel"
|
||||
inputProps={{
|
||||
inputMode: "number",
|
||||
min: 0,
|
||||
pattern: "[0-9]*",
|
||||
sx: { textAlign: "center" },
|
||||
}}
|
||||
onChange={(event) => {
|
||||
const inputValue = event.target.value;
|
||||
if (isNaN(Number(inputValue))) {
|
||||
return;
|
||||
}
|
||||
inputValue.length <= 2 ? field.onChange(inputValue) : null;
|
||||
}}
|
||||
size="small"
|
||||
placeholder="xx"
|
||||
sx={{ flexGrow: 2, "& fieldset": { border: "none" } }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="plate_part3"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="tel"
|
||||
inputProps={{
|
||||
inputMode: "number",
|
||||
min: 0,
|
||||
pattern: "[0-9]*",
|
||||
sx: { textAlign: "center" },
|
||||
}}
|
||||
onChange={(event) => {
|
||||
const inputValue = event.target.value;
|
||||
if (isNaN(Number(inputValue))) {
|
||||
return;
|
||||
}
|
||||
inputValue.length <= 3 ? field.onChange(inputValue) : null;
|
||||
}}
|
||||
size="small"
|
||||
placeholder="xxx"
|
||||
sx={{
|
||||
flexGrow: 3,
|
||||
borderLeft: 1,
|
||||
borderRadius: 0,
|
||||
borderColor: "divider",
|
||||
"& fieldset": { border: "none" },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Button
|
||||
onClick={() => setPlateDrawer(true)}
|
||||
sx={{ height: "100%", borderRadius: 0, borderColor: "divider" }}
|
||||
size={"small"}
|
||||
>
|
||||
{plate_words.find((p) => p.value === platePart2)?.name || "الف"}
|
||||
</Button>
|
||||
</Box>
|
||||
<Drawer
|
||||
sx={{ zIndex: 9999 }}
|
||||
anchor={"bottom"}
|
||||
open={plateDrawer}
|
||||
onClose={() => setPlateDrawer(false)}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
mx: "auto",
|
||||
my: 2,
|
||||
gap: 2,
|
||||
width: "60%",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{plate_words.map((item) => (
|
||||
<Controller
|
||||
key={item.id}
|
||||
name="plate_part2"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPlateDrawer(false);
|
||||
field.onChange(item.value);
|
||||
}}
|
||||
sx={{ width: "fit-content" }}
|
||||
variant="contained"
|
||||
>
|
||||
{item.name}
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Drawer>
|
||||
<Controller
|
||||
name="plate_part1"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
type="tel"
|
||||
size="small"
|
||||
placeholder="xx"
|
||||
sx={{ flexGrow: 2, "& fieldset": { border: "none" } }}
|
||||
inputProps={{
|
||||
inputMode: "number",
|
||||
min: 0,
|
||||
pattern: "[0-9]*",
|
||||
sx: { textAlign: "center" },
|
||||
}}
|
||||
onChange={(event) => {
|
||||
const inputValue = event.target.value;
|
||||
if (isNaN(Number(inputValue))) {
|
||||
return;
|
||||
}
|
||||
inputValue.length <= 2 ? field.onChange(inputValue) : null;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Stack sx={{ width: 24, borderLeft: 1, borderColor: "divider" }}>
|
||||
<Box sx={{ flexGrow: 1, bgcolor: "#1ebc1e" }}></Box>
|
||||
<Box sx={{ flexGrow: 1, bgcolor: "white" }}></Box>
|
||||
<Box sx={{ flexGrow: 1, bgcolor: "#f52121" }}></Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{plateErrors && (
|
||||
<FormHelperText sx={{ ml: 2 }} error>
|
||||
پلاک نامعتبر است!!!
|
||||
</FormHelperText>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PlateNumber;
|
||||
49
src/core/components/SelectBox.jsx
Normal file
49
src/core/components/SelectBox.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material";
|
||||
|
||||
function SelectBox({
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
onBlur,
|
||||
selectors,
|
||||
label,
|
||||
error,
|
||||
schema,
|
||||
disabled,
|
||||
helperText,
|
||||
isLoading,
|
||||
errorEcured,
|
||||
variant = "outlined",
|
||||
}) {
|
||||
return (
|
||||
<FormControl disabled={disabled || false} fullWidth variant={variant} size="small" error={!!error}>
|
||||
<InputLabel>{label}</InputLabel>
|
||||
<Select
|
||||
label={label}
|
||||
name={name}
|
||||
size="small"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
onBlur={onBlur}
|
||||
>
|
||||
{isLoading ? (
|
||||
<MenuItem sx={{ color: "primary.main" }}>درحال دریافت اطلاعات...</MenuItem>
|
||||
) : errorEcured ? (
|
||||
<MenuItem sx={{ color: "secondary.main" }}>خطا در دریافت اطلاعات!!!</MenuItem>
|
||||
) : (
|
||||
selectors.map((selector) => {
|
||||
return (
|
||||
<MenuItem key={selector.id} value={selector[schema.value]}>
|
||||
{selector[schema.name]}
|
||||
</MenuItem>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Select>
|
||||
<FormHelperText>{helperText || error?.message}</FormHelperText>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
export default SelectBox;
|
||||
@@ -14,7 +14,6 @@ const ShowPlak = ({ plak_number }) => {
|
||||
};
|
||||
};
|
||||
const plakParts = processPlak(plak_number);
|
||||
|
||||
return (
|
||||
<Stack sx={{ border: 1, borderColor: "divider", borderRadius: 1 }} direction={"row"}>
|
||||
<Stack
|
||||
|
||||
@@ -468,7 +468,7 @@ export const pageMenu = [
|
||||
id: "receiptManagmentOparationCartable",
|
||||
label: "کارتابل",
|
||||
type: "page",
|
||||
route: process.env.NEXT_PUBLIC_API_URL + "/v2/receipt",
|
||||
route: "/dashboard/technical-building-damage/operator",
|
||||
permissions: ["all"],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -85,5 +85,8 @@ export const CREATE_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages";
|
||||
export const UPDATE_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages";
|
||||
export const ATIVITY_RECEIPT_DAMAGE_ITEMS = api + "/api/v3/damages/activate";
|
||||
|
||||
// damage receipt
|
||||
export const GET_TECHNICAL_DAMAGE_OPERATOR_LIST = api + "/api/v3/receipts";
|
||||
export const GET_DAMAGE_ITEM_LIST = api + "/api/v3/damages/list";
|
||||
// activity code log
|
||||
export const ACTIVITY_LOG = api + "/api/v3/profile/add_activity";
|
||||
|
||||
@@ -38,8 +38,22 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns,
|
||||
return innerObject.value === "";
|
||||
}
|
||||
});
|
||||
const CheckHideValues = Object.values(hideData).every((value) => value === true);
|
||||
const flattenObject = (obj) => {
|
||||
let result = {};
|
||||
for (const key in obj) {
|
||||
if (typeof obj[key] === "object" && obj[key] !== null) {
|
||||
Object.assign(result, flattenObject(obj[key])); // باز کردن اشیای تودرتو
|
||||
} else {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// تبدیل آبجکت به آبجکت فلت شده
|
||||
const flatHideData = flattenObject(hideData);
|
||||
|
||||
const CheckHideValues = Object.values(flatHideData).every((value) => value === true);
|
||||
hasAnyConflict =
|
||||
!CheckHideValues || JSON.stringify(sortData) !== JSON.stringify(initialSort) || !CheckFilterValues;
|
||||
|
||||
|
||||
29
src/lib/hooks/useDamageItemList.js
Normal file
29
src/lib/hooks/useDamageItemList.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { GET_DAMAGE_ITEM_LIST, GET_PROVINCE_LISTS } from "@/core/utils/routes";
|
||||
import useRequest from "@/lib/hooks/useRequest";
|
||||
|
||||
const useDamageItemList = () => {
|
||||
const requestServer = useRequest();
|
||||
const [damageItemList, setDamageItemList] = useState([]);
|
||||
const [loadingDamageItemList, setLoadingDamageItemList] = useState(true);
|
||||
const [errorDamageItemList, setErrorDamageItemList] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCities = async () => {
|
||||
try {
|
||||
const response = await requestServer(`${GET_DAMAGE_ITEM_LIST}`);
|
||||
setDamageItemList(response.data.data);
|
||||
setLoadingDamageItemList(false);
|
||||
} catch (e) {
|
||||
setErrorDamageItemList(e);
|
||||
setLoadingDamageItemList(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCities();
|
||||
}, []);
|
||||
|
||||
return { damageItemList, loadingDamageItemList, errorDamageItemList };
|
||||
};
|
||||
|
||||
export default useDamageItemList;
|
||||
@@ -15,7 +15,6 @@ const useProvinces = () => {
|
||||
setProvinces(response.data.data);
|
||||
setLoadingProvinces(false);
|
||||
} catch (e) {
|
||||
console.error("Error fetching provinces:", e);
|
||||
setErrorProvinces(e);
|
||||
setLoadingProvinces(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user