diff --git a/src/components/dashboard/roadSafety/operator/OperatorList.jsx b/src/components/dashboard/roadSafety/operator/OperatorList.jsx index 0e889da..13a530c 100644 --- a/src/components/dashboard/roadSafety/operator/OperatorList.jsx +++ b/src/components/dashboard/roadSafety/operator/OperatorList.jsx @@ -121,8 +121,8 @@ const OperatorList = () => { props.dependencyFieldValue?.value === "" ? "empty" : loadingEdaratList - ? "loading" - : props.filterParameters.value + ? "loading" + : props.filterParameters.value } columnSelectOption={getColumnSelectOptions} /> diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActionDate.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActionDate.jsx deleted file mode 100644 index 431c7ac..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActionDate.jsx +++ /dev/null @@ -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 ( - - { - onChange([formattedDate, value[1]]); - }} - maxDate={value[1]} - placeholder={`از تاریخ`} - /> - { - onChange([value[0], formattedDate]); - }} - minDate={value[0]} - placeholder={`تا تاریخ`} - helperText={error ? error.message : null} - error={Boolean(error)} - /> - - ); -}; -export default ActionDate; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActivityDateTimeFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActivityDateTimeFilter.jsx deleted file mode 100644 index 143c0cd..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ActivityDateTimeFilter.jsx +++ /dev/null @@ -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 ( - - { - onChange([formattedDate, value[1]]); - }} - maxDate={value[1]} - placeholder={`از تاریخ`} - /> - { - onChange([value[0], formattedDate]); - }} - minDate={value[0]} - placeholder={`تا تاریخ`} - helperText={error ? error.message : null} - error={Boolean(error)} - /> - - ); -}; -export default ActivityDateTimeFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/AxisTypeFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/AxisTypeFilter.jsx deleted file mode 100644 index 037a6a2..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/AxisTypeFilter.jsx +++ /dev/null @@ -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 ( - - - نوع محور - - - - ); -}; -export default AxisTypeFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/CreatedAtFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/CreatedAtFilter.jsx deleted file mode 100644 index 1bcffba..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/CreatedAtFilter.jsx +++ /dev/null @@ -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 ( - - { - onChange([formattedDate, value[1]]); - }} - maxDate={value[1]} - placeholder={`از تاریخ`} - /> - { - onChange([value[0], formattedDate]); - }} - minDate={value[0]} - placeholder={`تا تاریخ`} - helperText={error ? error.message : null} - error={Boolean(error)} - /> - - ); -}; -export default CreatedAtFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/EdarehShahriFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/EdarehShahriFilter.jsx deleted file mode 100644 index eb08e27..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/EdarehShahriFilter.jsx +++ /dev/null @@ -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 ; -}; - -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 ( - - - اداره - - - - ); -}; -export default EdarehShahriFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/InfoFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/InfoFilter.jsx deleted file mode 100644 index 605c0af..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/InfoFilter.jsx +++ /dev/null @@ -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 ( - - - مورد مشاهده شده - - - - ); -}; -export default InfoFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/JudiciaryDocumentUploadDateFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/JudiciaryDocumentUploadDateFilter.jsx deleted file mode 100644 index 24a6b2f..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/JudiciaryDocumentUploadDateFilter.jsx +++ /dev/null @@ -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 ( - - { - onChange([formattedDate, value[1]]); - }} - maxDate={value[1]} - placeholder={`از تاریخ`} - /> - { - onChange([value[0], formattedDate]); - }} - minDate={value[0]} - placeholder={`تا تاریخ`} - helperText={error ? error.message : null} - error={Boolean(error)} - /> - - ); -}; -export default JudiciaryDocumentUploadDateFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ProvinceFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ProvinceFilter.jsx deleted file mode 100644 index 4de3f9e..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/ProvinceFilter.jsx +++ /dev/null @@ -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 ( - - - استان - - - - ); -}; -export default ProvinceFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/StepFilter.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/StepFilter.jsx deleted file mode 100644 index b6eca29..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/StepFilter.jsx +++ /dev/null @@ -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 ( - - - وضعیت - - - - ); -}; -export default StepFilter; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/index.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/index.jsx deleted file mode 100644 index 1a39633..0000000 --- a/src/components/dashboard/roadSafety/reports/map/Filter/Drawer/index.jsx +++ /dev/null @@ -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 ( - <> - - - - - فیلتر - - - - - - - - -
- - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - - - - - -
-
- - ); -}; -export default FilterDrawer; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx index c12a511..3cbbe5d 100644 --- a/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx +++ b/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx @@ -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", diff --git a/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx index bcfc03d..50957e5 100644 --- a/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx +++ b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx @@ -63,7 +63,7 @@ const ContentInfoItem = ({ data }) => { )} - + { /> - - - - action_picture - - + {data.step == 3 && ( + + + + action_picture + + + )} ); diff --git a/src/components/dashboard/roadSafety/reports/map/index.jsx b/src/components/dashboard/roadSafety/reports/map/index.jsx index 907f90b..37bc5cd 100644 --- a/src/components/dashboard/roadSafety/reports/map/index.jsx +++ b/src/components/dashboard/roadSafety/reports/map/index.jsx @@ -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: () => , 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 ( + + ); + }, + }, + { + 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 ( + + ); + }, + }, + { + 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 ( + + ); + }, + }, + { + 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 ( diff --git a/src/core/components/FilterDrawer/FilterController.jsx b/src/core/components/FilterDrawer/FilterController.jsx new file mode 100644 index 0000000..4a6a125 --- /dev/null +++ b/src/core/components/FilterDrawer/FilterController.jsx @@ -0,0 +1,25 @@ +import { Controller } from "react-hook-form"; +import FilterField from "./FilterField"; + +const FilterController = ({ item, control, reset, errors }) => { + return ( + { + return ( + reset()} + errors={errors} + /> + ); + }} + /> + ); +}; +export default FilterController; diff --git a/src/core/components/FilterDrawer/FilterControllerWithDependency.jsx b/src/core/components/FilterDrawer/FilterControllerWithDependency.jsx new file mode 100644 index 0000000..361941f --- /dev/null +++ b/src/core/components/FilterDrawer/FilterControllerWithDependency.jsx @@ -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 ( + { + return ( + reset()} + errors={errors} + /> + ); + }} + /> + ); +}; +export default FilterControllerWithDependency; diff --git a/src/core/components/FilterDrawer/FilterField.jsx b/src/core/components/FilterDrawer/FilterField.jsx new file mode 100644 index 0000000..ed86df8 --- /dev/null +++ b/src/core/components/FilterDrawer/FilterField.jsx @@ -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 ; + } + if (filterParameters.filterMode === "equals") { + return item.SelectComponent ? ( + + ) : ( + + ); + } + break; + case "date": + if (filterParameters.filterMode === "equals") { + return ; + } + if (filterParameters.filterMode === "between") { + return ; + } + break; + + case "array": + if (filterParameters.filterMode === "equals") { + return ; + } + break; + + default: + return ; + } + }; + + return ( + <> + {renderField()} + {Array.isArray(item.filterModeOptions) && ( + + )} + + ); +} + +export default FilterField; diff --git a/src/core/components/FilterDrawer/FilterOptionList.jsx b/src/core/components/FilterDrawer/FilterOptionList.jsx new file mode 100644 index 0000000..1a797f6 --- /dev/null +++ b/src/core/components/FilterDrawer/FilterOptionList.jsx @@ -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 ( + + {filterOption.map((option, index) => ( + handleChangeItem(event, index)} + selected={option === selectedFilter} + sx={{ cursor: "pointer", width: "100px", display: "flex", justifyContent: "center" }} + > + {filterModeOptionFa[option]} + + ))} + + ); +} + +export default FilterOptionList; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomDate/CustomDatePicker.jsx b/src/core/components/FilterDrawer/fieldsType/CustomDate/CustomDatePicker.jsx new file mode 100644 index 0000000..e207cfb --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomDate/CustomDatePicker.jsx @@ -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 ( + { + handleChange({ ...filterParameters, value: formattedDate }); + }} + placeholder={column.header} + helperText={ + + نوع فیلتر: {defaultFilterTranslation} (تاریخ) + + } + /> + ); +} + +export default CustomDatePicker; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomDate/CustomDatePickerRange.jsx b/src/core/components/FilterDrawer/fieldsType/CustomDate/CustomDatePickerRange.jsx new file mode 100644 index 0000000..58c597f --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomDate/CustomDatePickerRange.jsx @@ -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 ( + + { + handleChange({ ...filterParameters, value: [formattedDate, filterParameters.value[1]] }); + }} + maxDate={filterParameters.value[1]} + placeholder={`از تاریخ`} + helperText={ + + نوع فیلتر: {defaultFilterTranslation} (تاریخ) + + } + /> + { + 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)} + /> + + ); +} + +export default CustomDatePickerRange; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomDate/MuiDatePicker.jsx b/src/core/components/FilterDrawer/fieldsType/CustomDate/MuiDatePicker.jsx new file mode 100644 index 0000000..d2d9df0 --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomDate/MuiDatePicker.jsx @@ -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 ( + + + { + 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: ( + + { + event.stopPropagation(); + setFieldValue(name, ""); + }} + sx={{ + color: "#bfbfbf", + "&:hover": { + backgroundColor: "rgba(189, 189, 189, 0.1)", + color: "#363434", + }, + }} + > + + + + ), + }, + InputLabelProps: { + shrink: true, + }, + }, + }} + /> + {/* + {helperText ? helperText : ""} + */} + + + ); +} + +export default MuiDatePicker; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomSelect.jsx b/src/core/components/FilterDrawer/fieldsType/CustomSelect.jsx new file mode 100644 index 0000000..61172e6 --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomSelect.jsx @@ -0,0 +1,30 @@ +"use client"; + +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; +function CustomSelect({ item, filterParameters, handleChange }) { + return ( + + + {item.header} + + + + ); +} + +export default CustomSelect; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomSelectByDependency.jsx b/src/core/components/FilterDrawer/fieldsType/CustomSelectByDependency.jsx new file mode 100644 index 0000000..69536cd --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomSelectByDependency.jsx @@ -0,0 +1,30 @@ +"use client"; + +import { FormControl, InputLabel, MenuItem, OutlinedInput, Select } from "@mui/material"; +function CustomSelectByDependency({ item, filterParameters, value, handleChange, selectOption }) { + return ( + + + {item.header} + + + + ); +} + +export default CustomSelectByDependency; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomSelectMultiple.jsx b/src/core/components/FilterDrawer/fieldsType/CustomSelectMultiple.jsx new file mode 100644 index 0000000..c1fd7fa --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomSelectMultiple.jsx @@ -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 ( + + + {item.header} + + + + ); +} + +export default CustomSelectMultiple; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomTextField.jsx b/src/core/components/FilterDrawer/fieldsType/CustomTextField.jsx new file mode 100644 index 0000000..86a9c57 --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomTextField.jsx @@ -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 ( + handleChange({ ...filterParameters, value: e.target.value })} + onBlur={handleBlur} + fullWidth + variant="outlined" + size="small" + sx={{ my: 1 }} + InputProps={{ + endAdornment: ( + + + + ), + }} + InputLabelProps={{ shrink: true }} + /> + ); +} + +export default CustomTextField; diff --git a/src/core/components/FilterDrawer/fieldsType/CustomTextFieldRange.jsx b/src/core/components/FilterDrawer/fieldsType/CustomTextFieldRange.jsx new file mode 100644 index 0000000..147ec85 --- /dev/null +++ b/src/core/components/FilterDrawer/fieldsType/CustomTextFieldRange.jsx @@ -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 ( + + + handleChange({ ...filterParameters, value: [e.target.value, filterParameters.value[1]] }) + } + onBlur={handleBlur} + label={از {item.header}} + value={filterParameters.value[0]} + fullWidth + error={touched?.[`${item.id}`]?.value && Boolean(errors?.[`${item.id}`]?.value)} + helperText={ + + نوع فیلتر: {defaultFilterTranslation} + + } + variant="outlined" + size="small" + sx={{ my: 1, marginRight: 1 }} + /> + + 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={تا {item.header}} + value={filterParameters.value[1]} + fullWidth + variant="outlined" + size="small" + sx={{ my: 1 }} + InputProps={{ + endAdornment: ( + + + + ), + }} + /> + + ); +} + +export default CustomTextFieldRange; diff --git a/src/core/components/FilterDrawer/index.jsx b/src/core/components/FilterDrawer/index.jsx new file mode 100644 index 0000000..3b3699b --- /dev/null +++ b/src/core/components/FilterDrawer/index.jsx @@ -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 ( + <> + + + + + فیلتر + + + + + + + +
+ + {Object.values(defaultValues).map((item) => + item.dependencyId ? ( + + ) : ( + + ) + )} + + + + + +
+
+ + ); +}; +export default FilterDrawer;