Feature/change filter report map
This commit is contained in:
@@ -121,8 +121,8 @@ const OperatorList = () => {
|
||||
props.dependencyFieldValue?.value === ""
|
||||
? "empty"
|
||||
: loadingEdaratList
|
||||
? "loading"
|
||||
: props.filterParameters.value
|
||||
? "loading"
|
||||
: props.filterParameters.value
|
||||
}
|
||||
columnSelectOption={getColumnSelectOptions}
|
||||
/>
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const ActionDate = ({ value, onChange, error }) => {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ اقدام"}
|
||||
name={`action_date[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ اقدام"}
|
||||
name={`action_date[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default ActionDate;
|
||||
@@ -1,32 +0,0 @@
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const ActivityDateTimeFilter = ({ value, onChange, error }) => {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بازدید"}
|
||||
name={`activity_date_time[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بازدید"}
|
||||
name={`activity_date_time[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default ActivityDateTimeFilter;
|
||||
@@ -1,37 +0,0 @@
|
||||
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
|
||||
|
||||
const AxisTypeFilter = ({ value, onChange }) => {
|
||||
const axisOptions = [
|
||||
{ value: "", label: "همه محور ها" },
|
||||
{ value: 1, label: "آزادراه" },
|
||||
{ value: 2, label: "بزرگراه" },
|
||||
{ value: 3, label: "اصلی" },
|
||||
{ value: 4, label: "فرعی" },
|
||||
{ value: 5, label: "روستایی" },
|
||||
];
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelaxis`} shrink>
|
||||
نوع محور
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelaxis`}
|
||||
id={"axis"}
|
||||
name={`axis`}
|
||||
value={value}
|
||||
input={<OutlinedInput label="نوع محور" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{axisOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default AxisTypeFilter;
|
||||
@@ -1,32 +0,0 @@
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const CreatedAtFilter = ({ value, onChange, error }) => {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ ثبت"}
|
||||
name={`activity_date_time[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ ثبت"}
|
||||
name={`activity_date_time[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default CreatedAtFilter;
|
||||
@@ -1,65 +0,0 @@
|
||||
"use client";
|
||||
import useEdaratLists from "@/lib/hooks/useEdaratLists";
|
||||
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useWatch } from "react-hook-form";
|
||||
|
||||
const EdarehShahriFilter = ({ control, value, onChange }) => {
|
||||
const dependencyField = useWatch({ control, name: "province_id" });
|
||||
return <EdarehShahriController value={value} onChange={onChange} dependencyField={dependencyField} />;
|
||||
};
|
||||
|
||||
const EdarehShahriController = ({ value, onChange, dependencyField }) => {
|
||||
const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(dependencyField);
|
||||
const [prevDependency, setPrevDependency] = useState(dependencyField);
|
||||
|
||||
const columnSelectOption = useMemo(() => {
|
||||
if (dependencyField === "") {
|
||||
return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }];
|
||||
}
|
||||
if (loadingEdaratList) {
|
||||
return [{ value: "loading", label: "در حال بارگذاری..." }];
|
||||
}
|
||||
if (errorEdaratList) {
|
||||
return [{ value: "error", label: "خطا در بارگذاری" }];
|
||||
}
|
||||
return [
|
||||
{ value: "", label: "کل ادارات" },
|
||||
...edaratList.map((edare) => ({
|
||||
value: edare.id,
|
||||
label: edare.name_fa,
|
||||
})),
|
||||
];
|
||||
}, [edaratList, loadingEdaratList, errorEdaratList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (prevDependency === dependencyField) return;
|
||||
onChange("");
|
||||
setPrevDependency(dependencyField);
|
||||
}, [dependencyField]);
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labeledarat_id`} shrink>
|
||||
اداره
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labeledarat_id`}
|
||||
id={"edarat_id"}
|
||||
name={`edarat_id`}
|
||||
value={dependencyField === "" ? "empty" : loadingEdaratList ? "loading" : value}
|
||||
input={<OutlinedInput label="اداره" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default EdarehShahriFilter;
|
||||
@@ -1,47 +0,0 @@
|
||||
import useSafetyAndPrivacyGetItems from "@/lib/hooks/useSafetyAndPrivacyGetItems";
|
||||
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const InfoFilter = ({ value, onChange }) => {
|
||||
const { itemsList, loadingItemsList, errorItemsList } = useSafetyAndPrivacyGetItems();
|
||||
const columnSelectOption = useMemo(() => {
|
||||
if (loadingItemsList) {
|
||||
return [{ value: "loading", label: "در حال بارگذاری..." }];
|
||||
}
|
||||
if (errorItemsList) {
|
||||
return [{ value: "error", label: "خطا در بارگذاری" }];
|
||||
}
|
||||
return [
|
||||
{ value: "", label: "همه موارد" },
|
||||
...itemsList.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
})),
|
||||
];
|
||||
}, [itemsList, loadingItemsList, errorItemsList]);
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelinfo_id`} shrink>
|
||||
مورد مشاهده شده
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelinfo_id`}
|
||||
id={"info_id"}
|
||||
name={`info_id`}
|
||||
value={loadingItemsList ? "loading" : value}
|
||||
input={<OutlinedInput label="مورد مشاهده شده" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default InfoFilter;
|
||||
@@ -1,32 +0,0 @@
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
const JudiciaryDocumentUploadDateFilter = ({ value, onChange, error }) => {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بارگذاری مستندات"}
|
||||
name={`judiciary_document_upload_date[0]`}
|
||||
value={value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([formattedDate, value[1]]);
|
||||
}}
|
||||
maxDate={value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={"تاریخ بارگذاری مستندات"}
|
||||
name={`judiciary_document_upload_date[1]`}
|
||||
value={value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
onChange([value[0], formattedDate]);
|
||||
}}
|
||||
minDate={value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={error ? error.message : null}
|
||||
error={Boolean(error)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
export default JudiciaryDocumentUploadDateFilter;
|
||||
@@ -1,47 +0,0 @@
|
||||
import useProvinces from "@/lib/hooks/useProvince";
|
||||
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const ProvinceFilter = ({ value, onChange }) => {
|
||||
const { provinces, errorProvinces, loadingProvinces } = useProvinces();
|
||||
const columnSelectOption = useMemo(() => {
|
||||
if (loadingProvinces) {
|
||||
return [{ value: "loading", label: "در حال بارگذاری..." }];
|
||||
}
|
||||
if (errorProvinces) {
|
||||
return [{ value: "error", label: "خطا در بارگذاری" }];
|
||||
}
|
||||
return [
|
||||
{ value: "", label: "کل کشور" },
|
||||
...provinces.map((province) => ({
|
||||
value: province.id,
|
||||
label: province.name_fa,
|
||||
})),
|
||||
];
|
||||
}, [provinces, errorProvinces, loadingProvinces]);
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelprovince_id`} shrink>
|
||||
استان
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelprovince_id`}
|
||||
id={"province_id"}
|
||||
name={`province_id`}
|
||||
value={loadingProvinces ? "loading" : value}
|
||||
input={<OutlinedInput label="استان" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default ProvinceFilter;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
|
||||
|
||||
const StepFilter = ({ value, onChange }) => {
|
||||
const statusOptions = [
|
||||
{ value: "", label: "همه وضعیت ها" },
|
||||
{ value: 1, label: "گام اول (شناسایی)" },
|
||||
{ value: 2, label: "گام دوم (مستندات قضایی)" },
|
||||
{ value: 3, label: "گام سوم (برخورد)" },
|
||||
];
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`labelstep`} shrink>
|
||||
وضعیت
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`labelstep`}
|
||||
id={"step"}
|
||||
name={`step`}
|
||||
value={value}
|
||||
input={<OutlinedInput label="وضعیت" />}
|
||||
size="small"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
{statusOptions.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
export default StepFilter;
|
||||
@@ -1,166 +0,0 @@
|
||||
import ScrollBox from "@/core/components/ScrollBox";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import CancelIcon from "@mui/icons-material/Cancel";
|
||||
import FilterAltIcon from "@mui/icons-material/FilterAlt";
|
||||
import { Box, Button, IconButton, Typography } from "@mui/material";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import * as Yup from "yup";
|
||||
import ActivityDateTimeFilter from "./ActivityDateTimeFilter";
|
||||
import AxisTypeFilter from "./AxisTypeFilter";
|
||||
import EdarehShahriFilter from "./EdarehShahriFilter";
|
||||
import InfoFilter from "./InfoFilter";
|
||||
import ProvinceFilter from "./ProvinceFilter";
|
||||
import StepFilter from "./StepFilter";
|
||||
import CreatedAtFilter from "./CreatedAtFilter";
|
||||
import JudiciaryDocumentUploadDateFilter from "./JudiciaryDocumentUploadDateFilter";
|
||||
import ActionPictureDocumentUploadDateFilter from "./ActionDate";
|
||||
import ActionDate from "./ActionDate";
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
activity_date_time: Yup.array()
|
||||
.of(Yup.string().nullable())
|
||||
.test({
|
||||
test(value, ctx) {
|
||||
const [start, end] = value || ["", ""];
|
||||
if ((start && !end) || (!start && end)) {
|
||||
return ctx.createError({ message: "این بخش را تکمیل نمایید" });
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const headerSx = {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
px: 2,
|
||||
py: 1,
|
||||
backgroundColor: "#155175",
|
||||
boxShadow: "rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px",
|
||||
maxWidth: "450px",
|
||||
};
|
||||
|
||||
const headerTitleSx = { display: "flex", alignItems: "center" };
|
||||
const headerIconSx = { color: "#fff", mr: 1 };
|
||||
const iconButtonSx = { color: "#fff" };
|
||||
|
||||
const formContainerSx = { px: 2, py: 3 };
|
||||
const footerSx = { display: "flex", justifyContent: "center", alignItems: "center", pb: 2 };
|
||||
|
||||
const submitButtonSx = {
|
||||
px: 8,
|
||||
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
|
||||
backgroundColor: "primary2",
|
||||
":hover": { backgroundColor: "primary2" },
|
||||
};
|
||||
|
||||
const FilterDrawer = ({ defaultValues, setFilterData, closeDrawer }) => {
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { isDirty },
|
||||
} = useForm({
|
||||
defaultValues,
|
||||
resolver: yupResolver(validationSchema),
|
||||
mode: "onBlur",
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setFilterData(data);
|
||||
closeDrawer();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={headerSx}>
|
||||
<Box sx={headerTitleSx}>
|
||||
<FilterAltIcon sx={headerIconSx} />
|
||||
<Typography variant="h6" sx={{ color: "#fff" }}>
|
||||
فیلتر
|
||||
</Typography>
|
||||
</Box>
|
||||
<IconButton sx={iconButtonSx} onClick={closeDrawer}>
|
||||
<CancelIcon sx={{ fontSize: "1em" }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<ScrollBox>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Box sx={formContainerSx}>
|
||||
<Controller
|
||||
name="province_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<ProvinceFilter value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="edarat_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<EdarehShahriFilter value={value} onChange={onChange} control={control} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="info_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<InfoFilter value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="axis_type_id"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<AxisTypeFilter value={value} onChange={onChange} control={control} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="step"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<StepFilter value={value} onChange={onChange} control={control} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="created_at"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<CreatedAtFilter value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="activity_date_time"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<ActivityDateTimeFilter value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="judiciary_document_upload_date"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<JudiciaryDocumentUploadDateFilter value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="action_date"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<ActionDate value={value} onChange={onChange} error={error} />
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={footerSx}>
|
||||
<Button type="submit" variant="contained" size="large" disabled={!isDirty} sx={submitButtonSx}>
|
||||
اعمال فیلتر
|
||||
</Button>
|
||||
</Box>
|
||||
</form>
|
||||
</ScrollBox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default FilterDrawer;
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Box, Button, Drawer } from "@mui/material";
|
||||
import FilterListIcon from "@mui/icons-material/FilterList";
|
||||
import { useCallback, useState } from "react";
|
||||
import FilterDrawer from "./Drawer";
|
||||
import FilterDrawer from "@/core/components/FilterDrawer";
|
||||
|
||||
const drawerSx = {
|
||||
overflowY: "hidden",
|
||||
|
||||
@@ -63,7 +63,7 @@ const ContentInfoItem = ({ data }) => {
|
||||
</Stack>
|
||||
)}
|
||||
<Stack spacing={2}>
|
||||
<Stack sx={{ flex: 1, border: 1, borderColor: "divider", borderRadius: 2, p: 1 }} alignItems={"center"}>
|
||||
<Stack sx={{ flex: 1, border: 1, borderColor: "divider", borderRadius: 2, p: 1 }} spacing={1} alignItems={"center"}>
|
||||
<Chip size="small" label="تصویر بازدید" />
|
||||
<Box sx={{ position: "relative", height: 300, width: "100%" }}>
|
||||
<Image
|
||||
@@ -75,18 +75,20 @@ const ContentInfoItem = ({ data }) => {
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack sx={{ flex: 1, border: 1, borderColor: "divider", borderRadius: 2, p: 1 }} alignItems={"center"}>
|
||||
<Chip size="small" label="تصویر اقدام" />
|
||||
<Box sx={{ position: "relative", height: 300, width: "100%" }}>
|
||||
<Image
|
||||
src={data.action_picture}
|
||||
style={{ objectFit: "contain" }}
|
||||
alt="action_picture"
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
{data.step == 3 && (
|
||||
<Stack sx={{ flex: 1, border: 1, borderColor: "divider", borderRadius: 2, p: 1 }} spacing={1} alignItems={"center"}>
|
||||
<Chip size="small" label="تصویر اقدام" />
|
||||
<Box sx={{ position: "relative", height: 300, width: "100%" }}>
|
||||
<Image
|
||||
src={data.action_picture}
|
||||
style={{ objectFit: "contain" }}
|
||||
alt="action_picture"
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,31 +1,40 @@
|
||||
"use client";
|
||||
import LoadingHardPage from "@/core/components/LoadingHardPage";
|
||||
import MapLoading from "@/core/components/MapLayer/Loading";
|
||||
import isArrayEmpty from "@/core/utils/isArrayEmpty";
|
||||
import { GET_ROAD_SAFETY_REPORT_MAP } from "@/core/utils/routes";
|
||||
import useRequest from "@/lib/hooks/useRequest";
|
||||
import { Box, Stack } from "@mui/material";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import ClusterSwitch from "./ClusterSwitch";
|
||||
import Filter from "./Filter";
|
||||
import Legend from "./Legend";
|
||||
import PointsOnMap from "./PointsOnMap";
|
||||
import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure";
|
||||
import useProvinces from "@/lib/hooks/useProvince";
|
||||
import CustomSelectByDependency from "@/core/components/FilterDrawer/fieldsType/CustomSelectByDependency";
|
||||
import useEdaratLists from "@/lib/hooks/useEdaratLists";
|
||||
import useSafetyAndPrivacyGetItems from "@/lib/hooks/useSafetyAndPrivacyGetItems";
|
||||
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
|
||||
loading: () => <MapLoading />,
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const defaultValues = {
|
||||
province_id: "",
|
||||
edarat_id: "",
|
||||
info_id: "",
|
||||
axis_type_id: "",
|
||||
step: "",
|
||||
created_at: ["", ""],
|
||||
activity_date_time: ["", ""],
|
||||
judiciary_document_upload_date: ["", ""],
|
||||
action_date: ["", ""],
|
||||
};
|
||||
const statusOptions = [
|
||||
{ value: "", label: "همه وضعیت ها" },
|
||||
{ value: 1, label: "گام اول (شناسایی)" },
|
||||
{ value: 2, label: "گام دوم (مستندات قضایی)" },
|
||||
{ value: 3, label: "گام سوم (برخورد)" },
|
||||
];
|
||||
const axisOptions = [
|
||||
{ value: "", label: "همه محور ها" },
|
||||
{ value: 1, label: "آزادراه" },
|
||||
{ value: 2, label: "بزرگراه" },
|
||||
{ value: 3, label: "اصلی" },
|
||||
{ value: 4, label: "فرعی" },
|
||||
{ value: 5, label: "روستایی" },
|
||||
];
|
||||
|
||||
const MAX_POINTS = 1000;
|
||||
|
||||
@@ -44,62 +53,205 @@ const controlBarStyles = {
|
||||
};
|
||||
|
||||
const RoadSafetyReportMap = () => {
|
||||
const defaultValues = useMemo(
|
||||
() => [
|
||||
{
|
||||
header: "استان",
|
||||
id: "province_id",
|
||||
filterMode: "equals",
|
||||
datatype: "numeric",
|
||||
value: "",
|
||||
SelectComponent: (props) => {
|
||||
const { provinces, errorProvinces, loadingProvinces } = useProvinces();
|
||||
const getSelectOptions = 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}
|
||||
selectOption={getSelectOptions}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
eader: "اداره",
|
||||
id: "edare_shahri_id",
|
||||
filterMode: "equals",
|
||||
datatype: "numeric",
|
||||
dependencyId: "province_id",
|
||||
value: "",
|
||||
SelectComponent: (props) => {
|
||||
const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(
|
||||
props.dependencyFieldValue.value
|
||||
);
|
||||
const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value);
|
||||
|
||||
const getSelectOptions = 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
|
||||
}
|
||||
selectOption={getSelectOptions}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "مورد مشاهده شده",
|
||||
id: "info_id",
|
||||
filterMode: "equals",
|
||||
datatype: "numeric",
|
||||
value: "",
|
||||
SelectComponent: (props) => {
|
||||
const { itemsList, loadingItemsList, errorItemsList } = useSafetyAndPrivacyGetItems();
|
||||
|
||||
const getSelectOptions = useMemo(() => {
|
||||
if (loadingItemsList) {
|
||||
return [{ value: "loading", label: "در حال بارگذاری..." }];
|
||||
}
|
||||
if (errorItemsList) {
|
||||
return [{ value: "error", label: "خطا در بارگذاری" }];
|
||||
}
|
||||
return [
|
||||
{ value: "", label: "همه موارد" },
|
||||
...itemsList.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
})),
|
||||
];
|
||||
}, [itemsList, loadingItemsList, errorItemsList]);
|
||||
|
||||
return (
|
||||
<CustomSelectByDependency
|
||||
{...props}
|
||||
value={loadingItemsList ? "loading" : props.filterParameters.value}
|
||||
selectOption={getSelectOptions}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "نوع محور",
|
||||
id: "axis_type_id",
|
||||
filterMode: "equals",
|
||||
datatype: "numeric",
|
||||
value: "",
|
||||
selectOption: () => {
|
||||
return axisOptions.map((status) => ({
|
||||
value: status.value,
|
||||
label: status.label,
|
||||
}));
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "وضعیت",
|
||||
id: "step",
|
||||
filterMode: "equals",
|
||||
datatype: "numeric",
|
||||
value: "",
|
||||
selectOption: () => {
|
||||
return statusOptions.map((status) => ({
|
||||
value: status.value,
|
||||
label: status.label,
|
||||
}));
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "تاریخ ثبت",
|
||||
id: "created_at",
|
||||
filterMode: "between",
|
||||
datatype: "date",
|
||||
value: ["", ""],
|
||||
},
|
||||
{
|
||||
header: "تاریخ بازدید",
|
||||
id: "activity_date_time",
|
||||
filterMode: "between",
|
||||
datatype: "date",
|
||||
value: ["", ""],
|
||||
},
|
||||
{
|
||||
header: "تاریخ بارگذاری مستندات",
|
||||
id: "judiciary_document_upload_date",
|
||||
filterMode: "between",
|
||||
datatype: "date",
|
||||
value: ["", ""],
|
||||
},
|
||||
{
|
||||
header: "تاریخ اقدام",
|
||||
id: "action_date",
|
||||
filterMode: "between",
|
||||
datatype: "date",
|
||||
value: ["", ""],
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
const requestServer = useRequest();
|
||||
const [filterData, setFilterData] = useState(defaultValues);
|
||||
const [filterData, setFilterData] = useState(
|
||||
defaultValues.reduce((acc, item) => {
|
||||
acc[item.id] = item;
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
const [isCluster, setCluster] = useState(false);
|
||||
const [data, setData] = useState([]);
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
|
||||
const buildQueryParams = useCallback(() => {
|
||||
const params = new URLSearchParams();
|
||||
const {
|
||||
province_id,
|
||||
edarat_id,
|
||||
info_id,
|
||||
axis_type_id,
|
||||
step,
|
||||
created_at,
|
||||
activity_date_time,
|
||||
judiciary_document_upload_date,
|
||||
action_date,
|
||||
} = filterData;
|
||||
|
||||
const filterArray = [];
|
||||
|
||||
if (province_id) filterArray.push({ id: "province_id", fn: "equals", datatype: "numeric", value: province_id });
|
||||
if (edarat_id) filterArray.push({ id: "edarat_id", fn: "equals", datatype: "numeric", value: edarat_id });
|
||||
if (info_id) filterArray.push({ id: "info_id", fn: "equals", datatype: "numeric", value: info_id });
|
||||
if (axis_type_id)
|
||||
filterArray.push({ id: "axis_type_id", fn: "equals", datatype: "numeric", value: axis_type_id });
|
||||
if (step) filterArray.push({ id: "step", fn: "equals", datatype: "numeric", value: step });
|
||||
if (created_at[0]) filterArray.push({ id: "created_at", fn: "between", datatype: "date", value: created_at });
|
||||
if (activity_date_time[0])
|
||||
filterArray.push({ id: "activity_date_time", fn: "between", datatype: "date", value: activity_date_time });
|
||||
if (judiciary_document_upload_date[0])
|
||||
filterArray.push({
|
||||
id: "judiciary_document_upload_date",
|
||||
fn: "between",
|
||||
datatype: "date",
|
||||
value: judiciary_document_upload_date,
|
||||
});
|
||||
if (action_date[0])
|
||||
filterArray.push({ id: "action_date", fn: "between", datatype: "date", value: action_date });
|
||||
|
||||
console.log(filterArray);
|
||||
|
||||
params.set("filters", JSON.stringify(filterArray || []));
|
||||
const filters = DataTableFilterDataStructure(filterData, isArrayEmpty);
|
||||
params.set("filters", JSON.stringify(filters || []));
|
||||
return params.toString();
|
||||
}, [filterData]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("gasgsa");
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const query = buildQueryParams();
|
||||
console.log("amir");
|
||||
|
||||
const response = await requestServer(`${GET_ROAD_SAFETY_REPORT_MAP}?${query}`);
|
||||
const fetchedData = response?.data?.data || [];
|
||||
|
||||
@@ -117,8 +269,6 @@ const RoadSafetyReportMap = () => {
|
||||
fetchData();
|
||||
}, [filterData, buildQueryParams]);
|
||||
|
||||
console.log("sfa");
|
||||
|
||||
return (
|
||||
<Box sx={containerStyles}>
|
||||
<MapLayer style={{ borderRadius: "4px" }}>
|
||||
|
||||
25
src/core/components/FilterDrawer/FilterController.jsx
Normal file
25
src/core/components/FilterDrawer/FilterController.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Controller } from "react-hook-form";
|
||||
import FilterField from "./FilterField";
|
||||
|
||||
const FilterController = ({ item, control, reset, errors }) => {
|
||||
return (
|
||||
<Controller
|
||||
name={`${item.id}`}
|
||||
control={control}
|
||||
render={({ field: { value, onBlur, onChange } }) => {
|
||||
return (
|
||||
<FilterField
|
||||
item={item}
|
||||
filterParameters={value}
|
||||
handleChange={onChange}
|
||||
handleBlur={onBlur}
|
||||
dependencyFieldValue={null}
|
||||
resetForm={() => reset()}
|
||||
errors={errors}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default FilterController;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Controller, useWatch } from "react-hook-form";
|
||||
import FilterField from "./FilterField";
|
||||
|
||||
const FilterControllerWithDependency = ({ item, control, reset, errors }) => {
|
||||
const dependencyField = useWatch({ control, name: item.dependencyId });
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={`${item.id}`}
|
||||
control={control}
|
||||
render={({ field: { value, name, onBlur, onChange } }) => {
|
||||
return (
|
||||
<FilterField
|
||||
item={item}
|
||||
filterParameters={value}
|
||||
handleChange={onChange}
|
||||
handleBlur={onBlur}
|
||||
dependencyFieldValue={dependencyField}
|
||||
resetForm={() => reset()}
|
||||
errors={errors}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export default FilterControllerWithDependency;
|
||||
103
src/core/components/FilterDrawer/FilterField.jsx
Normal file
103
src/core/components/FilterDrawer/FilterField.jsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import { useState } from "react";
|
||||
import CustomTextFieldRange from "./fieldsType/CustomTextFieldRange";
|
||||
import CustomSelect from "./fieldsType/CustomSelect";
|
||||
import CustomDatePicker from "../CustomDatePicker";
|
||||
import CustomDatePickerRange from "./fieldsType/CustomDate/CustomDatePickerRange";
|
||||
import CustomSelectMultiple from "./fieldsType/CustomSelectMultiple";
|
||||
import CustomTextField from "./fieldsType/CustomTextField";
|
||||
import FilterOptionList from "./FilterOptionList";
|
||||
|
||||
const filterModeOptionFa = {
|
||||
equals: "برابر",
|
||||
notEquals: "نابرابر",
|
||||
contains: "شامل",
|
||||
lessThan: "کوچکتر",
|
||||
greaterThan: "بزرگتر",
|
||||
fuzzy: "فازی",
|
||||
between: "مابین",
|
||||
};
|
||||
|
||||
function FilterField({
|
||||
item,
|
||||
filterParameters,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
dependencyFieldValue,
|
||||
setFieldValue,
|
||||
resetForm,
|
||||
errors,
|
||||
}) {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const defaultFilterTranslation = filterModeOptionFa[filterParameters.filterMode] || filterParameters.filterMode;
|
||||
|
||||
const handleOpenFilterBox = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const renderField = () => {
|
||||
const commonProps = {
|
||||
item,
|
||||
filterParameters,
|
||||
defaultFilterTranslation,
|
||||
handleOpenFilterBox,
|
||||
dependencyFieldValue,
|
||||
setFieldValue,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
errors,
|
||||
};
|
||||
|
||||
switch (filterParameters.datatype) {
|
||||
case "numeric":
|
||||
if (filterParameters.filterMode === "between") {
|
||||
return <CustomTextFieldRange {...commonProps} />;
|
||||
}
|
||||
if (filterParameters.filterMode === "equals") {
|
||||
return item.SelectComponent ? (
|
||||
<item.SelectComponent {...commonProps} />
|
||||
) : (
|
||||
<CustomSelect {...commonProps} />
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "date":
|
||||
if (filterParameters.filterMode === "equals") {
|
||||
return <CustomDatePicker {...commonProps} />;
|
||||
}
|
||||
if (filterParameters.filterMode === "between") {
|
||||
return <CustomDatePickerRange {...commonProps} />;
|
||||
}
|
||||
break;
|
||||
|
||||
case "array":
|
||||
if (filterParameters.filterMode === "equals") {
|
||||
return <CustomSelectMultiple {...commonProps} />;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return <CustomTextField {...commonProps} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderField()}
|
||||
{Array.isArray(item.filterModeOptions) && (
|
||||
<FilterOptionList
|
||||
item={item}
|
||||
anchorEl={anchorEl}
|
||||
setAnchorEl={setAnchorEl}
|
||||
filterParameters={filterParameters}
|
||||
filterType={filterParameters.filterMode}
|
||||
handleChange={handleChange}
|
||||
resetForm={resetForm}
|
||||
filterOption={item.filterModeOptions}
|
||||
filterModeOptionFa={filterModeOptionFa}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterField;
|
||||
47
src/core/components/FilterDrawer/FilterOptionList.jsx
Normal file
47
src/core/components/FilterDrawer/FilterOptionList.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
"use client";
|
||||
|
||||
import { ListItem, Menu } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
|
||||
function FilterOptionList({
|
||||
filterType,
|
||||
filterOption,
|
||||
filterParameters,
|
||||
anchorEl,
|
||||
filterModeOptionFa,
|
||||
setAnchorEl,
|
||||
handleChange,
|
||||
}) {
|
||||
const [selectedFilter, setSelectedFilter] = useState(filterType);
|
||||
|
||||
const handleChangeItem = (event, index) => {
|
||||
handleChange({
|
||||
...filterParameters,
|
||||
value: filterOption[index] === "between" ? ["", ""] : "",
|
||||
filterMode: filterOption[index],
|
||||
});
|
||||
setSelectedFilter(filterOption[index]);
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleCloseFilterBox = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleCloseFilterBox}>
|
||||
{filterOption.map((option, index) => (
|
||||
<ListItem
|
||||
key={index}
|
||||
onClick={(event) => handleChangeItem(event, index)}
|
||||
selected={option === selectedFilter}
|
||||
sx={{ cursor: "pointer", width: "100px", display: "flex", justifyContent: "center" }}
|
||||
>
|
||||
{filterModeOptionFa[option]}
|
||||
</ListItem>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterOptionList;
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
function CustomDatePicker({ column, filterParameters, defaultFilterTranslation, handleChange }) {
|
||||
return (
|
||||
<MuiDatePicker
|
||||
name={`${column.id}.value`}
|
||||
value={filterParameters.value}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
handleChange({ ...filterParameters, value: formattedDate });
|
||||
}}
|
||||
placeholder={column.header}
|
||||
helperText={
|
||||
<Typography variant="button" sx={{ color: "#155175" }}>
|
||||
نوع فیلتر: {defaultFilterTranslation} (تاریخ)
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomDatePicker;
|
||||
@@ -0,0 +1,41 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
|
||||
function CustomDatePickerRange({ item, filterParameters, defaultFilterTranslation, handleChange, errors }) {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<MuiDatePicker
|
||||
label={item.header}
|
||||
name={`${item.id}.value[0]`}
|
||||
value={filterParameters.value[0]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
handleChange({ ...filterParameters, value: [formattedDate, filterParameters.value[1]] });
|
||||
}}
|
||||
maxDate={filterParameters.value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
helperText={
|
||||
<Typography variant="button" sx={{ color: "#155175" }}>
|
||||
نوع فیلتر: {defaultFilterTranslation} (تاریخ)
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
label={item.header}
|
||||
name={`${item.id}.value[1]`}
|
||||
value={filterParameters.value[1]}
|
||||
setFieldValue={(name, formattedDate) => {
|
||||
handleChange({ ...filterParameters, value: [filterParameters.value[0], formattedDate] });
|
||||
}}
|
||||
minDate={filterParameters.value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={errors?.[`${item.id}`]?.value ? errors?.[`${item.id}`]?.value.message : null}
|
||||
error={Boolean(errors?.[`${item.id}`]?.value)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomDatePickerRange;
|
||||
@@ -0,0 +1,72 @@
|
||||
import React from "react";
|
||||
import { LocalizationProvider, MobileDatePicker } from "@mui/x-date-pickers";
|
||||
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
|
||||
import { faIR } from "@mui/x-date-pickers/locales";
|
||||
import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material";
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import moment from "jalali-moment";
|
||||
|
||||
function MuiDatePicker({ label, value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error }) {
|
||||
return (
|
||||
<Box sx={{ display: "flex", flexDirection: "column", my: 1 }}>
|
||||
<LocalizationProvider
|
||||
dateAdapter={AdapterDateFnsJalali}
|
||||
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
|
||||
>
|
||||
<MobileDatePicker
|
||||
value={value ? new Date(value) : null}
|
||||
sx={{ width: "100%" }}
|
||||
id={name}
|
||||
label={label}
|
||||
name={name}
|
||||
closeOnSelect={true}
|
||||
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: {
|
||||
size: "small",
|
||||
error: error,
|
||||
placeholder: placeholder,
|
||||
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>
|
||||
),
|
||||
},
|
||||
InputLabelProps: {
|
||||
shrink: true,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{/* <FormHelperText id="component-helper-text" variant="caption" sx={{ ml: 2, color: error ? "#d32f2f" : "unset", fontWeight: 'bold' }}>
|
||||
{helperText ? helperText : ""}
|
||||
</FormHelperText> */}
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default MuiDatePicker;
|
||||
30
src/core/components/FilterDrawer/fieldsType/CustomSelect.jsx
Normal file
30
src/core/components/FilterDrawer/fieldsType/CustomSelect.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
|
||||
function CustomSelect({ item, filterParameters, handleChange }) {
|
||||
return (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel id={`label${item.id}`} shrink>
|
||||
{item.header}
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`label${item.id}`}
|
||||
id={item.id}
|
||||
name={`${item.id}.value`}
|
||||
value={filterParameters.value}
|
||||
input={<OutlinedInput label={item.header} />}
|
||||
size="small"
|
||||
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
|
||||
displayEmpty
|
||||
>
|
||||
{item.selectOption().map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomSelect;
|
||||
@@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material";
|
||||
function CustomSelectByDependency({ item, filterParameters, value, handleChange, selectOption }) {
|
||||
return (
|
||||
<FormControl fullWidth sx={{ my: 1 }} size="small">
|
||||
<InputLabel id={`label${item.id}`} shrink>
|
||||
{item.header}
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`label${item.id}`}
|
||||
id={item.id}
|
||||
name={`${item.id}.value`}
|
||||
value={value}
|
||||
size="small"
|
||||
input={<OutlinedInput label={item.header} />}
|
||||
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
|
||||
displayEmpty
|
||||
>
|
||||
{selectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomSelectByDependency;
|
||||
@@ -0,0 +1,55 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Box,
|
||||
Chip,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
OutlinedInput,
|
||||
Select,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
function CustomSelectMultiple({ item, filterParameters, handleChange }) {
|
||||
const selectOption = item.selectOption;
|
||||
|
||||
const getLabelForValue = (value) => {
|
||||
const option = selectOption.find((opt) => opt.value === value);
|
||||
return option ? option.label : value;
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{ marginY: 1 }} size="small">
|
||||
<InputLabel htmlFor="uncontrolled-native" id={`label${item.id}`}>
|
||||
{item.header}
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId={`label${item.id}`}
|
||||
id={item.id}
|
||||
value={filterParameters.value}
|
||||
multiple
|
||||
size="small"
|
||||
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={getLabelForValue(value)} />
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
input={<OutlinedInput label={item.header} />}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
>
|
||||
{selectOption.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomSelectMultiple;
|
||||
@@ -0,0 +1,29 @@
|
||||
import { InputAdornment, TextField, Typography } from "@mui/material";
|
||||
import FilterListIcon from "@mui/icons-material/FilterList";
|
||||
|
||||
function CustomTextField({ item, filterParameters, handleOpenFilterBox, handleBlur, handleChange }) {
|
||||
return (
|
||||
<TextField
|
||||
id={item.id}
|
||||
name={item.id}
|
||||
label={item.header}
|
||||
value={filterParameters.value}
|
||||
onChange={(e) => handleChange({ ...filterParameters, value: e.target.value })}
|
||||
onBlur={handleBlur}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
size="small"
|
||||
sx={{ my: 1 }}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}>
|
||||
<FilterListIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomTextField;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Box, InputAdornment, TextField, Typography } from "@mui/material";
|
||||
import FilterListIcon from "@mui/icons-material/FilterList";
|
||||
|
||||
function CustomTextFieldRange({
|
||||
item,
|
||||
defaultFilterTranslation,
|
||||
handleOpenFilterBox,
|
||||
handleChange,
|
||||
filterParameters,
|
||||
handleBlur,
|
||||
errors,
|
||||
}) {
|
||||
return (
|
||||
<Box sx={{ display: "flex" }}>
|
||||
<TextField
|
||||
id={`${item.id}.value[0]`}
|
||||
name={`${item.id}.value[0]`}
|
||||
onChange={(e) =>
|
||||
handleChange({ ...filterParameters, value: [e.target.value, filterParameters.value[1]] })
|
||||
}
|
||||
onBlur={handleBlur}
|
||||
label={<Typography>از {item.header}</Typography>}
|
||||
value={filterParameters.value[0]}
|
||||
fullWidth
|
||||
error={touched?.[`${item.id}`]?.value && Boolean(errors?.[`${item.id}`]?.value)}
|
||||
helperText={
|
||||
<Typography variant="caption" sx={{ color: "#155175", fontWeight: "bold" }}>
|
||||
نوع فیلتر: {defaultFilterTranslation}
|
||||
</Typography>
|
||||
}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
sx={{ my: 1, marginRight: 1 }}
|
||||
/>
|
||||
<TextField
|
||||
id={`${item.id}.value[1]`}
|
||||
name={`${item.id}.value[1]`}
|
||||
onChange={(e) =>
|
||||
handleChange({ ...filterParameters, value: [filterParameters.value[0], e.target.value] })
|
||||
}
|
||||
onBlur={handleBlur}
|
||||
error={Boolean(errors?.[`${item.id}`]?.value)}
|
||||
helperText={errors?.[`${item.id}`]?.value ? errors?.[`${item.id}`]?.value.message : null}
|
||||
label={<Typography>تا {item.header}</Typography>}
|
||||
value={filterParameters.value[1]}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
size="small"
|
||||
sx={{ my: 1 }}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}>
|
||||
<FilterListIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomTextFieldRange;
|
||||
151
src/core/components/FilterDrawer/index.jsx
Normal file
151
src/core/components/FilterDrawer/index.jsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { Cancel, FilterAlt } from "@mui/icons-material";
|
||||
import { Box, Button, IconButton, Typography } from "@mui/material";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as Yup from "yup";
|
||||
import ScrollBox from "../ScrollBox";
|
||||
import FilterController from "./FilterController";
|
||||
import FilterControllerWithDependency from "./FilterControllerWithDependency";
|
||||
|
||||
const headerSx = {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
px: 2,
|
||||
py: 1,
|
||||
backgroundColor: "#155175",
|
||||
boxShadow: "rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px",
|
||||
maxWidth: "450px",
|
||||
};
|
||||
|
||||
const headerTitleSx = { display: "flex", alignItems: "center" };
|
||||
const headerIconSx = { color: "#fff", mr: 1 };
|
||||
const iconButtonSx = { color: "#fff" };
|
||||
|
||||
const formContainerSx = { px: 2, py: 3 };
|
||||
const footerSx = { display: "flex", justifyContent: "center", alignItems: "center", pb: 2 };
|
||||
|
||||
const submitButtonSx = {
|
||||
px: 8,
|
||||
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
|
||||
backgroundColor: "primary2",
|
||||
":hover": { backgroundColor: "primary2" },
|
||||
};
|
||||
|
||||
const validationSchema = Yup.object({
|
||||
activity_date_time: Yup.array()
|
||||
.of(Yup.string().nullable())
|
||||
.test({
|
||||
test(value, ctx) {
|
||||
const [start, end] = value || ["", ""];
|
||||
if ((start && !end) || (!start && end)) {
|
||||
return ctx.createError({ message: "این بخش را تکمیل نمایید" });
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const FilterDrawer = ({ defaultValues, setFilterData, closeDrawer }) => {
|
||||
const {
|
||||
control,
|
||||
errors,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { isDirty },
|
||||
} = useForm({
|
||||
defaultValues,
|
||||
resolver: yupResolver(
|
||||
Yup.object(
|
||||
Object.keys(defaultValues).reduce((acc, key) => {
|
||||
const initialValue = defaultValues[key];
|
||||
if (initialValue.filterMode === "between") {
|
||||
acc[key] = Yup.object().shape({
|
||||
value: Yup.array()
|
||||
.of(Yup.string().nullable())
|
||||
.test({
|
||||
test(value, ctx) {
|
||||
const [start, end] = value || ["", ""];
|
||||
if (
|
||||
initialValue.datatype === "numeric" &&
|
||||
parseInt(end, 10) <= parseInt(start, 10)
|
||||
) {
|
||||
return ctx.createError({
|
||||
message: `مقدار وارده باید بیشتر از (${start}) باشد`,
|
||||
});
|
||||
} else if ((start && !end) || (!start && end)) {
|
||||
return ctx.createError({
|
||||
message: "این بخش را تکمیل نمایید",
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, {})
|
||||
)
|
||||
),
|
||||
mode: "onBlur",
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setFilterData(data);
|
||||
closeDrawer();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={headerSx}>
|
||||
<Box sx={headerTitleSx}>
|
||||
<FilterAlt sx={headerIconSx} />
|
||||
<Typography variant="h6" sx={{ color: "#fff" }}>
|
||||
فیلتر
|
||||
</Typography>
|
||||
</Box>
|
||||
<IconButton sx={iconButtonSx} onClick={closeDrawer}>
|
||||
<Cancel sx={{ fontSize: "1em" }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<ScrollBox>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Box sx={formContainerSx}>
|
||||
{Object.values(defaultValues).map((item) =>
|
||||
item.dependencyId ? (
|
||||
<FilterControllerWithDependency
|
||||
key={item.id}
|
||||
item={item}
|
||||
control={control}
|
||||
errors={errors}
|
||||
reset={reset}
|
||||
/>
|
||||
) : (
|
||||
<FilterController
|
||||
key={item.id}
|
||||
item={item}
|
||||
control={control}
|
||||
errors={errors}
|
||||
reset={reset}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
|
||||
<Box sx={footerSx}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
disabled={!isDirty}
|
||||
sx={submitButtonSx}
|
||||
>
|
||||
اعمال فیلتر
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</form>
|
||||
</ScrollBox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default FilterDrawer;
|
||||
Reference in New Issue
Block a user