Feature/complaint create

This commit is contained in:
2025-02-27 13:02:31 +00:00
committed by AmirHossein Mahmoodi
parent d0a2fd3145
commit fa82abd086
11 changed files with 389 additions and 9 deletions

View File

@@ -4,7 +4,6 @@ import { yupResolver } from "@hookform/resolvers/yup";
import { array, mixed, number, object, string } from "yup";
import { Box, Button, DialogActions, DialogContent, Tab, Tabs } 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";

View File

@@ -242,7 +242,7 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => {
}, []);
return (
<Dialog onClose={() => setOpen(false)} open={open} fullWidth maxWidth={"md"}>
<Dialog onClose={() => setOpen(false)} open={open} fullWidth maxWidth={"xl"}>
{open && (
<Box sx={{ p: 1, border: 1, borderColor: "divider", borderRadius: 1 }}>
<DataTableWithAuth

View File

@@ -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, title }) => {
const [beforeImg, setBeforeImg] = useState(value ? value : null);
const [beforeFileType, setBeforeFileType] = useState(value ? "image/" : null);
const [beforeFileName, setBeforeFileName] = useState(null);
const [showBeforeImage, setShowBeforeImage] = useState(!value);
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;

View File

@@ -0,0 +1,108 @@
import { Button, DialogActions, DialogContent, FormControlLabel, RadioGroup, Stack } from "@mui/material";
import { Controller, useForm } from "react-hook-form";
import Radio from "@mui/material/Radio";
import StyledForm from "@/core/components/StyledForm";
import { useState } from "react";
import RegisterActionDone from "./RegisterActionDone";
import RegisterActionUndone from "./RegisterActionUndone";
import { yupResolver } from "@hookform/resolvers/yup";
import { mixed, object, string } from "yup";
import BeenhereIcon from "@mui/icons-material/Beenhere";
const RegisterActionContent = ({ setOpen, defaultValues, onBaseSubmit }) => {
const [selectedOption, setSelectedOption] = useState("1");
const handleOptionChange = (e) => {
setSelectedOption(e.target.value);
};
const validationSchema = object({
description: string().required("توضیحات الزامی است"),
start_point: mixed().when("rms_status", {
is: "1",
then: (schema) => schema.required("مکان اقدام الزامی است"),
otherwise: (schema) => schema.notRequired(),
}),
image_before_1: mixed().when("rms_status", {
is: "1",
then: (schema) => schema.required("تصویر قبل از اقدام الزامی است"),
otherwise: (schema) => schema.notRequired(),
}),
image_after_1: mixed().when("rms_status", {
is: "1",
then: (schema) => schema.required("تصویر بعد از اقدام الزامی است"),
otherwise: (schema) => schema.notRequired(),
}),
});
const {
control,
handleSubmit,
setValue,
formState: { isSubmitting, errors },
} = useForm({
defaultValues,
resolver: yupResolver(validationSchema),
mode: "onChange",
});
const onSubmit = async (data) => {
console.log(data);
await onBaseSubmit(data);
};
return (
<StyledForm onSubmit={handleSubmit(onSubmit)}>
<DialogContent dividers sx={{ overflowY: "auto", maxHeight: "70vh" }}>
<RadioGroup
aria-label="file-options"
name="status-options"
value={selectedOption}
onChange={handleOptionChange}
>
<Stack direction={"row"} alignItems={"center"} justifyContent={"center"}>
<FormControlLabel
control={
<Controller
name="rms_status"
control={control}
render={({ field }) => <Radio {...field} value="1" />}
/>
}
label="اقدام انجام شد"
/>
<FormControlLabel
control={
<Controller
name="rms_status"
control={control}
render={({ field }) => <Radio {...field} value="2" />}
/>
}
label="اقدام انجام نشد"
/>
</Stack>
</RadioGroup>
{selectedOption === "1" ? (
<RegisterActionDone control={control} setValue={setValue} errors={errors} />
) : (
<RegisterActionUndone control={control} />
)}
</DialogContent>
<DialogActions>
<Button color={"secondary"} variant={"outlined"} onClick={() => setOpen(false)}>
بستن
</Button>
<Button
disabled={isSubmitting}
endIcon={<BeenhereIcon />}
color={"primary"}
variant={"contained"}
type={"submit"}
>
{isSubmitting ? "درحال ثبت اقدام..." : "ثبت اقدام"}
</Button>
</DialogActions>
</StyledForm>
);
};
export default RegisterActionContent;

View File

@@ -0,0 +1,86 @@
import { Controller, useWatch } from "react-hook-form";
import { Grid, Stack, TextField } from "@mui/material";
import ImageUpload from "./ImageUpload";
import MapInfoOneMarker from "@/core/components/MapInfoOneMarker";
const RegisterActionDone = ({ control, setValue, errors }) => {
const StartPoint = useWatch({ control, name: "rms_start_latlng" });
return (
<Stack spacing={2}>
<Stack>
<Grid container spacing={2}>
<Grid item xs={12} sm={12}>
<Controller
name="description"
control={control}
render={({ field, fieldState: { error } }) => {
return (
<TextField
name={field.name}
value={field.value}
label={"توضیحات"}
placeholder={"لطفا توضیحات خود را وارد کنید"}
onChange={field.onChange}
fullWidth
multiline
rows={4}
error={error}
helperText={error ? error.message : null}
size={"small"}
/>
);
}}
/>
</Grid>
</Grid>
</Stack>
<Stack>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<Controller
name="image_before_1"
control={control}
render={({ field, fieldState: { error } }) => (
<ImageUpload
name={field.name}
value={field.value}
error={error}
onChange={field.onChange}
title={"تصویر قبل از اقدام"}
/>
)}
/>
</Grid>
<Grid item xs={12} sm={6}>
<Controller
name="image_after_1"
control={control}
render={({ field, fieldState: { error } }) => (
<ImageUpload
name={field.name}
value={field.value}
error={error}
onChange={field.onChange}
title={"تصویر بعد از اقدام"}
/>
)}
/>
</Grid>
</Grid>
</Stack>
<Stack>
<Grid container>
<Grid item xs={12} sm={12}>
<MapInfoOneMarker
setValue={setValue}
StartPoint={StartPoint}
errors={errors}
title={"برای ثبت محل اقدام، لطفاً نقشه را بیشتر زوم کنید."}
/>
</Grid>
</Grid>
</Stack>
</Stack>
);
};
export default RegisterActionDone;

View File

@@ -0,0 +1,37 @@
import { Grid, Stack, TextField } from "@mui/material";
import { Controller } from "react-hook-form";
const RegisterActionUndone = ({ control }) => {
return (
<Stack spacing={2}>
<Stack>
<Grid container spacing={2}>
<Grid item xs={12} sm={12}>
<Controller
name="description"
control={control}
render={({ field, fieldState: { error } }) => {
return (
<TextField
name={field.name}
value={field.value}
label={"توضیحات"}
placeholder={"لطفا توضیحات خود را وارد کنید"}
onChange={field.onChange}
fullWidth
multiline
rows={4}
error={error}
helperText={error ? error.message : null}
size={"small"}
/>
);
}}
/>
</Grid>
</Grid>
</Stack>
</Stack>
);
};
export default RegisterActionUndone;

View File

@@ -0,0 +1,66 @@
import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
import RegisterActionContent from "./RegisterActionContent";
import AppRegistrationIcon from "@mui/icons-material/AppRegistration";
import useRequest from "@/lib/hooks/useRequest";
import { REGISTER_COMPLAINTS_LIST } from "@/core/utils/routes";
const RegisterAction = ({ rowId, mutate }) => {
const requestServer = useRequest({ notificationSuccess: true });
const [openRegisterActionDialog, setOpenRegisterActionDialog] = useState(false);
const defaultValues = {
rms_status: "1",
description: "",
start_point: "",
image_before_1: null,
image_after_1: null,
};
const onBaseSubmit = async (result) => {
console.log(result);
const formData = new FormData();
const rmsLatLng = `${result.start_point.lat},${result.start_point.lng}`;
if (result.rms_status === "1") {
formData.append("rms_description", result.description);
formData.append("rms_status", 1);
formData.append("start_point", rmsLatLng);
formData.append("image_before", result.image_before_1);
formData.append("image_after", result.image_after_1);
} else {
formData.append("rms_description", result.description);
formData.append("rms_status", 2);
}
await requestServer(`${REGISTER_COMPLAINTS_LIST}/${rowId}`, "post", {
data: formData,
})
.then((res) => {
mutate();
setOpenRegisterActionDialog(false);
})
.catch(() => {});
};
return (
<>
<Tooltip title="ثبت اقدام">
<IconButton color="primary" onClick={() => setOpenRegisterActionDialog(true)}>
<AppRegistrationIcon />
</IconButton>
</Tooltip>
<Dialog
fullWidth
open={openRegisterActionDialog}
PaperProps={{
sx: {
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
},
}}
>
<DialogTitle>ثبت اقدام</DialogTitle>
<RegisterActionContent
setOpen={setOpenRegisterActionDialog}
onBaseSubmit={onBaseSubmit}
defaultValues={defaultValues}
/>
</Dialog>
</>
);
};
export default RegisterAction;

View File

@@ -1,10 +1,12 @@
import { Box } from "@mui/material";
import RegisterAction from "../Form/registerAction";
import ReferList from "./ReferList";
import Refer from "./Refer";
const RowActions = ({ row, mutate }) => {
return (
<Box sx={{ display: "flex", gap: 1 }}>
<RegisterAction mutate={mutate} rowId={row.getValue("id")} />
<Refer rowId={row.getValue("id")} mutate={mutate} />
<ReferList rowId={row.getValue("id")} />
</Box>

View File

@@ -13,12 +13,12 @@ const RowActions = ({ row, mutate }) => {
return (
<Box sx={{ display: "flex", gap: 1 }}>
{hasSupervisePermission && row.original?.status === 0 && (
<ConfirmForm rowId={row.getValue("id")} mutate={mutate} />
<ConfirmForm rowId={row.getValue("road_observeds__id")} mutate={mutate} />
)}
{hasSupervisePermission && row.original?.status === 0 && (
<RejectForm rowId={row.getValue("id")} mutate={mutate} />
<RejectForm rowId={row.getValue("road_observeds__id")} mutate={mutate} />
)}
{hasRestorePermission && <RestoreForm rowId={row.getValue("id")} mutate={mutate} />}
{hasRestorePermission && <RestoreForm rowId={row.getValue("road_observeds__id")} mutate={mutate} />}
</Box>
);
};

View File

@@ -67,7 +67,7 @@ const SupervisorList = () => {
{
accessorKey: "id",
header: "کد یکتا",
id: "id",
id: "road_observeds__id",
enableColumnFilter: true,
datatype: "text",
filterMode: "equals",
@@ -91,7 +91,7 @@ const SupervisorList = () => {
...(hasCountryPermission ? [dynamicColumns] : []),
{
header: "اداره",
id: "edarat_id",
id: "city_id",
enableColumnFilter: true,
datatype: "numeric",
filterMode: "equals",
@@ -272,7 +272,7 @@ const SupervisorList = () => {
{
accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"),
header: "تاریخ ثبت",
id: "created_at",
id: "road_observeds__created_at",
enableColumnFilter: true,
datatype: "date",
filterMode: "between",
@@ -280,7 +280,11 @@ const SupervisorList = () => {
size: 100,
},
{
accessorFn: (row) => moment(row.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD"),
accessorFn: (row) => {
return row.rms_last_activity
? moment(row?.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD")
: "بدون تاریخ";
},
header: "تاریخ اقدام",
id: "rms_last_activity",
enableColumnFilter: true,

View File

@@ -116,6 +116,7 @@ export const GET_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/superv
export const GET_FAST_REACT_COMPLAINTS = api + "/api/v3/road_observations/complaints_index";
export const VERIFY_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/verify";
export const REJECT_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/verify";
export const REGISTER_COMPLAINTS_LIST = api + "/api/v3/road_observations/register";
export const GET_FAST_REACT_OPERATOR = api + "/api/v3/road_observations/operator_index";
export const GET_FAST_REACT_REFER_LIST = api + "/api/v3/road_observations/refer_list";
export const REFER_FAST_REACT = api + "/api/v3/road_observations/refer";