diff --git a/.env.local.example b/.env.local.example index 37b65ca..c9c0de0 100644 --- a/.env.local.example +++ b/.env.local.example @@ -1,4 +1,4 @@ HOST="rms.witel.ir" -NEXT_PUBLIC_VERSION="0.18.0" +NEXT_PUBLIC_VERSION="1.0.0" NEXT_PUBLIC_API_URL="https://rms.witel.ir" NEXT_PUBLIC_MAPTILE_ENDPOINT="https://rmsmap.rmto.ir/141map" \ No newline at end of file diff --git a/src/app/(withAuth)/dashboard/road-safety/reports/map/page.js b/src/app/(withAuth)/dashboard/road-safety/reports/map/page.js new file mode 100644 index 0000000..b272e5e --- /dev/null +++ b/src/app/(withAuth)/dashboard/road-safety/reports/map/page.js @@ -0,0 +1,9 @@ +import RoadSafetyReportMap from "@/components/dashboard/roadSafety/reports/map"; +export const metadata = { + title: "پراکندگی بر روی نقشه نگهداری حریم", +}; + +const Page = () => { + return ; +}; +export default Page; diff --git a/src/app/(withAuth)/dashboard/road-safety/report/page.js b/src/app/(withAuth)/dashboard/road-safety/reports/page.js similarity index 97% rename from src/app/(withAuth)/dashboard/road-safety/report/page.js rename to src/app/(withAuth)/dashboard/road-safety/reports/page.js index 26681da..54df8d0 100644 --- a/src/app/(withAuth)/dashboard/road-safety/report/page.js +++ b/src/app/(withAuth)/dashboard/road-safety/reports/page.js @@ -1,4 +1,4 @@ -import ReportPage from "@/components/dashboard/roadSafety/report"; +import ReportPage from "@/components/dashboard/roadSafety/reports/reportPage"; import WithPermission from "@/core/middlewares/withPermission"; export const metadata = { title: "کارتابل گزارش نگهداری حریم", diff --git a/src/app/(withAuth)/infrastructure/layout.js b/src/app/(withAuth)/infrastructure/layout.js new file mode 100644 index 0000000..219cee2 --- /dev/null +++ b/src/app/(withAuth)/infrastructure/layout.js @@ -0,0 +1,13 @@ +import HeaderWithLogo from "@/components/layouts/dashboard/headerWithLogo"; +import HeaderWithSidebar from "@/components/layouts/dashboard/headerWithSidebar"; +import { Stack } from "@mui/material"; + +const Layout = ({ children }) => { + return ( + + + {children} + + ); +}; +export default Layout; diff --git a/src/app/(withAuth)/infrastructure/toll-house/loading.jsx b/src/app/(withAuth)/infrastructure/toll-house/loading.jsx new file mode 100644 index 0000000..4535a8d --- /dev/null +++ b/src/app/(withAuth)/infrastructure/toll-house/loading.jsx @@ -0,0 +1,7 @@ +"use client"; +import PageLoading from "@/core/components/PageLoading"; + +const Loading = () => { + return ; +}; +export default Loading; diff --git a/src/app/(withAuth)/infrastructure/toll-house/page.js b/src/app/(withAuth)/infrastructure/toll-house/page.js new file mode 100644 index 0000000..dec7c50 --- /dev/null +++ b/src/app/(withAuth)/infrastructure/toll-house/page.js @@ -0,0 +1,16 @@ +import WithPermission from "@/core/middlewares/withPermission"; +import TollHousePage from "@/components/infrastructure/tollHouse"; + +export const metadata = { + title: "فهرست راهدارخانه ها", +}; + +const Page = () => { + return ( + + + + ); +}; + +export default Page; diff --git a/src/components/dashboard/azmayesh/Forms/CandUAzmayesh/FormAndRequest.jsx b/src/components/dashboard/azmayesh/Forms/CandUAzmayesh/FormAndRequest.jsx index f397229..14d5b3d 100644 --- a/src/components/dashboard/azmayesh/Forms/CandUAzmayesh/FormAndRequest.jsx +++ b/src/components/dashboard/azmayesh/Forms/CandUAzmayesh/FormAndRequest.jsx @@ -91,7 +91,7 @@ const CandUAzmayesh = ({ setOpen, mutate, updateInfo, rowId }) => { handleSubmit, setValue, formState: { isSubmitting, errors }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/azmayeshType/Forms/CandUAzmayeshType/FormAndRequest.jsx b/src/components/dashboard/azmayeshType/Forms/CandUAzmayeshType/FormAndRequest.jsx index a794154..467fc91 100644 --- a/src/components/dashboard/azmayeshType/Forms/CandUAzmayeshType/FormAndRequest.jsx +++ b/src/components/dashboard/azmayeshType/Forms/CandUAzmayeshType/FormAndRequest.jsx @@ -68,7 +68,7 @@ const FormAndRequest = ({ setOpen, mutate, rowId, updateInfo }) => { setValue, watch, formState: { errors, isSubmitting }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const { fields, append, remove } = useFieldArray({ control, diff --git a/src/components/dashboard/damageItems/Actions/Create/Form/CreateFormContent.jsx b/src/components/dashboard/damageItems/Actions/Create/Form/CreateFormContent.jsx index e3745f4..53fd6a3 100644 --- a/src/components/dashboard/damageItems/Actions/Create/Form/CreateFormContent.jsx +++ b/src/components/dashboard/damageItems/Actions/Create/Form/CreateFormContent.jsx @@ -30,7 +30,7 @@ const CreateFormContent = ({ setOpen, mutate }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = async (data) => { diff --git a/src/components/dashboard/damageItems/RowActions/Edit/Form/index.jsx b/src/components/dashboard/damageItems/RowActions/Edit/Form/index.jsx index 30965fe..e6c2efb 100644 --- a/src/components/dashboard/damageItems/RowActions/Edit/Form/index.jsx +++ b/src/components/dashboard/damageItems/RowActions/Edit/Form/index.jsx @@ -30,7 +30,7 @@ const EditFormContent = ({ row, rowId, mutate, setOpen }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = async (data) => { diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/CityContent.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/CityContent.jsx index 08da4e5..5092ad9 100644 --- a/src/components/dashboard/damages/operator/Actions/create/Forms/CityContent.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/CityContent.jsx @@ -1,9 +1,9 @@ import SelectBox from "@/core/components/SelectBox"; -import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import useCities from "@/lib/hooks/useCities"; import { useEffect, useState } from "react"; const CityContent = ({ provinceID, field, fieldState: { error } }) => { - const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(provinceID); + const { cityList, loadingCityList, errorCityList } = useCities(provinceID); const [prevDependency, setPrevDependency] = useState(provinceID); useEffect(() => { @@ -15,22 +15,16 @@ const CityContent = ({ provinceID, field, fieldState: { error } }) => { return ( diff --git a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx index e52c280..5891455 100644 --- a/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx +++ b/src/components/dashboard/damages/operator/Actions/create/Forms/DamageItem.jsx @@ -49,7 +49,7 @@ const DamageItem = ({ baseOnChange, baseDamageItems }) => { const { control, handleSubmit, reset, setValue } = useForm({ defaultValues, resolver: yupResolver(schema), - mode: "onBlur", + mode: "all", }); const deleteDamageItem = (id) => { setSelectedDamageItemList((prev) => prev.filter((DamageItem) => DamageItem.id !== id)); diff --git a/src/components/dashboard/damages/operator/OperatorList.jsx b/src/components/dashboard/damages/operator/OperatorList.jsx index ec6b61e..2c07053 100644 --- a/src/components/dashboard/damages/operator/OperatorList.jsx +++ b/src/components/dashboard/damages/operator/OperatorList.jsx @@ -1,19 +1,19 @@ "use client"; -import { useEffect, useMemo, useState } from "react"; -import { Box, Stack, Typography } from "@mui/material"; -import DataTableWithAuth from "@/core/components/DataTableWithAuth"; -import Toolbar from "./Toolbar"; -import { GET_TECHNICAL_DAMAGE_OPERATOR_LIST } from "@/core/utils/routes"; -import moment from "jalali-moment"; -import RowActions from "./RowActions"; -import useProvinces from "@/lib/hooks/useProvince"; import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; -import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import { GET_TECHNICAL_DAMAGE_OPERATOR_LIST } from "@/core/utils/routes"; +import useCities from "@/lib/hooks/useCities"; +import useProvinces from "@/lib/hooks/useProvince"; +import { Box, Stack, Typography } from "@mui/material"; +import moment from "jalali-moment"; +import { useEffect, useMemo, useState } from "react"; +import RowActions from "./RowActions"; +import DamageItemDialog from "./RowActions/DamageItemDialog"; +import DamageReportDialog from "./RowActions/DamageReport"; import ImageDialog from "./RowActions/ImageDialog"; import LocationForm from "./RowActions/LocationForm"; import ShowPlate from "./RowActions/ShowPlate"; -import DamageItemDialog from "./RowActions/DamageItemDialog"; -import DamageReportDialog from "./RowActions/DamageReport"; +import Toolbar from "./Toolbar"; const OperatorList = () => { const statusOptions = [ @@ -94,7 +94,7 @@ const OperatorList = () => { grow: false, size: 120, ColumnSelectComponent: (props) => { - const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( + const { cityList, loadingCityList, errorCityList } = useCities( props.dependencyFieldValue.value ); const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value); @@ -103,20 +103,20 @@ const OperatorList = () => { if (props.dependencyFieldValue.value === "") { return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; } - if (loadingEdaratList) { + if (loadingCityList) { return [{ value: "loading", label: "در حال بارگذاری..." }]; } - if (errorEdaratList) { + if (errorCityList) { return [{ value: "error", label: "خطا در بارگذاری" }]; } return [ { value: "", label: "کل شهر ها" }, - ...edaratList.map((edare) => ({ - value: edare.id, - label: edare.name_fa, + ...cityList.map((city) => ({ + value: city.id, + label: city.name_fa, })), ]; - }, [edaratList, loadingEdaratList, errorEdaratList]); + }, [cityList, loadingCityList, errorCityList]); useEffect(() => { if (prevDependency === props.dependencyFieldValue?.value) return; props.handleChange({ ...props.filterParameters, value: "" }); @@ -128,7 +128,7 @@ const OperatorList = () => { value={ props.dependencyFieldValue?.value === "" ? "empty" - : loadingEdaratList + : loadingCityList ? "loading" : props.filterParameters.value } diff --git a/src/components/dashboard/damages/report/index.jsx b/src/components/dashboard/damages/report/index.jsx index e2fa70e..8505f2f 100644 --- a/src/components/dashboard/damages/report/index.jsx +++ b/src/components/dashboard/damages/report/index.jsx @@ -82,10 +82,10 @@ const DamagesReportPage = () => { setData({ data: [ nationalReport, - ...result.cities.map((edare) => { - const filteredReports = result.activities.find((report) => report.city_id == edare.id); + ...result.cities.map((city) => { + const filteredReports = result.activities.find((report) => report.city_id == city.id); return { - edare_name: edare.name_fa, + edare_name: city.name_fa, ...filteredReports, }; }), @@ -118,7 +118,7 @@ const DamagesReportPage = () => { const { control, handleSubmit } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); return ( diff --git a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx index 4ca8402..ac16216 100644 --- a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx +++ b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx @@ -237,10 +237,7 @@ const ComplaintListTable = ({ open, setOpen, mutate }) => { size: 100, }, { - accessorFn: (row) => - moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A") - .locale("fa") - .format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => moment(row.StartTime_DateTime).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", id: "StartTime_DateTime", enableColumnFilter: false, diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx index 34d4130..c4830cb 100644 --- a/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/RegisterActionContent.jsx @@ -42,7 +42,7 @@ const RegisterActionContent = ({ setOpen, defaultValues, onBaseSubmit }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onChange", + mode: "all", }); const onSubmit = async (data) => { diff --git a/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx b/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx index 168acb3..850bc7d 100644 --- a/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx +++ b/src/components/dashboard/fastReact/complaintList/Form/registerAction/index.jsx @@ -29,6 +29,7 @@ const RegisterAction = ({ rowId, mutate, mutateParentTable }) => { } await requestServer(`${REGISTER_COMPLAINTS_LIST}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then((res) => { mutate(); diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx index 59774e6..705fabf 100644 --- a/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx +++ b/src/components/dashboard/fastReact/complaintList/RowActions/Refer/ReferContent.jsx @@ -28,7 +28,7 @@ const ReferContent = ({ rowId, mutate, setOpenReferDialog }) => { description: "", }, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = async (data) => { @@ -38,6 +38,7 @@ const ReferContent = ({ rowId, mutate, setOpenReferDialog }) => { formData.append("refer_description", data.description); await requestServer(`${REFER_FAST_REACT}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then(() => { mutate(); diff --git a/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx b/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx index 6a46985..f20797f 100644 --- a/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx +++ b/src/components/dashboard/fastReact/operator/Form/EditForm/EditController.jsx @@ -47,6 +47,7 @@ const EditController = ({ rowId, mutate, setOpenEditDialog }) => { await requestServer(`${UPDATE_FAST_REACT}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then(() => { mutate(); diff --git a/src/components/dashboard/fastReact/operator/OperatorList.jsx b/src/components/dashboard/fastReact/operator/OperatorList.jsx index 925d89e..9de64f1 100644 --- a/src/components/dashboard/fastReact/operator/OperatorList.jsx +++ b/src/components/dashboard/fastReact/operator/OperatorList.jsx @@ -149,10 +149,7 @@ const OperatorList = () => { size: 100, }, { - accessorFn: (row) => - moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A") - .locale("fa") - .format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => moment(row.StartTime_DateTime).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", id: "StartTime_DateTime", enableColumnFilter: false, diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx index 5944db3..2a27c3e 100644 --- a/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx +++ b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx @@ -22,6 +22,7 @@ const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => { formData.append("verify", 1); requestServer(`${VERIFY_BY_FAST_REACT_SUPERVISOR}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then(() => { mutate(); diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx index 5c51886..b5feca5 100644 --- a/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx +++ b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx @@ -22,7 +22,7 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => { description: "", }, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = async (data) => { @@ -31,6 +31,7 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => { formData.append("verify", 2); requestServer(`${REJECT_BY_FAST_REACT_SUPERVISOR}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then(() => { mutate(); diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx index 06c86a6..b62566f 100644 --- a/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx +++ b/src/components/dashboard/fastReact/supervisor/RowActions/RestoreForm/RestoreContent.jsx @@ -8,7 +8,7 @@ const RestoreContent = ({ rowId, mutate, setOpenRestoreDialog }) => { const requestServer = useRequest({ notificationSuccess: true }); const handleClick = () => { setSubmitting(true); - requestServer(`${RESTORE_FAST_REACT}/${rowId}`, "post") + requestServer(`${RESTORE_FAST_REACT}/${rowId}`, "post", { hasSidebarUpdate: true }) .then(() => { mutate(); setOpenRestoreDialog(false); diff --git a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx index b10eade..d543a18 100644 --- a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx @@ -250,10 +250,7 @@ const SupervisorList = () => { size: 100, }, { - accessorFn: (row) => - moment(row.StartTime_DateTime, "M/D/YYYY h:mm:ss A") - .locale("fa") - .format("HH:mm | yyyy/MM/DD"), + accessorFn: (row) => moment(row.StartTime_DateTime).locale("fa").format("HH:mm | yyyy/MM/DD"), header: "تاریخ ثبت", id: "StartTime_DateTime", enableColumnFilter: false, diff --git a/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx b/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx index d9cd72d..4f3f740 100644 --- a/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx +++ b/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx @@ -46,7 +46,7 @@ const RoadSafetyFormContext = ({ rowId, mutate, setOpenRoadSafetyForm }) => { register, handleSubmit, formState: { isSubmitting, errors }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); formData.append("description", data.description); diff --git a/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/TaskDetail/DetailDialog.jsx b/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/TaskDetail/DetailDialog.jsx index aff1058..7b50738 100644 --- a/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/TaskDetail/DetailDialog.jsx +++ b/src/components/dashboard/inquiryPrivacy/assistant/zaminGov/RowActions/TaskDetail/DetailDialog.jsx @@ -48,7 +48,7 @@ const DetailDialog = ({ taskModal, setTaskModal, rowId, mutate }) => { register, handleSubmit, formState: { isSubmitting, errors, touchedFields }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx b/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx index fbc6c47..ea2440e 100644 --- a/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx +++ b/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx @@ -37,7 +37,7 @@ const ReferFormContext = ({ setOpenReferDialog, rowId, mutate }) => { register, handleSubmit, formState: { isSubmitting, errors, touchedFields }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx b/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx index a91fd31..bd2f835 100644 --- a/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx +++ b/src/components/dashboard/inquiryPrivacy/cityAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx @@ -57,7 +57,7 @@ const DetailDialog = ({ taskModal, setTaskModal, rowId, mutate }) => { register, handleSubmit, formState: { isSubmitting, errors, touchedFields }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx b/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx index c480382..f952d45 100644 --- a/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx +++ b/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/RoadSafetyForm/RoadSafetyFormContext.jsx @@ -46,7 +46,7 @@ const RoadSafetyFormContext = ({ rowId, mutate, setOpenRoadSafetyForm }) => { register, handleSubmit, formState: { isSubmitting, errors }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); formData.append("description", data.description); diff --git a/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/TaskDetail/DetailDialog.jsx b/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/TaskDetail/DetailDialog.jsx index aff1058..7b50738 100644 --- a/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/TaskDetail/DetailDialog.jsx +++ b/src/components/dashboard/inquiryPrivacy/generalManager/zaminGov/RowActions/TaskDetail/DetailDialog.jsx @@ -48,7 +48,7 @@ const DetailDialog = ({ taskModal, setTaskModal, rowId, mutate }) => { register, handleSubmit, formState: { isSubmitting, errors, touchedFields }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx index 1284aa2..5a6b1e8 100644 --- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx +++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/Refer/ReferFormContext.jsx @@ -37,7 +37,7 @@ const ReferFormContext = ({ setOpenReferDialog, rowId, mutate }) => { register, handleSubmit, formState: { isSubmitting, errors, touchedFields }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx index 05cb6e0..353fe19 100644 --- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx +++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/RoadSafetyForm/RoadSaftyFormContext.jsx @@ -46,7 +46,7 @@ const RoadSafetyFormContext = ({ rowId, mutate, setOpenRoadSafetyForm }) => { register, handleSubmit, formState: { isSubmitting, errors }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); formData.append("description", data.description); diff --git a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx index aff1058..7b50738 100644 --- a/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx +++ b/src/components/dashboard/inquiryPrivacy/provinceAdmin/zaminGov/RowActions/TaskDetail/DetailDialog.jsx @@ -48,7 +48,7 @@ const DetailDialog = ({ taskModal, setTaskModal, rowId, mutate }) => { register, handleSubmit, formState: { isSubmitting, errors, touchedFields }, - } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(validationSchema), mode: "all" }); const onSubmit = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/permissionTable/Actions/Create/Form/CreateFormContent.jsx b/src/components/dashboard/permissionTable/Actions/Create/Form/CreateFormContent.jsx index 6cb55e8..dd9fede 100644 --- a/src/components/dashboard/permissionTable/Actions/Create/Form/CreateFormContent.jsx +++ b/src/components/dashboard/permissionTable/Actions/Create/Form/CreateFormContent.jsx @@ -30,7 +30,7 @@ const CreateFormContent = ({ setOpen, defaultValues, onSubmitBase }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const handleOnSubmit = async (data) => { await onSubmitBase(data); diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx index 62bc6a8..2e7a775 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx @@ -164,7 +164,7 @@ const CreateFormContent = ({ setOpen, onSubmit, is_gasht = false }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", context: { subItemsList, is_gasht }, }); const onSubmitBase = async (data) => { diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx index edcc098..40d0c2a 100644 --- a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx @@ -14,6 +14,7 @@ const OperatorCreateForm = ({ open, setOpen, mutate }) => { } await requestServer(CREATE_ROAD_ITEMS, "post", { data: formData, + hasSidebarUpdate: true, }) .then((response) => { mutate(); diff --git a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreateContent.jsx b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreateContent.jsx index 80408d3..5816ddf 100644 --- a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreateContent.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/GashtCreateContent.jsx @@ -79,7 +79,7 @@ const GashtCreateContent = ({ mutate, setOpen, tabState, setTabState }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSearchSubmit = async (data) => { setSpecialFilter([ diff --git a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/RowActions/CompleteItem.jsx b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/RowActions/CompleteItem.jsx index 3ef226d..f0e048d 100644 --- a/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/RowActions/CompleteItem.jsx +++ b/src/components/dashboard/roadItems/operator/Actions/ObservedGashtCreate/RowActions/CompleteItem.jsx @@ -89,7 +89,7 @@ const CompleteItem = ({ subItem, itemInfo, setSubmitCompleteForm, mutate, setOpe } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", context: { subItem }, }); @@ -117,6 +117,7 @@ const CompleteItem = ({ subItem, itemInfo, setSubmitCompleteForm, mutate, setOpe await requestServer(CREATE_ROAD_ITEMS, "post", { data: formData, + hasSidebarUpdate: true, }) .then((response) => { mutate(); diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx index 4c4fcc9..ef28c64 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditController.jsx @@ -69,14 +69,11 @@ const EditController = ({ rowId, mutate, setOpenEditDialog, openEditDialog, row for (const [key, value] of Object.entries(result)) { formData.append(key, value); } - await requestServer( - `${UPDATE_ROAD_ITEMS}/${rowId}`, - "post", - { - data: formData, - }, - { notificationSuccess: true } - ) + await requestServer(`${UPDATE_ROAD_ITEMS}/${rowId}`, "post", { + data: formData, + notificationSuccess: true, + hasSidebarUpdate: true, + }) .then((response) => { mutate(); setOpenEditDialog(false); diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx index fa038ca..4a3e6f6 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormCreate.jsx @@ -78,7 +78,7 @@ const EditFormCreate = ({ defaultData, subItem, onSubmitBase, defaultValues, set } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", context: { subItem, is_gasht }, }); diff --git a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx index dfe6585..3b79841 100644 --- a/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx +++ b/src/components/dashboard/roadItems/reports/ItemsReports/index.jsx @@ -153,7 +153,7 @@ const ReportPage = () => { const { control, handleSubmit } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); return ( diff --git a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx index 9700220..074977e 100644 --- a/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx +++ b/src/components/dashboard/roadItems/reports/Map/Filter/Drawer/index.jsx @@ -59,7 +59,7 @@ const FilterDrawer = ({ defaultValues, setFilterData, closeDrawer }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = (data) => { diff --git a/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx b/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx index 48a9e2e..87f9bd5 100644 --- a/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx +++ b/src/components/dashboard/roadItems/reports/SubItemsReports/index.jsx @@ -238,7 +238,7 @@ const SubItemsReports = () => { const { control, handleSubmit } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); return ( diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx index 648ab37..4cba1f8 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx @@ -23,6 +23,7 @@ const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => { formData.append("verify", 1); requestServer(`${VERIFY_BY_SUPERVISOR}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then(() => { mutate(); diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx index 2d03dc7..4bfb3ea 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx @@ -8,7 +8,9 @@ const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => { const requestServer = useRequest({ notificationSuccess: true }); const handleClick = () => { setSubmitting(true); - requestServer(`${DELETE_BY_SUPERVISOR}/${rowId}`, "post") + requestServer(`${DELETE_BY_SUPERVISOR}/${rowId}`, "post", { + hasSidebarUpdate: true, + }) .then(() => { mutate(); setOpenDeleteDialog(false); diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx index 3d319fa..8702ea4 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx @@ -22,7 +22,7 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => { description: "", }, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = async (data) => { @@ -31,6 +31,7 @@ const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => { formData.append("verify", 2); await requestServer(`${REJECT_BY_SUPERVISOR}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then(() => { mutate(); diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx index 3155406..61192d7 100644 --- a/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx @@ -8,7 +8,9 @@ const RestoreContent = ({ rowId, mutate, setOpenRestoreDialog }) => { const requestServer = useRequest({ notificationSuccess: true }); const handleClick = () => { setSubmitting(true); - requestServer(`${RESTORE_BY_SUPERVISOR}/${rowId}`, "post") + requestServer(`${RESTORE_BY_SUPERVISOR}/${rowId}`, "post", { + hasSidebarUpdate: true, + }) .then(() => { mutate(); setOpenRestoreDialog(false); diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx index ca4f198..5402d42 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CompeleteRequest.jsx @@ -63,14 +63,14 @@ const CompeleteRequest = ({ allData, handlePrev, mutate, setOpen }) => { handleSubmit: handleSubmitOtp, register: registerOtp, formState: { isSubmitting: isSubmittingOtp, isValid: isValidOtp }, - } = useForm({ defaultValues: defaultValuesOtp, resolver: yupResolver(schemaOtp), mode: "onBlur" }); + } = useForm({ defaultValues: defaultValuesOtp, resolver: yupResolver(schemaOtp), mode: "all" }); const { handleSubmit: handleSubmitSend, setValue: setValueSend, register: registerSend, formState: { isSubmitting: isSubmittingSend, isValid: isValidSend }, - } = useForm({ defaultValues: defaultValuesSend, resolver: yupResolver(schemaSend), mode: "onBlur" }); + } = useForm({ defaultValues: defaultValuesSend, resolver: yupResolver(schemaSend), mode: "all" }); useEffect(() => { if (counter === 0) { diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx index 63b0ec9..216cdfe 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/CreateFormContentObservedItem.jsx @@ -91,7 +91,7 @@ const CreateFormContentObservedItem = ({ setOpen, onSubmit }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", context: { subItemsList }, }); const onSubmitBase = async (data) => { diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetail.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetail.jsx index 75515aa..6202bb6 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetail.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/PatrolDetail.jsx @@ -46,7 +46,7 @@ const PatrolDetail = ({ allData, setAllData, handlePrev, setTabState }) => { handleSubmit, setValue, formState: { isSubmitting, isValid }, - } = useForm({ defaultValues, resolver: yupResolver(schema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(schema), mode: "all" }); const requestPatrolInfo = async (data) => { setPatrolResultStatus(1); diff --git a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/SuperVisorsDetail.jsx b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/SuperVisorsDetail.jsx index 42cd036..34adc2e 100644 --- a/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/SuperVisorsDetail.jsx +++ b/src/components/dashboard/roadPatrols/operator/Forms/CreatePatrol/SuperVisorsDetail.jsx @@ -27,7 +27,7 @@ const SuperVisorsDetail = ({ allData, setAllData, handlePrev, setTabState }) => reset, setValue, formState: { isSubmitting, isValid }, - } = useForm({ defaultValues, resolver: yupResolver(schema), mode: "onBlur" }); + } = useForm({ defaultValues, resolver: yupResolver(schema), mode: "all" }); const requestPatrolInfo = (data) => { setSuperVisorList((prev) => [...prev, data.road_patrol_rahdaran_id]); diff --git a/src/components/dashboard/roadPatrols/reports/index.jsx b/src/components/dashboard/roadPatrols/reports/index.jsx index 262940d..d3b7679 100644 --- a/src/components/dashboard/roadPatrols/reports/index.jsx +++ b/src/components/dashboard/roadPatrols/reports/index.jsx @@ -116,7 +116,7 @@ const ReportPage = () => { const { control, handleSubmit } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); return ( diff --git a/src/components/dashboard/roadSafety/operator/Form/EditAxisType/EditFormContent.jsx b/src/components/dashboard/roadSafety/operator/Form/EditAxisType/EditFormContent.jsx index a76342b..b42d741 100644 --- a/src/components/dashboard/roadSafety/operator/Form/EditAxisType/EditFormContent.jsx +++ b/src/components/dashboard/roadSafety/operator/Form/EditAxisType/EditFormContent.jsx @@ -30,7 +30,7 @@ const EditFormContent = ({ setOpen, mutate, row }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmitBase = async (data) => { const formData = new FormData(); diff --git a/src/components/dashboard/roadSafety/operator/Form/StepOne/CreateFormContent.jsx b/src/components/dashboard/roadSafety/operator/Form/StepOne/CreateFormContent.jsx index 6c49683..bcc6012 100644 --- a/src/components/dashboard/roadSafety/operator/Form/StepOne/CreateFormContent.jsx +++ b/src/components/dashboard/roadSafety/operator/Form/StepOne/CreateFormContent.jsx @@ -77,7 +77,7 @@ const CreateFormContent = ({ setOpen, mutate }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmitBase = async (data) => { const startPoint = `${data.start_point.lat},${data.start_point.lng}`; @@ -90,6 +90,7 @@ const CreateFormContent = ({ setOpen, mutate }) => { formData.append("point", startPoint); await requestServer(FIRST_STEP_STORE, "post", { data: formData, + hasSidebarUpdate: true, }) .then((response) => { mutate(); diff --git a/src/components/dashboard/roadSafety/operator/Form/StepThree/StepThreeContent.jsx b/src/components/dashboard/roadSafety/operator/Form/StepThree/StepThreeContent.jsx index e3916d1..0e9770c 100644 --- a/src/components/dashboard/roadSafety/operator/Form/StepThree/StepThreeContent.jsx +++ b/src/components/dashboard/roadSafety/operator/Form/StepThree/StepThreeContent.jsx @@ -2,33 +2,42 @@ import { Button, DialogActions, DialogContent, Stack } from "@mui/material"; import { Controller, useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import StyledForm from "@/core/components/StyledForm"; -import { mixed, object } from "yup"; +import { mixed, object, string } from "yup"; import ImageUpload from "./ImageUpload"; import useRequest from "@/lib/hooks/useRequest"; import { THIRD_STEP_STORE } from "@/core/utils/routes"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import MuiTimePicker from "@/core/components/MuiTimePicker"; +import { format } from "date-fns"; const StepThreeContent = ({ setOpen, mutate, rowId }) => { const requestServer = useRequest({ notificationSuccess: true }); const defaultValues = { + action_date: "", + action_time: "", action_picture: null, }; const validationSchema = object({ + action_date: string().required("لطفا تاریخ اقدام را انتخاب کنید!"), + action_time: string().required("لطفا ساعت اقدام را انتخاب کنید!"), action_picture: mixed().nullable().required("لطفا عکس اقدام را بارگذاری کنید!"), }); const { control, handleSubmit, - formState: { isSubmitting, errors }, + formState: { isSubmitting }, } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = async (data) => { const formData = new FormData(); + formData.append("action_date", `${data.action_date} ${format(new Date(data.action_time), "HH:mm")}`); formData.append("action_picture", data.action_picture); await requestServer(`${THIRD_STEP_STORE}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then((response) => { mutate(); @@ -40,6 +49,42 @@ const StepThreeContent = ({ setOpen, mutate, rowId }) => { + + { + return ( + field.onChange(value || [])} + helperText={error ? error.message : null} + /> + ); + }} + name={"action_date"} + /> + { + return ( + field.onChange(value || null)} + helperText={error ? error.message : null} + /> + ); + }} + name={"action_time"} + /> + { بستن diff --git a/src/components/dashboard/roadSafety/operator/Form/StepThree/index.jsx b/src/components/dashboard/roadSafety/operator/Form/StepThree/index.jsx index 8602d6e..025222d 100644 --- a/src/components/dashboard/roadSafety/operator/Form/StepThree/index.jsx +++ b/src/components/dashboard/roadSafety/operator/Form/StepThree/index.jsx @@ -6,7 +6,7 @@ const StepThree = ({ mutate, rowId }) => { const [open, setOpen] = useState(false); return ( <> - + setOpen(true)}> @@ -20,10 +20,10 @@ const StepThree = ({ mutate, rowId }) => { }, }} dir="rtl" - maxWidth={"sm"} + maxWidth={"xs"} fullWidth > - بارگذاری تصویر اقدام + ثبت اقدام diff --git a/src/components/dashboard/roadSafety/operator/Form/StepTwo/StepTwoContent.jsx b/src/components/dashboard/roadSafety/operator/Form/StepTwo/StepTwoContent.jsx index 1b9099d..c9ab104 100644 --- a/src/components/dashboard/roadSafety/operator/Form/StepTwo/StepTwoContent.jsx +++ b/src/components/dashboard/roadSafety/operator/Form/StepTwo/StepTwoContent.jsx @@ -27,7 +27,7 @@ const StepTwoContent = ({ setOpenStepTwoDialog, mutate, rowId }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const handleCreateDamage = (files) => { @@ -48,6 +48,7 @@ const StepTwoContent = ({ setOpenStepTwoDialog, mutate, rowId }) => { data.judiciary_document.map((item, index) => formData.append(`judiciary_document[${index}]`, item)); await requestServer(`${SECOND_STEP_STORE}/${rowId}`, "post", { data: formData, + hasSidebarUpdate: true, }) .then((response) => { mutate(); diff --git a/src/components/dashboard/roadSafety/operator/OperatorList.jsx b/src/components/dashboard/roadSafety/operator/OperatorList.jsx index c59e81b..13a530c 100644 --- a/src/components/dashboard/roadSafety/operator/OperatorList.jsx +++ b/src/components/dashboard/roadSafety/operator/OperatorList.jsx @@ -19,7 +19,8 @@ import useSafetyAndPrivacyGetItems from "@/lib/hooks/useSafetyAndPrivacyGetItems const OperatorList = () => { const { data: userPermissions } = usePermissions(); - const hasCountryPermission = userPermissions?.includes("show-fast-react"); + const hasCountryPermission = userPermissions?.includes("show-safety-and-privacy-operator-cartable"); + const hasProvincePermission = userPermissions?.includes("show-safety-and-privacy-operator-cartable-province"); const { user } = useAuth(); const statusOptions = [ { value: "", label: "همه وضعیت ها" }, @@ -37,7 +38,7 @@ const OperatorList = () => { ]; const columns = useMemo(() => { - const dynamicColumns = { + const ProvinceColumn = { header: "استان", id: "province_id", enableColumnFilter: true, @@ -72,6 +73,63 @@ const OperatorList = () => { }, Cell: ({ row }) => <>{row.original.province_fa}, }; + const EdareShahriColumn = { + header: "اداره", + id: "edare_shahri_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + dependencyId: hasCountryPermission ? "province_id" : null, + grow: false, + size: 120, + ColumnSelectComponent: (props) => { + const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id + ); + const [prevDependency, setPrevDependency] = useState( + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id + ); + + const getColumnSelectOptions = useMemo(() => { + if (hasCountryPermission && 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 (hasCountryPermission) return; + if (prevDependency === props.dependencyFieldValue?.value) return; + props.handleChange({ ...props.filterParameters, value: "" }); + setPrevDependency(props.dependencyFieldValue?.value); + }, [props.dependencyFieldValue?.value, hasCountryPermission]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.edare_shahri_name}, + }; return [ { accessorKey: "id", @@ -85,64 +143,9 @@ const OperatorList = () => { grow: false, size: 100, }, - ...(hasCountryPermission ? [dynamicColumns] : []), - { - header: "اداره", - id: "edare_shahri_id", - enableColumnFilter: true, - datatype: "numeric", - filterMode: "equals", - dependencyId: hasCountryPermission ? "province_id" : null, - grow: false, - size: 120, - ColumnSelectComponent: (props) => { - const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( - hasCountryPermission ? props.dependencyFieldValue.value : user.province_id - ); - const [prevDependency, setPrevDependency] = useState( - hasCountryPermission ? props.dependencyFieldValue.value : user.province_id - ); + ...(hasCountryPermission ? [ProvinceColumn] : []), + ...(hasProvincePermission ? [EdareShahriColumn] : []), - const getColumnSelectOptions = useMemo(() => { - if (hasCountryPermission && 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 (hasCountryPermission) return; - if (prevDependency === props.dependencyFieldValue?.value) return; - props.handleChange({ ...props.filterParameters, value: "" }); - setPrevDependency(props.dependencyFieldValue?.value); - }, [props.dependencyFieldValue?.value, hasCountryPermission]); - return ( - - ); - }, - Cell: ({ row }) => <>{row.original.edare_shahri_name}, - }, { header: "اطلاعات فعالیت", id: "info", @@ -246,6 +249,23 @@ const OperatorList = () => { }, ], }, + { + header: "وضعیت", + id: "step", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + sortDescFirst: true, + grow: false, + size: 100, + columnSelectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + Cell: ({ row }) => <>{row.original.step_fa}, + }, { header: "گام اول (شناسایی)", id: "stepOne", @@ -396,15 +416,13 @@ const OperatorList = () => { }, { accessorFn: (row) => - row.action_picture_document_upload_date ? ( - moment(row.action_picture_document_upload_date) - .locale("fa") - .format("HH:mm | yyyy/MM/DD") + row.action_date ? ( + moment(row.action_date).locale("fa").format("HH:mm | yyyy/MM/DD") ) : ( <>- ), - header: "تاریخ ثبت اقدام", - id: "action_picture_document_upload_date", + header: "تاریخ اقدام", + id: "action_date", enableColumnFilter: true, datatype: "date", filterMode: "between", @@ -413,23 +431,6 @@ const OperatorList = () => { }, ], }, - { - header: "وضعیت", - id: "step", - enableColumnFilter: true, - datatype: "numeric", - filterMode: "equals", - sortDescFirst: true, - grow: false, - size: 100, - columnSelectOption: () => { - return statusOptions.map((status) => ({ - value: status.value, - label: status.label, - })); - }, - Cell: ({ row }) => <>{row.original.step_fa}, - }, ]; }, []); diff --git a/src/components/dashboard/roadSafety/operator/RowActions/DeleteDialog/DeleteContent.jsx b/src/components/dashboard/roadSafety/operator/RowActions/DeleteDialog/DeleteContent.jsx index 7a83c08..bb88f78 100644 --- a/src/components/dashboard/roadSafety/operator/RowActions/DeleteDialog/DeleteContent.jsx +++ b/src/components/dashboard/roadSafety/operator/RowActions/DeleteDialog/DeleteContent.jsx @@ -8,7 +8,9 @@ const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => { const requestServer = useRequest({ notificationSuccess: true }); const handleClick = () => { setSubmitting(true); - requestServer(`${DELETE_SAFETY_AND_PRIVACY}/${rowId}`, "delete") + requestServer(`${DELETE_SAFETY_AND_PRIVACY}/${rowId}`, "delete", { + hasSidebarUpdate: true, + }) .then(() => { mutate(); setOpenDeleteDialog(false); diff --git a/src/components/dashboard/roadSafety/report/index.jsx b/src/components/dashboard/roadSafety/report/index.jsx deleted file mode 100644 index cafa140..0000000 --- a/src/components/dashboard/roadSafety/report/index.jsx +++ /dev/null @@ -1,142 +0,0 @@ -"use client"; -import PageTitle from "@/core/components/PageTitle"; -import { LinearProgress, Stack } from "@mui/material"; -import SearchReportList from "./Search"; -import { useEffect, useState } from "react"; -import ReportLists from "./ReportLists"; -import useRequest from "@/lib/hooks/useRequest"; -import { GET_ROAD_SAFETY_CITY_ACTIVITY_REPORT, GET_ROAD_SAFETY_PROVINCE_ACTIVITY_REPORT } from "@/core/utils/routes"; -import moment from "jalali-moment"; -import * as yup from "yup"; -import { useForm } from "react-hook-form"; -import { yupResolver } from "@hookform/resolvers/yup"; -import { usePermissions } from "@/lib/hooks/usePermissions"; -import { useAuth } from "@/lib/contexts/auth"; - -const ReportPage = () => { - const [data, setData] = useState(null); - const { data: userPermissions } = usePermissions(); - const { user } = useAuth(); - const requestServer = useRequest(); - const hasProvincesPermission = userPermissions.includes("show-safety-and-privacy-operator-cartable"); - - const defaultValues = { - from_date: moment(new Date()).format("YYYY-MM-DD"), - date_to: moment(new Date()).format("YYYY-MM-DD"), - province_id: hasProvincesPermission ? "-1" : user.province_id, - axis_type_id: "-1", - }; - const defaultFilter = [ - { value: `${defaultValues.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, - { value: `${defaultValues.axis_type_id}`, datatype: "numeric", id: "axis_type_id", fn: "equals" }, - { value: `${defaultValues.from_date}`, datatype: "date", id: "from_date", fn: "between" }, - { value: `${defaultValues.date_to}`, datatype: "date", id: "date_to", fn: "between" }, - ]; - const [specialFilter, setSpecialFilter] = useState(defaultFilter); - - const onSearchSubmit = async (data) => { - const params = new URLSearchParams(); - params.set("from_date", `${data.from_date}`); - params.set("date_to", `${data.date_to}`); - if (data.axis_type_id != "-1") params.set("axis_type_id", `${data.axis_type_id}`); - - setSpecialFilter([ - { value: `${data.province_id}`, datatype: "numeric", id: "province_id", fn: "equals" }, - { value: `${data.axis_type_id}`, datatype: "numeric", id: "axis_type_id", fn: "equals" }, - { value: `${data.from_date}`, datatype: "date", id: "from_date", fn: "between" }, - { value: `${data.date_to}`, datatype: "date", id: "date_to", fn: "between" }, - ]); - if (data.province_id === "-1") { - try { - const response = await requestServer(`${GET_ROAD_SAFETY_PROVINCE_ACTIVITY_REPORT}?${params}`); - const result = response.data.data; - const nationalReportForItem = result.activities.find((report) => report.province_id === -1); - const nationalReport = { - edare_name: "کل کشور", - ...nationalReportForItem, - }; - - setData({ - data: [ - nationalReport, - ...result.provinces.map((province) => { - const filteredReports = result.activities.find( - (report) => report.province_id === province.id - ); - return { - ...filteredReports, - edare_name: province.name_fa, - }; - }), - ], - filters: data, - }); - } catch (e) {} - } else { - try { - params.set("province_id", `${data.province_id}`); - const response = await requestServer(`${GET_ROAD_SAFETY_CITY_ACTIVITY_REPORT}?${params}`); - const result = response.data.data; - const nationalReportForItem = result.activities.find((report) => report.edare_shahri_id == "-1"); - const nationalReport = { - ...nationalReportForItem, - edare_name: "کل استان", - }; - setData({ - data: [ - nationalReport, - ...result.edarateShahri.map((edare) => { - const filteredReports = result.activities.find( - (report) => report.edare_shahri_id == edare.id - ); - return { - edare_name: edare.name_fa, - ...filteredReports, - }; - }), - ], - filters: data, - }); - } catch (e) {} - } - }; - - useEffect(() => { - if (!userPermissions) return; - onSearchSubmit(defaultValues); - }, [userPermissions]); - - const validationSchema = yup.object().shape({ - from_date: yup - .string() - .required("تاریخ ثبت الزامی است!") - .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ ثبت الزامی است!") - .test("is-valid-date", "تاریخ ثبت معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), - date_to: yup - .string() - .required("تاریخ ثبت الزامی است!") - .matches(/^\d{4}-\d{2}-\d{2}$/, "تاریخ ثبت الزامی است!") - .test("is-valid-date", "تاریخ ثبت معتبر نیست!", (value) => moment(value, "YYYY-MM-DD", true).isValid()), - province_id: yup.string().nullable(), - axis_type_id: yup.string().nullable(), - }); - - const { control, handleSubmit } = useForm({ - defaultValues, - resolver: yupResolver(validationSchema), - mode: "onBlur", - }); - return ( - - - - {!data ? : } - - ); -}; -export default ReportPage; diff --git a/src/components/dashboard/roadSafety/reports/map/ClusterSwitch/index.jsx b/src/components/dashboard/roadSafety/reports/map/ClusterSwitch/index.jsx new file mode 100644 index 0000000..e8fac87 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/ClusterSwitch/index.jsx @@ -0,0 +1,25 @@ +import DoneIcon from "@mui/icons-material/Done"; +import { Chip, Tooltip, Zoom } from "@mui/material"; + +const ClusterSwitch = ({ isCluster, setCluster, disabled, max }) => { + return ( + + + setCluster((c) => !c)} + sx={{ borderRadius: 1 }} + icon={ + + + + } + /> + + + ); +}; +export default ClusterSwitch; diff --git a/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx b/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx new file mode 100644 index 0000000..3cbbe5d --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Filter/index.jsx @@ -0,0 +1,44 @@ +"use client"; +import { Box, Button, Drawer } from "@mui/material"; +import FilterListIcon from "@mui/icons-material/FilterList"; +import { useCallback, useState } from "react"; +import FilterDrawer from "@/core/components/FilterDrawer"; + +const drawerSx = { + overflowY: "hidden", + display: "flex", + flexDirection: "column", + height: "100%", + zIndex: "1300", +}; + +const boxSx = { + width: { xs: 300, sm: 450 }, +}; + +const Filter = ({ filterData, setFilterData }) => { + const [open, setOpen] = useState(false); + + const openDrawer = useCallback(() => setOpen(true), []); + const closeDrawer = useCallback(() => setOpen(false), []); + + return ( + <> + + + + {open && ( + + )} + + + + ); +}; +export default Filter; diff --git a/src/components/dashboard/roadSafety/reports/map/Legend/index.jsx b/src/components/dashboard/roadSafety/reports/map/Legend/index.jsx new file mode 100644 index 0000000..91bc710 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/Legend/index.jsx @@ -0,0 +1,48 @@ +import { Box, Stack, Typography } from "@mui/material"; + +const Legend = () => { + return ( + + + theme.palette.warning.main, + background: (theme) => theme.palette.warning.light, + }} + /> + گام اول (شناسایی) + + + theme.palette.error.main, + background: (theme) => theme.palette.error.light, + }} + /> + گام دوم (مستندات قضایی) + + + theme.palette.success.main, + background: (theme) => theme.palette.success.light, + }} + /> + گام سوم (برخورد) + + + ); +}; +export default Legend; diff --git a/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx new file mode 100644 index 0000000..58064a2 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/ContentInfoItem.jsx @@ -0,0 +1,104 @@ +import { Box, Chip, Divider, Stack, Typography } from "@mui/material"; +import moment from "jalali-moment"; +import Image from "next/image"; + +const ContentInfoItem = ({ data }) => { + return ( + + + + + {data.id !== "" && data.id} + + + + + + {data.province_fa !== "" && data.province_fa} + + + + + + + {data.edare_shahri_name !== "" && data.edare_shahri_name} + + + + + + + {data.info_fa !== "" && data.info_fa} + + + + + + + {data.axis_type_name !== "" && data.axis_type_name} + + + + + + + {data.step_fa !== "" && data.step_fa} + + + + + + + {data.activity_date_time && + moment(data.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD")} + + + {data.step == 3 && ( + + + + + {data.action_date && moment(data.action_date).locale("fa").format("HH:mm | yyyy/MM/DD")} + + + )} + + + + + recognize_picture + + + {data.step == 3 && ( + + + + action_picture + + + )} + + + ); +}; +export default ContentInfoItem; diff --git a/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/index.jsx b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/index.jsx new file mode 100644 index 0000000..3107c53 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/DialogInfoItem/index.jsx @@ -0,0 +1,51 @@ +import DialogLoading from "@/core/components/DialogLoading"; +import { GET_ROAD_SAFETY_DETAILS } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import { Button, DialogActions, DialogContent, DialogTitle } from "@mui/material"; +import { useEffect, useState } from "react"; +import ContentInfoItem from "./ContentInfoItem"; + +const DialogInfoItem = ({ selectedId, closeDialog }) => { + const request = useRequest(); + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + + useEffect(() => { + const controller = new AbortController(); + + const fetchItem = async () => { + setLoading(true); + setData(null); + try { + const response = await request(`${GET_ROAD_SAFETY_DETAILS}/${selectedId}`, "get", { + signal: controller.signal, + }); + setData(response.data.data); + } catch (error) { + } finally { + setLoading(false); + } + }; + + fetchItem(); + + return () => { + controller.abort(); + }; + }, [selectedId]); + + return ( + <> + جزئیات فعالیت + + {loading ? : data && } + + + + + + ); +}; +export default DialogInfoItem; diff --git a/src/components/dashboard/roadSafety/reports/map/PointsOnMap/index.jsx b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/index.jsx new file mode 100644 index 0000000..b965bda --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/PointsOnMap/index.jsx @@ -0,0 +1,71 @@ +import { Dialog, useTheme } from "@mui/material"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { CircleMarker, useMap } from "react-leaflet"; +import MarkerClusterGroup from "react-leaflet-markercluster"; +import DialogInfoItem from "./DialogInfoItem"; + +const PointsOnMap = ({ data, isCluster }) => { + const map = useMap(); + const theme = useTheme(); + const [selectedId, setSelectedId] = useState(null); + const [open, setOpen] = useState(false); + + const openDialog = useCallback(() => setOpen(true), []); + const closeDialog = useCallback(() => setOpen(false), []); + + const getMarkerColor = (status) => { + if (status === 1) return theme.palette.warning.main; + if (status === 2) return theme.palette.error.main; + return theme.palette.success.main; + }; + + const markers = useMemo(() => { + return data.map(({ id, lat, lon, step }) => ( + { + openDialog(); + setSelectedId(id); + }, + }} + key={id} + center={L.latLng(lat, lon)} + radius={8} + color={getMarkerColor(step)} + /> + )); + }, [data, theme]); + + useEffect(() => { + if (data.length === 0) return; + const bounds = L.latLngBounds(data.map(({ lat, lon }) => L.latLng(lat, lon))); + map.flyToBounds(bounds, { duration: 0.7 }); + }, [data]); + + const createClusterCustomIcon = (cluster) => { + const count = cluster.getChildCount(); + return L.divIcon({ + html: `
${count}
`, + className: "custom-marker-cluster", + iconSize: L.point(40, 40, true), + }); + }; + + if (data.length === 0) return null; + + return ( + <> + {isCluster ? ( + + {markers} + + ) : ( + markers + )} + + {open && } + + + ); +}; +export default PointsOnMap; diff --git a/src/components/dashboard/roadSafety/reports/map/index.jsx b/src/components/dashboard/roadSafety/reports/map/index.jsx new file mode 100644 index 0000000..91c0979 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/map/index.jsx @@ -0,0 +1,322 @@ +"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, 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 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; + +const containerStyles = { + width: "100%", + height: "100%", + p: 1, + border: "1px dashed", + borderColor: "divider", + borderRadius: 1, +}; + +const controlBarStyles = { + p: 1, + background: "#fff", +}; + +const RoadSafetyReportMap = () => { + const defaultValues = useMemo( + () => [ + { + header: "استان", + id: "province_id", + filterMode: "equals", + datatype: "numeric", + value: "", + enable: true, + 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: "", + enable: true, + 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: "", + enable: true, + 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: "", + enable: true, + selectOption: () => { + return axisOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + }, + { + header: "وضعیت", + id: "step", + filterMode: "equals", + datatype: "numeric", + value: "", + enable: true, + selectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + }, + { + header: "تاریخ ثبت", + id: "created_at", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + { + header: "تاریخ بازدید", + id: "activity_date_time", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + { + header: "تاریخ بارگذاری مستندات", + id: "judiciary_document_upload_date", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + { + header: "تاریخ اقدام", + id: "action_date", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + ], + [] + ); + const requestServer = useRequest(); + 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 filters = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filters || [])); + return params.toString(); + }, [filterData]); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const params = new URLSearchParams(); + const filters = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filters || [])); + const query = params.toString(); + const response = await requestServer(`${GET_ROAD_SAFETY_REPORT_MAP}?${query}`); + const fetchedData = response?.data?.data || []; + + if (fetchedData.length > MAX_POINTS) { + setCluster(true); + } + setData(fetchedData); + } catch (error) { + console.log(error); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [filterData, buildQueryParams]); + + return ( + + + + + MAX_POINTS} + max={MAX_POINTS} + /> + + + + + + + + + + + {isLoading && ( + + + + )} + + + + + ); +}; +export default RoadSafetyReportMap; diff --git a/src/components/dashboard/roadSafety/report/ExcelPrint/index.jsx b/src/components/dashboard/roadSafety/reports/reportPage/ExcelPrint/index.jsx similarity index 63% rename from src/components/dashboard/roadSafety/report/ExcelPrint/index.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/ExcelPrint/index.jsx index 192d4fb..25ccefa 100644 --- a/src/components/dashboard/roadSafety/report/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadSafety/reports/reportPage/ExcelPrint/index.jsx @@ -1,44 +1,39 @@ "use client"; -import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; -import { useMemo, useState } from "react"; -import moment from "jalali-moment"; -import FileSaver from "file-saver"; -import DescriptionIcon from "@mui/icons-material/Description"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; +import { EXPORT_COUNTRY_ROAD_SAFETY_REPORT, EXPORT_PROVINCE_ROAD_SAFETY_REPORT } from "@/core/utils/routes"; import useRequest from "@/lib/hooks/useRequest"; import { useTheme } from "@emotion/react"; -import { EXPORT_COUNTRY_ROAD_PATROL_REPORTS, EXPORT_PROVINCE_ROAD_PATROL_REPORTS } from "@/core/utils/routes"; +import DescriptionIcon from "@mui/icons-material/Description"; +import { Button, CircularProgress, IconButton, useMediaQuery } from "@mui/material"; +import FileSaver from "file-saver"; +import moment from "jalali-moment"; +import { useState } from "react"; const PrintExcel = ({ table, filterData }) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); const [loading, setLoading] = useState(false); const requestServer = useRequest({ notificationSuccess: true }); - const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { - if (filter.value) { - acc.push({ id: filter.id, value: filter.value }); - } - return acc; - }, []); - - const filterParams = useMemo(() => { - const params = new URLSearchParams(); - activeFilters.length > 0 && - activeFilters.map((filter, index) => { - params.set(`${filter.id}`, filter.value); - }); - return params; - }, [activeFilters]); const clickHandler = () => { setLoading(true); - const CountryOrProvince = activeFilters.find((item) => item.id === "province_id"); - const requestUrl = - CountryOrProvince.value === "-1" ? EXPORT_COUNTRY_ROAD_PATROL_REPORTS : EXPORT_PROVINCE_ROAD_PATROL_REPORTS; + const filters = DataTableFilterDataStructure(filterData, isArrayEmpty); + const filterParams = new URLSearchParams(); + let requestUrl; + filterParams.set("filters", JSON.stringify(filters || [])); + if (filterData.province_id.value === "") { + requestUrl = EXPORT_COUNTRY_ROAD_SAFETY_REPORT; + } else { + filterParams.set("province_id", filterData.province_id.value); + requestUrl = EXPORT_PROVINCE_ROAD_SAFETY_REPORT; + } + requestServer(`${requestUrl}?${filterParams}`, "get", { requestOptions: { responseType: "blob" }, }) .then((response) => { - const filename = `خروجی کارتابل گزارشات گشت راهداری تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + const filename = `خروجی کارتابل نگهداری حریم راه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; FileSaver.saveAs(response.data, filename); }) .catch(() => {}) diff --git a/src/components/dashboard/roadSafety/report/ReportLists.jsx b/src/components/dashboard/roadSafety/reports/reportPage/ReportLists.jsx similarity index 98% rename from src/components/dashboard/roadSafety/report/ReportLists.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/ReportLists.jsx index 9d64db2..cb0e8b7 100644 --- a/src/components/dashboard/roadSafety/report/ReportLists.jsx +++ b/src/components/dashboard/roadSafety/reports/reportPage/ReportLists.jsx @@ -4,7 +4,7 @@ import { Box } from "@mui/material"; import { useMemo } from "react"; import Toolbar from "./Toolbar"; -const ReportLists = ({ data, specialFilter }) => { +const ReportLists = ({ data, filterData, setFilterData, isLoading }) => { const columns = useMemo(() => { return [ { @@ -289,11 +289,13 @@ const ReportLists = ({ data, specialFilter }) => { ); diff --git a/src/components/dashboard/roadSafety/report/Search/FromDateController.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/FromDateController.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/FromDateController.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/FromDateController.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/SearchReportField.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/SearchReportField.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/SearchReportField.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/SearchReportField.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/SelectAxisType.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/SelectAxisType.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/SelectAxisType.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/SelectAxisType.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/SelectProvince.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/SelectProvince.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/SelectProvince.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/SelectProvince.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/ToDateController.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/ToDateController.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/ToDateController.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/ToDateController.jsx diff --git a/src/components/dashboard/roadSafety/report/Search/index.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Search/index.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Search/index.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Search/index.jsx diff --git a/src/components/dashboard/roadSafety/report/Toolbar.jsx b/src/components/dashboard/roadSafety/reports/reportPage/Toolbar.jsx similarity index 100% rename from src/components/dashboard/roadSafety/report/Toolbar.jsx rename to src/components/dashboard/roadSafety/reports/reportPage/Toolbar.jsx diff --git a/src/components/dashboard/roadSafety/reports/reportPage/index.jsx b/src/components/dashboard/roadSafety/reports/reportPage/index.jsx new file mode 100644 index 0000000..a8f8998 --- /dev/null +++ b/src/components/dashboard/roadSafety/reports/reportPage/index.jsx @@ -0,0 +1,264 @@ +"use client"; +import CustomSelectByDependency from "@/core/components/FilterDrawer/fieldsType/CustomSelectByDependency"; +import PageTitle from "@/core/components/PageTitle"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; +import { GET_ROAD_SAFETY_CITY_ACTIVITY_REPORT, GET_ROAD_SAFETY_PROVINCE_ACTIVITY_REPORT } from "@/core/utils/routes"; +import { useAuth } from "@/lib/contexts/auth"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import useProvinces from "@/lib/hooks/useProvince"; +import useRequest from "@/lib/hooks/useRequest"; +import useSafetyAndPrivacyGetItems from "@/lib/hooks/useSafetyAndPrivacyGetItems"; +import { LinearProgress, Stack } from "@mui/material"; +import { useEffect, useMemo, useState } from "react"; +import ReportLists from "./ReportLists"; + +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 ReportPage = () => { + const { data: userPermissions } = usePermissions(); + const hasProvincesPermission = userPermissions.includes("show-safety-and-privacy-operator-cartable"); + const { user } = useAuth(); + const requestServer = useRequest(); + const [data, setData] = useState(null); + const [isLoading, setLoading] = useState(true); + const defaultValues = useMemo( + () => [ + { + header: "استان", + id: "province_id", + filterMode: "equals", + datatype: "numeric", + value: hasProvincesPermission ? "" : user.province_id, + enable: hasProvincesPermission, + 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 ( + + ); + }, + }, + { + header: "مورد مشاهده شده", + id: "info_id", + filterMode: "equals", + datatype: "numeric", + value: "", + enable: true, + 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: "", + enable: true, + selectOption: () => { + return axisOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + }, + { + header: "وضعیت", + id: "step", + filterMode: "equals", + datatype: "numeric", + value: "", + enable: true, + selectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + }, + { + header: "تاریخ ثبت", + id: "created_at", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + { + header: "تاریخ بازدید", + id: "activity_date_time", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + { + header: "تاریخ بارگذاری مستندات", + id: "judiciary_document_upload_date", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + { + header: "تاریخ اقدام", + id: "action_date", + filterMode: "between", + datatype: "date", + value: ["", ""], + enable: true, + }, + ], + [hasProvincesPermission, user.province_id] + ); + + const [filterData, setFilterData] = useState( + defaultValues.reduce((acc, item) => { + acc[item.id] = item; + return acc; + }, {}) + ); + + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + const filters = DataTableFilterDataStructure(filterData, isArrayEmpty); + const params = new URLSearchParams(); + params.set("filters", JSON.stringify(filters || [])); + if (filterData.province_id.value === "") { + try { + const response = await requestServer( + `${GET_ROAD_SAFETY_PROVINCE_ACTIVITY_REPORT}?${params.toString()}` + ); + const result = response.data.data; + const nationalReportForItem = result.activities.find((report) => report.province_id === -1); + const nationalReport = { + edare_name: "کل کشور", + ...nationalReportForItem, + }; + + setData([ + nationalReport, + ...result.provinces.map((province) => { + const filteredReports = result.activities.find( + (report) => report.province_id === province.id + ); + return { + ...filteredReports, + edare_name: province.name_fa, + }; + }), + ]); + } catch (e) { + console.log(e); + } + } else { + params.set("province_id", filterData.province_id.value); + try { + const response = await requestServer( + `${GET_ROAD_SAFETY_CITY_ACTIVITY_REPORT}?${params.toString()}` + ); + const result = response.data.data; + const nationalReportForItem = result.activities.find( + (report) => report.edare_shahri_id == "-1" + ); + const nationalReport = { + ...nationalReportForItem, + edare_name: "کل استان", + }; + setData([ + nationalReport, + ...result.edarateShahri.map((edare) => { + const filteredReports = result.activities.find( + (report) => report.edare_shahri_id == edare.id + ); + return { + edare_name: edare.name_fa, + ...filteredReports, + }; + }), + ]); + } catch (e) { + console.log(e); + } + } + } catch (error) { + console.log(error); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [filterData]); + + return ( + + + {!data ? ( + + ) : ( + + )} + + ); +}; +export default ReportPage; diff --git a/src/components/dashboard/userActivities/Actions/Create/Form/CreateFormContent.jsx b/src/components/dashboard/userActivities/Actions/Create/Form/CreateFormContent.jsx index 2898404..ff2f25f 100644 --- a/src/components/dashboard/userActivities/Actions/Create/Form/CreateFormContent.jsx +++ b/src/components/dashboard/userActivities/Actions/Create/Form/CreateFormContent.jsx @@ -21,7 +21,7 @@ const CreateFormContent = ({ setOpen, defaultValues, onSubmitBase }) => { } = useForm({ defaultValues, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const handleOnSubmit = async (data) => { await onSubmitBase(data); diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CityContent.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CityContent.jsx new file mode 100644 index 0000000..3069208 --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CityContent.jsx @@ -0,0 +1,33 @@ +import SelectBox from "@/core/components/SelectBox"; +import { useEffect, useState } from "react"; +import useCities from "@/lib/hooks/useCities"; + +const CityContent = ({ provinceID, field, fieldState: { error } }) => { + const { cityList, loadingCityList, errorCityList } = useCities(provinceID); + const [prevDependency, setPrevDependency] = useState(provinceID); + + useEffect(() => { + if (prevDependency === provinceID) return; + field.onChange(null); + setPrevDependency(provinceID); + }, [provinceID]); + + return ( + + ); +}; +export default CityContent; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CityController.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CityController.jsx new file mode 100644 index 0000000..72de542 --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CityController.jsx @@ -0,0 +1,14 @@ +import { Controller, useWatch } from "react-hook-form"; +import CityContent from "./CityContent"; + +const CityController = ({ control }) => { + const provinceID = useWatch({ control, name: "province_id" }); + return ( + } + /> + ); +}; +export default CityController; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx new file mode 100644 index 0000000..ef2e51d --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/CreateTollHouseContent.jsx @@ -0,0 +1,192 @@ +import StyledForm from "@/core/components/StyledForm"; +import { Box, Button, DialogActions, DialogContent, Tab, Tabs } from "@mui/material"; +import ExitToAppIcon from "@mui/icons-material/ExitToApp"; +import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; +import BeenhereIcon from "@mui/icons-material/Beenhere"; +import OtherHousesIcon from "@mui/icons-material/OtherHouses"; +import LocalShippingIcon from "@mui/icons-material/LocalShipping"; +import EditLocationAltIcon from "@mui/icons-material/EditLocationAlt"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { mixed, number, object, string } from "yup"; +import TollHouseInfo from "./TollHouseInfo"; +import KeyboardDoubleArrowLeftIcon from "@mui/icons-material/KeyboardDoubleArrowLeft"; +import MachineInfo from "@/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo"; +import TollHouseLocation from "@/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation"; + +function TabPanel(props) { + const { children, value, index } = props; + return ( + + ); +} + +const validationSchema = object({ + name: string().required("وارد کردن نام راهدارخانه الزامیست!"), + status: string().required("وارد کردن وضعیت الزامیست!"), + type: string().required("وارد کردن نوع الزامیست!"), + phone: string().required("وارد کردن تلفن راهدارخانه الزامیست!"), + team_num: string().required("وارد کردن تعداد اکیپ الزامیست!"), + staff_num: string().required("وارد کردن تعداد پرسنل الزامیست!"), + responsible_name: string().required("وارد کردن نام مسئول الزامیست!"), + responsible_mobile: string() + .required("وارد کردن تلفن مسئول الزامیست!") + .matches(/^09\d{9}$/, "شماره تلفن مسئول باید با ۰۹ شروع شده و ۱۱ رقم باشد!"), + successor_name: string().required("وارد کردن نام جانشین الزامیست!"), + successor_mobile: string() + .required("وارد کردن تلفن جانشین الزامیست!") + .matches(/^09\d{9}$/, "شماره تلفن جانشین باید با ۰۹ شروع شده و ۱۱ رقم باشد!"), + province_id: number().required("وارد کردن استان الزامیست!"), + city_id: number().required("وارد کردن شهرستان الزامیست!"), + machines_light: string().required("وارد کردن ماشین آلات سبک الزامیست!"), + machines_sheavy: string().required("وارد کردن ماشین آلات نیمه سنگین الزامیست!"), + machines_heavy: string().required("وارد کردن ماشین آلات سنگین الزامیست!"), + overview_files_1: mixed().nullable().required("لطفا نمای کلی از راهداری را بارگذاری کنید!"), + overview_files_2: mixed().nullable().required("لطفا آشیانه ماشین آلات را بارگذاری کنید!"), + overview_files_3: mixed().nullable().required("لطفا انباری شن و ماسه را بارگذاری کنید!"), + overview_files_4: mixed().nullable().required("لطفا محیط کاری را بارگذاری کنید!"), + overview_files_5: mixed().nullable().required("لطفا سایر تصاویر را بارگذاری کنید!"), + start_point: mixed() + .test("start-point-required", "لطفاً محل پروژه را مشخص کنید!", function (value) { + return !!value; + }) + .required("لطفاً محل پروژه را مشخص کنید!"), +}); +const CreateTollHouseContent = ({ setOpen, SubmitCreateTollHouse, defaultValues }) => { + const [tabState, setTabState] = useState(0); + const handleClose = () => { + setOpen(false); + }; + + const handleChangeTab = (event, newValue) => { + setTabState(newValue); + }; + const handleNext = async () => { + let fieldsToValidate = []; + if (tabState === 0) { + fieldsToValidate = [ + "name", + "province_id", + "city_id", + "status", + "type", + "phone", + "team_num", + "staff_num", + "responsible_name", + "responsible_mobile", + "successor_name", + "successor_mobile", + ]; + } else if (tabState === 1) { + fieldsToValidate = [ + "machines_light", + "machines_sheavy", + "machines_heavy", + "overview_files_1", + "overview_files_2", + "overview_files_3", + "overview_files_4", + "overview_files_5", + ]; + } else if (tabState === 2) { + fieldsToValidate = ["start_point"]; + } + const isValid = await trigger(fieldsToValidate); + if (isValid) { + setTabState(tabState + 1); + } + }; + const handlePrev = () => { + if (tabState === 0) { + handleClose(); + } else { + setTabState(tabState - 1); + } + }; + const { + control, + handleSubmit, + trigger, + setValue, + formState: { isSubmitting, errors }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "all", + }); + const onSubmitBase = async (data) => { + await SubmitCreateTollHouse(data); + }; + return ( + + + } label="اطلاعات راهدارخانه" /> + } label="ماشین آلات" /> + } label="موقعیت" /> + + + + + + + + + + + + + + + + + {tabState !== 2 && ( + + )} + {tabState === 2 && ( + + )} + + + ); +}; +export default CreateTollHouseContent; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/ImageUpload.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/ImageUpload.jsx new file mode 100644 index 0000000..5186a4b --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/ImageUpload.jsx @@ -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 ( + + + {title} + + + {error ? error.message : null} + + ); +}; +export default ImageUpload; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/LogesticController.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/LogesticController.jsx new file mode 100644 index 0000000..513abbf --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/LogesticController.jsx @@ -0,0 +1,42 @@ +import { Controller, useWatch } from "react-hook-form"; +import SelectBox from "@/core/components/SelectBox"; +import useProvinces from "@/lib/hooks/useProvince"; +import { Grid } from "@mui/material"; +import CityController from "./CityController"; + +const LogesticController = ({ control }) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const isProvince = useWatch({ control, name: "is_province" }); + return ( + + {!isProvince ? ( + + { + return ( + + ); + }} + /> + + ) : null} + + + + + ); +}; +export default LogesticController; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo.jsx new file mode 100644 index 0000000..44d3fea --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/MachineInfo.jsx @@ -0,0 +1,199 @@ +import { Grid, Stack, TextField } from "@mui/material"; +import { Controller } from "react-hook-form"; +import PersianTextField from "@/core/components/PersianTextField"; +import ImageUpload from "@/components/dashboard/damages/operator/Actions/create/Forms/ImageUpload"; + +const MachineInfo = ({ control }) => { + return ( + + + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + + + ( + + )} + /> + + + ( + + )} + /> + + + + + + + ( + + )} + /> + + + ( + + )} + /> + + + + + + + ( + + )} + /> + + + + + ); +}; +export default MachineInfo; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx new file mode 100644 index 0000000..d06a823 --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseInfo.jsx @@ -0,0 +1,311 @@ +import { Grid, Stack, TextField } from "@mui/material"; +import { Controller } from "react-hook-form"; +import SelectBox from "@/core/components/SelectBox"; +import useProvince from "@/lib/hooks/useProvince"; +import PersianTextField from "@/core/components/PersianTextField"; +import CityController from "./CityController"; +import LogesticController from "./LogesticController"; + +const TollHouseInfo = ({ control }) => { + const { provinces, loadingProvinces, errorProvinces } = useProvince(); + return ( + + + + + ( + + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + + + + + + { + return ( + + ); + }} + /> + + + { + return ( + + ); + }} + /> + + + + + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + + + ( + + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + + + ( + + )} + /> + + + ( + { + const inputValue = event.target.value; + if (isNaN(Number(inputValue))) { + return; + } + onChange(inputValue); + }} + /> + )} + /> + + + + + ); +}; +export default TollHouseInfo; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation.jsx new file mode 100644 index 0000000..98d36a7 --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/TollHouseLocation.jsx @@ -0,0 +1,20 @@ +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import { Stack } from "@mui/material"; +import { useWatch } from "react-hook-form"; + +const TollHouseLocation = ({ setValue, errors, control }) => { + const StartPoint = useWatch({ control, name: "start_point" }); + return ( + <> + + + + + ); +}; +export default TollHouseLocation; diff --git a/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx new file mode 100644 index 0000000..621f4e6 --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/CreateTollHouse/index.jsx @@ -0,0 +1,140 @@ +import { Button, Dialog, IconButton, useMediaQuery } from "@mui/material"; +import { useState } from "react"; +import CreateTollHouseContent from "./CreateTollHouseContent"; +import { AddCircle } from "@mui/icons-material"; +import { useTheme } from "@emotion/react"; +import { CREATE_TOLL_HOUSE } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; +import CloseIcon from "@mui/icons-material/Close"; +import { useAuth } from "@/lib/contexts/auth"; +import { usePermissions } from "@/lib/hooks/usePermissions"; + +const CreateTollHouse = ({ mutate }) => { + const requestServer = useRequest({ notificationSuccess: true }); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + const { user } = useAuth(); + const { data: userPermissions } = usePermissions(); + const hasCreateProvincePermission = userPermissions.some((item) => ["add-tollhouse"].includes(item)); + const handleOpen = () => { + setOpen(true); + }; + const SubmitCreateTollHouse = async (result) => { + const fields = [ + "province_id", + "city_id", + "name", + "status", + "type", + "team_num", + "staff_num", + "phone", + "responsible_name", + "responsible_mobile", + "successor_name", + "successor_mobile", + "machines_light", + "machines_sheavy", + "machines_heavy", + "overview_files_1", + "overview_files_2", + "overview_files_3", + "overview_files_4", + "overview_files_5", + ]; + const formData = new FormData(); + fields.forEach((field) => { + if (result[field] !== undefined && result[field] !== null) { + formData.append(field, result[field]); + } + }); + formData.append("lat", result.start_point.lat); + formData.append("lng", result.start_point.lng); + await requestServer(`${CREATE_TOLL_HOUSE}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpen(false); + }) + .catch(() => {}); + }; + const defaultValues = { + name: "", + is_province: !hasCreateProvincePermission, + province_id: !hasCreateProvincePermission ? user.province_id : null, + city_id: null, + status: "", + type: "", + phone: "", + team_num: "", + staff_num: "", + responsible_name: "", + responsible_mobile: "", + successor_name: "", + successor_mobile: "", + machines_light: "", + machines_sheavy: "", + machines_heavy: "", + overview_files_1: null, + overview_files_2: null, + overview_files_3: null, + overview_files_4: null, + overview_files_5: null, + start_point: "", + }; + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + setOpen(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"md"} + scroll="paper" + > + setOpen(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {open && ( + + )} + + + ); +}; +export default CreateTollHouse; diff --git a/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx b/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx new file mode 100644 index 0000000..774e5cb --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/Edit/EditController.jsx @@ -0,0 +1,107 @@ +import useRequest from "@/lib/hooks/useRequest"; +import { useEffect, useState } from "react"; +import { GET_TOLL_HOUSE_DETAILS, UPDATE_TOLL_HOUSE_ITEM } from "@/core/utils/routes"; +import DialogLoading from "@/core/components/DialogLoading"; +import CreateTollHouseContent from "../CreateTollHouse/CreateTollHouseContent"; +import { usePermissions } from "@/lib/hooks/usePermissions"; + +const EditController = ({ rowId, mutate, setOpenEditDialog }) => { + const { data: userPermissions } = usePermissions(); + const hasCreateProvincePermission = userPermissions.some((item) => ["add-receipt"].includes(item)); + const requestServer = useRequest(); + const [tollHouseItemDetails, setTollHouseItemDetails] = useState(null); + const [tollHouseItemDetailsLoading, setTollHouseItemDetailsLoading] = useState(false); + + useEffect(() => { + setTollHouseItemDetailsLoading(true); + requestServer(`${GET_TOLL_HOUSE_DETAILS}/${rowId}`, "get") + .then((response) => { + setTollHouseItemDetails(response.data.data); + setTollHouseItemDetailsLoading(false); + }) + .catch((e) => { + setTollHouseItemDetailsLoading(false); + }); + }, [rowId]); + + const defaultData = { + phone: tollHouseItemDetails?.phone || "", + is_province: !hasCreateProvincePermission, + province_id: tollHouseItemDetails?.province_id || null, + city_id: tollHouseItemDetails?.city_id || null, + start_point: { lat: tollHouseItemDetails?.lat || "", lng: tollHouseItemDetails?.lng || "" }, + name: tollHouseItemDetails?.name || "", + status: tollHouseItemDetails?.status || "", + type: tollHouseItemDetails?.type || "", + team_num: tollHouseItemDetails?.team_num || "", + staff_num: tollHouseItemDetails?.staff_num || "", + responsible_name: tollHouseItemDetails?.responsible_name || "", + responsible_mobile: tollHouseItemDetails?.responsible_mobile || "", + successor_name: tollHouseItemDetails?.successor_name || "", + successor_mobile: tollHouseItemDetails?.successor_mobile || "", + machines_light: tollHouseItemDetails?.machines_light || "", + machines_sheavy: tollHouseItemDetails?.machines_sheavy || "", + machines_heavy: tollHouseItemDetails?.machines_heavy || "", + overview_files_1: tollHouseItemDetails?.files[0]?.path || null, + overview_files_2: tollHouseItemDetails?.files[1]?.path || null, + overview_files_3: tollHouseItemDetails?.files[2]?.path || null, + overview_files_4: tollHouseItemDetails?.files[3]?.path || null, + overview_files_5: tollHouseItemDetails?.files[4]?.path || null, + }; + const HandleSubmit = async (result) => { + const fields = [ + "province_id", + "city_id", + "name", + "status", + "type", + "team_num", + "staff_num", + "phone", + "responsible_name", + "responsible_mobile", + "successor_name", + "successor_mobile", + "machines_light", + "machines_sheavy", + "machines_heavy", + ]; + const formData = new FormData(); + fields.forEach((field) => { + if (result[field] !== undefined && result[field] !== null) { + formData.append(field, result[field]); + } + }); + for (let i = 1; i <= 5; i++) { + if (result[`overview_files_${i}`] !== defaultData[`overview_files_${i}`]) { + formData.append(`overview_files_${i}`, result[`overview_files_${i}`]); + } + } + formData.append("lat", result.start_point.lat); + formData.append("lng", result.start_point.lng); + + await requestServer(`${UPDATE_TOLL_HOUSE_ITEM}/${rowId}`, "post", { + data: formData, + notificationSuccess: true, + }) + .then(() => { + mutate(); + setOpenEditDialog(false); + }) + .catch(() => {}); + }; + return ( + <> + {tollHouseItemDetailsLoading ? ( + + ) : ( + + )} + + ); +}; +export default EditController; diff --git a/src/components/infrastructure/tollHouse/Form/Edit/index.jsx b/src/components/infrastructure/tollHouse/Form/Edit/index.jsx new file mode 100644 index 0000000..53be78c --- /dev/null +++ b/src/components/infrastructure/tollHouse/Form/Edit/index.jsx @@ -0,0 +1,59 @@ +import { Dialog, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import BorderColorIcon from "@mui/icons-material/BorderColor"; +import EditController from "./EditController"; +import CloseIcon from "@mui/icons-material/Close"; + +const EditForm = ({ row, mutate, rowId }) => { + const [openEditDialog, setOpenEditDialog] = useState(false); + + return ( + <> + + { + setOpenEditDialog(true); + }} + > + + + + + setOpenEditDialog(false)} + sx={(theme) => ({ + position: "absolute", + right: 8, + top: 8, + zIndex: 50, + color: theme.palette.grey[500], + })} + > + + + {openEditDialog && ( + + )} + + + ); +}; +export default EditForm; diff --git a/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/DeleteContent.jsx b/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/DeleteContent.jsx new file mode 100644 index 0000000..3f3095a --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/DeleteContent.jsx @@ -0,0 +1,39 @@ +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { DELETE_TOLL_HOUSE_ITEM } from "@/core/utils/routes"; + +const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notificationSuccess: true }); + const handleClick = () => { + setSubmitting(true); + requestServer(`${DELETE_TOLL_HOUSE_ITEM}/${rowId}`, "delete") + .then(() => { + mutate(); + setOpenDeleteDialog(false); + setSubmitting(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + آیا از حذف راهدارخانه اطمینان دارید؟ + + + + + + + + ); +}; +export default DeleteContent; diff --git a/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/index.jsx b/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/index.jsx new file mode 100644 index 0000000..54278bd --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/DeleteDialog/index.jsx @@ -0,0 +1,34 @@ +import DeleteIcon from "@mui/icons-material/Delete"; +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import DeleteContent from "./DeleteContent"; +const DeleteDialog = ({ rowId, mutate }) => { + const [openDeleteDialog, setOpenDeleteDialog] = useState(false); + + return ( + <> + + setOpenDeleteDialog(true)}> + + + + setOpenDeleteDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"md"} + > + حذف راهدارخانه + {openDeleteDialog && ( + + )} + + + ); +}; +export default DeleteDialog; diff --git a/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImageFormContent.jsx b/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImageFormContent.jsx new file mode 100644 index 0000000..c8f22d0 --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImageFormContent.jsx @@ -0,0 +1,45 @@ +import { Box, Stack, Typography, useTheme } from "@mui/material"; +import Image from "next/image"; + +const ImageFormContent = ({ image, title }) => { + const theme = useTheme(); + return ( + + + {title} + + + Image + + + ); +}; +export default ImageFormContent; diff --git a/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImagesContent.jsx b/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImagesContent.jsx new file mode 100644 index 0000000..4b66734 --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/ImageForm/ImagesContent.jsx @@ -0,0 +1,38 @@ +import { useEffect, useState } from "react"; +import ImageFormContent from "./ImageFormContent"; +import DialogLoading from "@/core/components/DialogLoading"; +import useRequest from "@/lib/hooks/useRequest"; +import { GET_TOLL_HOUSE_IMAGES } from "@/core/utils/routes"; +import { Typography } from "@mui/material"; + +const ImagesContent = ({ rowId }) => { + const requestServer = useRequest(); + const [tollHouseImages, setTollHouseImages] = useState(null); + const [tollHouseImagesLoading, setTollHouseImagesLoading] = useState(false); + useEffect(() => { + setTollHouseImagesLoading(true); + requestServer(`${GET_TOLL_HOUSE_IMAGES}/${rowId}`, "get") + .then((response) => { + setTollHouseImages(response.data.data); + setTollHouseImagesLoading(false); + }) + .catch((e) => { + setTollHouseImagesLoading(false); + }); + }, [rowId]); + + return ( + <> + {tollHouseImagesLoading ? ( + + ) : tollHouseImages ? ( + tollHouseImages.map((image, index) => { + return ; + }) + ) : ( + تصویری در سامانه یافت نشد + )} + + ); +}; +export default ImagesContent; diff --git a/src/components/infrastructure/tollHouse/RowActions/ImageForm/index.jsx b/src/components/infrastructure/tollHouse/RowActions/ImageForm/index.jsx new file mode 100644 index 0000000..6ac8d25 --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/ImageForm/index.jsx @@ -0,0 +1,43 @@ +import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; +import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useEffect, useState } from "react"; +import ImagesContent from "./ImagesContent"; +import useRequest from "@/lib/hooks/useRequest"; +import { GET_TOLL_HOUSE_IMAGES } from "@/core/utils/routes"; +import DialogLoading from "@/core/components/DialogLoading"; + +const ImageDialog = ({ rowId }) => { + const [openImageDialog, setOpenImageDialog] = useState(false); + + return ( + <> + + setOpenImageDialog(true)}> + + + + setOpenImageDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"sm"} + scroll="paper" + > + تصاویر + {openImageDialog && } + + + + + + ); +}; + +export default ImageDialog; diff --git a/src/components/infrastructure/tollHouse/RowActions/LocationForm/LocationFormContent.jsx b/src/components/infrastructure/tollHouse/RowActions/LocationForm/LocationFormContent.jsx new file mode 100644 index 0000000..14b3db3 --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/LocationForm/LocationFormContent.jsx @@ -0,0 +1,35 @@ +"use client"; +import { Box, Button, DialogActions, DialogContent, Stack } from "@mui/material"; +import React from "react"; +import dynamic from "next/dynamic"; +import MapLoading from "@/core/components/MapLayer/Loading"; +import ShowLocationMarker from "@/core/components/ShowLocationMarker"; +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_lat, end_lng }) => { + return ( + <> + + + + + + + + + + + + ); +}; +export default LocationFormContent; diff --git a/src/components/infrastructure/tollHouse/RowActions/LocationForm/index.jsx b/src/components/infrastructure/tollHouse/RowActions/LocationForm/index.jsx new file mode 100644 index 0000000..f578ca8 --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/LocationForm/index.jsx @@ -0,0 +1,38 @@ +import React, { useState } from "react"; +import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; +import ExploreIcon from "@mui/icons-material/Explore"; +import LocationFormContent from "./LocationFormContent"; +const LocationForm = ({ start_lat, start_lng, end_lat, end_lng }) => { + const [openLocationDialog, setOpenLocationDialog] = useState(false); + + return ( + <> + + setOpenLocationDialog(true)}> + + + + setOpenLocationDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + dir="rtl" + maxWidth={"md"} + > + موقعیت + + + + ); +}; +export default LocationForm; diff --git a/src/components/infrastructure/tollHouse/RowActions/index.jsx b/src/components/infrastructure/tollHouse/RowActions/index.jsx new file mode 100644 index 0000000..8801bda --- /dev/null +++ b/src/components/infrastructure/tollHouse/RowActions/index.jsx @@ -0,0 +1,21 @@ +import { Box } from "@mui/material"; +import EditForm from "../Form/Edit"; +import DeleteDialog from "./DeleteDialog"; +import { usePermissions } from "@/lib/hooks/usePermissions"; + +const RowActions = ({ row, mutate }) => { + const { data: userPermissions } = usePermissions(); + const hasEditPermission = userPermissions.some((item) => + ["edit-tollhouse", "edit-tollhouse-province"].includes(item) + ); + const hasDeletePermission = userPermissions.some((item) => + ["delete-tollhouse", "delete-tollhouse-province"].includes(item) + ); + return ( + + {hasEditPermission && } + {hasDeletePermission && } + + ); +}; +export default RowActions; diff --git a/src/components/infrastructure/tollHouse/TollHouseList.jsx b/src/components/infrastructure/tollHouse/TollHouseList.jsx new file mode 100644 index 0000000..2b0a39c --- /dev/null +++ b/src/components/infrastructure/tollHouse/TollHouseList.jsx @@ -0,0 +1,412 @@ +"use client"; +import { useEffect, useMemo, useState } from "react"; +import { Box, Stack, Typography } from "@mui/material"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import Toolbar from "./Toolbar"; +import { GET_TOLL_HOUSE_LIST } from "@/core/utils/routes"; +import moment from "jalali-moment"; +import RowActions from "./RowActions"; +import useProvinces from "@/lib/hooks/useProvince"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import LocationForm from "./RowActions/LocationForm"; +import ImageDialog from "./RowActions/ImageForm"; + +const statusOptions = [ + { value: "", label: "همه وضعیت ها" }, + { value: 0, label: "غیرفعال" }, + { value: 1, label: "فعال" }, +]; +const typeOptions = [ + { value: "", label: "همه نوع ها" }, + { value: 0, label: "فصلی" }, + { value: 1, label: "داعمی" }, + { value: 2, label: "موقت" }, +]; +const TollHouseList = () => { + const { data: userPermissions } = usePermissions(); + const hasProvincePermission = userPermissions.some((item) => ["add-tollhouse"].includes(item)); + + const columns = useMemo(() => { + return [ + { + accessorKey: "id", + header: "کد یکتا", // Unique Code + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + + { + header: "اطلاعات راهدارخانه", + id: "tollHouseInfo", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + columns: [ + { + accessorKey: "name", + header: "نام راهدارخانه", + id: "name", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "province_id", + header: "استان", + id: "province_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 130, + ColumnSelectComponent: (props) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const getColumnSelectOptions = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.province_name}, + }, + { + accessorKey: "city_id", + header: "شهرستان", // Office + id: "city_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + dependencyId: "province_id", + grow: false, + size: 120, + ColumnSelectComponent: (props) => { + const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( + props.dependencyFieldValue.value + ); + const [prevDependency, setPrevDependency] = useState(props.dependencyFieldValue.value); + + const getColumnSelectOptions = useMemo(() => { + if (props.dependencyFieldValue.value === "") { + return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; + } + if (loadingEdaratList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorEdaratList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل شهر ها" }, + ...edaratList.map((edare) => ({ + value: edare.id, + label: edare.name_fa, + })), + ]; + }, [edaratList, loadingEdaratList, errorEdaratList]); + useEffect(() => { + if (prevDependency === props.dependencyFieldValue?.value) return; + props.handleChange({ ...props.filterParameters, value: "" }); + setPrevDependency(props.dependencyFieldValue?.value); + }, [props.dependencyFieldValue?.value]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.city_name}, + }, + { + accessorKey: "status", + header: "وضعیت", + id: "status", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + columnSelectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + Cell: ({ row }) => { + const status = statusOptions.find((option) => option.value == row.original.status); + return {status?.label || ""}; + }, + }, + { + accessorKey: "type", + header: "نوع", + id: "type", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 100, + columnSelectOption: () => { + return typeOptions.map((type) => ({ + value: type.value, + label: type.label, + })); + }, + Cell: ({ row }) => { + return {row.original.type_fa}; + }, + }, + { + accessorKey: "phone", + header: "تلفن راهدارخانه", + id: "phone", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "team_num", + header: "تعداد اکیپ", + id: "team_num", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "staff_num", + header: "تعداد پرسنل", + id: "staff_num", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "responsible_name", + header: "مسئول", + id: "responsible_name", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "responsible_mobile", + header: "شماره مسئول", + id: "responsible_mobile", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "successor_name", + header: "جانشین", + id: "successor_name", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + { + accessorKey: "successor_mobile", + header: "شماره جانشین", + id: "successor_mobile", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 120, + }, + ], + }, + { + header: "ماشین آلات", + id: "machines", + enableColumnFilter: false, + enableSorting: false, + datatype: "text", + grow: false, + size: 50, + columns: [ + { + accessorKey: "machines_light", + header: "ماشین آلات سبک", + id: "machines_light", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "machines_sheavy", + header: "ماشین آلات نیمه سنگین", + id: "machines_sheavy", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "machines_heavy", + header: "ماشین آلات سنگین", + id: "machines_heavy", + enableColumnFilter: false, + enableSorting: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + header: "تصاویر", + id: "images", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue, row }) => { + return ( + + + + ); + }, + }, + ], + }, + { + header: "موقعیت", // Location + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue, row }) => { + return ( + + + + ); + }, + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "created_at", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + ]; + }, []); + + return ( + <> + + + + + ); +}; +export default TollHouseList; diff --git a/src/components/infrastructure/tollHouse/Toolbar.jsx b/src/components/infrastructure/tollHouse/Toolbar.jsx new file mode 100644 index 0000000..d8ada27 --- /dev/null +++ b/src/components/infrastructure/tollHouse/Toolbar.jsx @@ -0,0 +1,10 @@ +import { Box } from "@mui/material"; +import CreateTollHouse from "./Form/CreateTollHouse"; +import { usePermissions } from "@/lib/hooks/usePermissions"; + +const Toolbar = ({ table, filterData, mutate }) => { + const { data: userPermissions } = usePermissions(); + const hasAddPermission = userPermissions.some((item) => ["add-tollhouse", "add-tollhouse-province"].includes(item)); + return {hasAddPermission && }; +}; +export default Toolbar; diff --git a/src/components/infrastructure/tollHouse/index.jsx b/src/components/infrastructure/tollHouse/index.jsx new file mode 100644 index 0000000..c1d4172 --- /dev/null +++ b/src/components/infrastructure/tollHouse/index.jsx @@ -0,0 +1,14 @@ +"use client"; +import PageTitle from "@/core/components/PageTitle"; +import { Stack } from "@mui/material"; +import TollHouseList from "./TollHouseList"; + +const TollHousePage = () => { + return ( + + + + + ); +}; +export default TollHousePage; diff --git a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx index fd8e97e..c61dbe3 100644 --- a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx +++ b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarListItems.jsx @@ -35,7 +35,11 @@ const SidebarListItems = ({ menuItem, dispatch }) => { {menuItem.badges && ( {menuItem.badges.map((badge, i) => ( - + ))} )} diff --git a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx index 5a4f1e5..ba0cd5f 100644 --- a/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx +++ b/src/components/layouts/dashboard/headerWithSidebar/Sidebar/SidebarSubitems.jsx @@ -33,7 +33,14 @@ const SidebarSubitems = ({ subitem, dispatch }) => { {subitem.badges && ( {subitem.badges.map((badge, i) => ( - + ))} )} diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js index efad5a0..ef2d81a 100644 --- a/src/core/components/DataTable/Main.js +++ b/src/core/components/DataTable/Main.js @@ -113,7 +113,7 @@ const DataTable_Main = (props) => { ...initialStateProps, }, state: { - showProgressBars: isValidating, + showProgressBars: isValidating || props.loading, columnVisibility: flattenHideData, sorting: sortData, pagination, @@ -171,6 +171,8 @@ const DataTable_Main = (props) => { page_name={page_name} table_name={table_name} special_data={props.data} + special_filter={specialFilter} + setFilterData={props.setFilterData} /> ); }; diff --git a/src/core/components/DataTable/filter/FilterBody.jsx b/src/core/components/DataTable/filter/FilterBody.jsx index 461eb01..67b34fd 100644 --- a/src/core/components/DataTable/filter/FilterBody.jsx +++ b/src/core/components/DataTable/filter/FilterBody.jsx @@ -58,7 +58,7 @@ function FilterBody({ columns, drawerState, setDrawerState }) { } = useForm({ defaultValues: filterData, resolver: yupResolver(validationSchema), - mode: "onBlur", + mode: "all", }); const onSubmit = (data) => { diff --git a/src/core/components/DataTable/filter/FilterCustom.jsx b/src/core/components/DataTable/filter/FilterCustom.jsx new file mode 100644 index 0000000..f8d43f3 --- /dev/null +++ b/src/core/components/DataTable/filter/FilterCustom.jsx @@ -0,0 +1,32 @@ +import { useCallback, useState } from "react"; +import FilterButton from "./FilterButton"; +import { Box, Drawer } from "@mui/material"; +import FilterDrawer from "../../FilterDrawer"; + +const FilterCustom = ({ filterData, setFilterData }) => { + const [open, setOpen] = useState(false); + + const closeDrawer = useCallback(() => setOpen(false), []); + + return ( + <> + + setOpen(false)} + sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%", zIndex: "1300" }} + > + + {open && ( + + )} + + + + ); +}; +export default FilterCustom; diff --git a/src/core/components/DataTable/table/Paper.js b/src/core/components/DataTable/table/Paper.js index 14665a3..9ce08fc 100644 --- a/src/core/components/DataTable/table/Paper.js +++ b/src/core/components/DataTable/table/Paper.js @@ -14,6 +14,7 @@ const DataTable_Paper = ({ need_filter, table_url, table_title, + setFilterData, ...rest }) => { const { @@ -86,6 +87,7 @@ const DataTable_Paper = ({ page_name={page_name} table_name={table_name} table_title={table_title} + setFilterData={setFilterData} {...paperProps} /> ))} diff --git a/src/core/components/DataTable/toolbar/TopToolbar.js b/src/core/components/DataTable/toolbar/TopToolbar.js index 2c322ff..ea0c6d0 100644 --- a/src/core/components/DataTable/toolbar/TopToolbar.js +++ b/src/core/components/DataTable/toolbar/TopToolbar.js @@ -6,6 +6,7 @@ import FilterColumn from "@/core/components/DataTable/filter"; import UpdateTable from "@/core/components/DataTable/update/UpdateTable"; import ResetStorage from "@/core/components/DataTable/reset/ResetStorage"; import HideColumn from "@/core/components/DataTable/hide"; +import FilterCustom from "../filter/FilterCustom"; const DataTable_TopToolbar = ({ mutate, @@ -18,6 +19,8 @@ const DataTable_TopToolbar = ({ table_name, table_title, special_data, + special_filter, + setFilterData, }) => { const { getState, @@ -118,6 +121,9 @@ const DataTable_TopToolbar = ({ table_name={table_name} /> )} + {special_filter && setFilterData && ( + + )} )} 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..a231b71 --- /dev/null +++ b/src/core/components/FilterDrawer/index.jsx @@ -0,0 +1,153 @@ +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: "all", + }); + + const onSubmit = (data) => { + setFilterData(data); + closeDrawer(); + }; + + return ( + <> + + + + + فیلتر + + + + + + + +
+ + {Object.values(defaultValues).map( + (item) => + item.enable && + (item.dependencyId ? ( + + ) : ( + + )) + )} + + + + + +
+
+ + ); +}; +export default FilterDrawer; diff --git a/src/core/components/FlyToLocation.jsx b/src/core/components/FlyToLocation.jsx new file mode 100644 index 0000000..0f63772 --- /dev/null +++ b/src/core/components/FlyToLocation.jsx @@ -0,0 +1,32 @@ +import { useEffect, useState } from "react"; +import { useMap } from "react-leaflet"; + +const FlyToUserLocation = ({ Component }) => { + const [userPosition, setUserPosition] = useState(null); + const map = useMap(); + + const handleLocateUser = () => { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + (position) => { + setUserPosition([position.coords.latitude, position.coords.longitude]); + }, + (error) => { + console.error("Location error:", error); + } + ); + } else { + alert("مرورگر شما از موقعیت‌یاب پشتیبانی نمی‌کند."); + } + }; + + useEffect(() => { + if (userPosition) { + map.flyTo(userPosition, 16); + } + }, [userPosition, map]); + + return ; +}; + +export default FlyToUserLocation; diff --git a/src/core/components/MapInfoOneMarker.jsx b/src/core/components/MapInfoOneMarker.jsx index a68d4bf..8a887da 100644 --- a/src/core/components/MapInfoOneMarker.jsx +++ b/src/core/components/MapInfoOneMarker.jsx @@ -1,11 +1,13 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Marker, useMapEvents } from "react-leaflet"; -import "leaflet/dist/leaflet.css"; -import L from "leaflet"; -import { Alert, Box, Button, Stack, Typography, Zoom } from "@mui/material"; -import dynamic from "next/dynamic"; -import MapLoading from "@/core/components/MapLayer/Loading"; import HereIcon from "@/assets/images/examine_marker_active.png"; +import MapLoading from "@/core/components/MapLayer/Loading"; +import { MyLocation } from "@mui/icons-material"; +import { Alert, Box, Button, Stack, Typography, Zoom } from "@mui/material"; +import L from "leaflet"; +import "leaflet/dist/leaflet.css"; +import dynamic from "next/dynamic"; +import { useEffect, useRef, useState } from "react"; +import { Marker, useMapEvents } from "react-leaflet"; +import FlyToUserLocation from "./FlyToLocation"; const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { loading: () => , @@ -113,20 +115,36 @@ const MapInteraction = ({ setValue, startLat, startLng, title }) => {
- {isMarkerLocked && ( - - - - )} + + ( + + )} + /> +
); }; diff --git a/src/core/components/MapInfoTwoMarker.jsx b/src/core/components/MapInfoTwoMarker.jsx index 9b114d3..668635c 100644 --- a/src/core/components/MapInfoTwoMarker.jsx +++ b/src/core/components/MapInfoTwoMarker.jsx @@ -7,6 +7,8 @@ import dynamic from "next/dynamic"; import MapLoading from "@/core/components/MapLayer/Loading"; import EndIcon from "@/assets/images/examine_marker_active.png"; import StartIcon from "@/assets/images/examine_marker.png"; +import FlyToUserLocation from "./FlyToLocation"; +import { MyLocation } from "@mui/icons-material"; const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { loading: () => , @@ -137,12 +139,12 @@ const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => { - @@ -197,16 +199,38 @@ const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => { - )} + + ( + + )} + /> + ); }; diff --git a/src/core/utils/DataTableFilterDataStructure.js b/src/core/utils/DataTableFilterDataStructure.js index c88bdfc..43f1144 100644 --- a/src/core/utils/DataTableFilterDataStructure.js +++ b/src/core/utils/DataTableFilterDataStructure.js @@ -1,9 +1,10 @@ export default function DataTableFilterDataStructure(filterData, isValueEmpty) { return Object.values(filterData) .filter((filter) => !isValueEmpty(filter.value)) - .map(({ filterMode, id, ...rest }) => ({ - ...rest, + .map(({ filterMode, id, datatype, value }) => ({ id: id.replace(/__/g, "."), fn: filterMode, + datatype, + value, })); } diff --git a/src/core/utils/headerMenu.js b/src/core/utils/headerMenu.js index a3ac3c9..dc58a18 100644 --- a/src/core/utils/headerMenu.js +++ b/src/core/utils/headerMenu.js @@ -37,7 +37,7 @@ export const headerMenu = [ }, { title: "راهدارخانه ها", - href: api + "/v2/rahdari_points", + href: "/infrastructure/toll-house", }, ], [ diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index 3465345..a4e1d31 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -95,7 +95,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: ["roadItem.total"], + badges: [{ key: "roadItem.total" }], Subitems: [ { id: "roadItemManagmentSupervisor", @@ -103,7 +103,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: ["roadItem.supervise_cnt"], + badges: [{ key: "roadItem.supervise_cnt" }], Subitems: [ { id: "roadItemManagmentSupervisorCartable", @@ -123,7 +123,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: ["roadItem.operation_cnt"], + badges: [{ key: "roadItem.operation_cnt" }], Subitems: [ { id: "roadItemManagmentOparationCartable", @@ -290,7 +290,10 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: ["safetyAndPrivacy.step_one", "safetyAndPrivacy.step_two"], + badges: [ + { key: "safetyAndPrivacy.step_one", color: "warning" }, + { key: "safetyAndPrivacy.step_two", color: "error" }, + ], Subitems: [ { id: "safetyAndPrivacyManagmentOparation", @@ -298,6 +301,10 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, + badges: [ + { key: "safetyAndPrivacy.step_one", color: "warning" }, + { key: "safetyAndPrivacy.step_two", color: "error" }, + ], Subitems: [ { id: "safetyAndPrivacyManagmentOparationCartable", @@ -316,7 +323,7 @@ export const pageMenu = [ id: "safetyAndPrivacyManagmentMap", label: "پراکندگی بر روی نقشه", type: "page", - route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=safety_and_privacy", + route: "/dashboard/road-safety/reports/map", icon: , permissions: [ "show-safety-and-privacy-operator-cartable", @@ -328,7 +335,7 @@ export const pageMenu = [ id: "safetyAndPrivacyManagmentReport", label: "گزارش ها", type: "page", - route: "/dashboard/road-safety/report", + route: "/dashboard/road-safety/reports", icon: , permissions: [ "show-safety-and-privacy-operator-cartable", @@ -344,7 +351,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: ["roadObserved.total"], + badges: [{ key: "roadObserved.total" }], Subitems: [ { id: "roadObservationsManagmentSupervisor", @@ -352,7 +359,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: ["roadObserved.supervise_cnt", "roadObserved.complaint_cnt"], + badges: [{ key: "roadObserved.supervise_cnt" }, { key: "roadObserved.complaint_cnt", color: "info" }], Subitems: [ { id: "roadObservationsManagmentSupervisorCartable", @@ -369,7 +376,7 @@ export const pageMenu = [ type: "menu", icon: , hasSubitems: true, - badges: ["roadObserved.operation_cnt", "roadObserved.complaint_cnt"], + badges: [{ key: "roadObserved.operation_cnt" }, { key: "roadObserved.complaint_cnt", color: "info" }], Subitems: [ { id: "roadObservationsManagmentOparationCartable", @@ -454,14 +461,6 @@ export const pageMenu = [ }, ], }, - { - id: "damageItems", - label: "آیتم خسارات", - type: "page", - route: "/dashboard/damage-items", - icon: , - permissions: ["manage-damage"], - }, { id: "damagesManagmentReport", label: "گزارش ها", @@ -489,27 +488,44 @@ export const pageMenu = [ permissions: ["azmayesh-type-management"], }, { - id: "permissionItem", - label: "سطح دسترسی ها", - type: "page", - route: "/dashboard/permissions", - icon: , - permissions: ["manage-permissions"], - }, - { - id: "userActivities", - label: "انواع فعالیت کاربران", - type: "page", - route: "/dashboard/user-activities", - icon: , - permissions: ["manage-log-list"], - }, - { - id: "CarDetails", - label: "اطلاعات خودرو", - type: "page", - route: "/dashboard/car-details", - icon: , - permissions: [""], + id: "adminManagement", + label: "مدیریت سامانه", + type: "menu", + icon: , + hasSubitems: true, + Subitems: [ + { + id: "permissionItem", + label: "سطح دسترسی ها", + type: "page", + route: "/dashboard/permissions", + icon: , + permissions: ["manage-permissions"], + }, + { + id: "userActivities", + label: "انواع فعالیت کاربران", + type: "page", + route: "/dashboard/user-activities", + icon: , + permissions: ["manage-log-list"], + }, + { + id: "damageItems", + label: "آیتم خسارات وارده بر ابنیه فنی", + type: "page", + route: "/dashboard/damage-items", + icon: , + permissions: ["manage-damage"], + }, + { + id: "CarDetails", + label: "اطلاعات خودرو", + type: "page", + route: "/dashboard/car-details", + icon: , + permissions: [""], + }, + ], }, ]; diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 4953100..5448448 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -9,7 +9,7 @@ export const GET_PERMISSIONS_ROUTE = api + "/webapi/user/get-permission"; export const GET_SIDEBAR_BADGE_ROUTE = api + "/api/v3/notifications"; export const REFER_ADMIN_PROVINCE = "/v3/api/fake-submit"; export const REFER_ADMIN_CITY = "/v3/api/fake-submit"; -export const GET_CITY_LISTS = "/v3/api/fake-cities"; +export const GET_CITY_LISTS = api + "/public/contents/provinces"; export const GET_PROVINCE_LISTS = api + "/public/contents/provinces"; export const GET_PREV_STATE_OPINION = "/v3/api/fake-prev-state-opinion"; export const SUBMIT_ROAD_SAFETY_FORM = "/v3/api/fake-submit"; @@ -140,6 +140,10 @@ export const UPDATE_SAFETY_AND_PRIVACY = api + "/api/v3/safety_and_privacy"; export const EXPORT_OPERATOR_ROAD_SAFETY = api + "/api/v3/safety_and_privacy/excel_report"; export const GET_ROAD_SAFETY_PROVINCE_ACTIVITY_REPORT = api + "/api/v3/safety_and_privacy_report/country_activity"; export const GET_ROAD_SAFETY_CITY_ACTIVITY_REPORT = api + "/api/v3/safety_and_privacy_report/province_activity"; +export const GET_ROAD_SAFETY_REPORT_MAP = api + "/api/v3/safety_and_privacy/map"; +export const GET_ROAD_SAFETY_DETAILS = api + "/api/v3/safety_and_privacy"; +export const EXPORT_COUNTRY_ROAD_SAFETY_REPORT = api + "/api/v3/safety_and_privacy_report/country_excel_activity"; +export const EXPORT_PROVINCE_ROAD_SAFETY_REPORT = api + "/api/v3/safety_and_privacy_report/province_excel_activity"; // permission table export const GET_PERMISSION_TABLE_LIST = api + "/api/v3/permissions"; @@ -152,3 +156,11 @@ export const GET_USER_ACTIVITIES_LIST = api + "/api/v3/log_list"; export const CREATE_USER_ACTIVITY = api + "/api/v3/log_list"; export const UPDATE_USER_ACTIVITY = api + "/api/v3/log_list"; export const DELETE_USER_ACTIVITIES = api + "/api/v3/log_list"; + +// toll-house +export const GET_TOLL_HOUSE_LIST = api + "/api/v3/road_maintenance_station"; +export const CREATE_TOLL_HOUSE = api + "/api/v3/road_maintenance_station"; +export const GET_TOLL_HOUSE_DETAILS = api + "/api/v3/road_maintenance_station"; +export const DELETE_TOLL_HOUSE_ITEM = api + "/api/v3/road_maintenance_station"; +export const UPDATE_TOLL_HOUSE_ITEM = api + "/api/v3/road_maintenance_station"; +export const GET_TOLL_HOUSE_IMAGES = api + "/api/v3/road_maintenance_station/images"; diff --git a/src/lib/hooks/useCities.js b/src/lib/hooks/useCities.js index 6ac535d..dfaadbb 100644 --- a/src/lib/hooks/useCities.js +++ b/src/lib/hooks/useCities.js @@ -1,30 +1,59 @@ -import { useState, useEffect } from "react"; +import { useEffect, useReducer } from "react"; import { GET_CITY_LISTS } from "@/core/utils/routes"; import useRequest from "@/lib/hooks/useRequest"; +const ACTIONS = { + LOADING: "loading", + SUCCESS: "success", + ERROR: "error", + RESET: "reset", +}; -const useCities = () => { +const initialState = { + data: [], + loading: true, + error: null, +}; +function reducer(state, action) { + switch (action.type) { + case ACTIONS.LOADING: + return { ...state, loading: true, error: null }; + case ACTIONS.SUCCESS: + return { ...state, loading: false, data: action.payload }; + case ACTIONS.ERROR: + return { ...state, loading: false, error: action.payload }; + case ACTIONS.RESET: + return { ...initialState, loading: false }; + default: + return state; + } +} +const useCities = (id) => { const requestServer = useRequest(); - const [cities, setCities] = useState([]); - const [loadingCities, setLoadingCities] = useState(true); - const [errorCities, setErrorCities] = useState(null); + const [state, dispatch] = useReducer(reducer, initialState); useEffect(() => { - const fetchCities = async () => { + if (!id) { + dispatch({ type: ACTIONS.RESET }); + return; + } + const fetchCityLists = async () => { + dispatch({ type: ACTIONS.LOADING }); try { - const response = await requestServer(`${GET_CITY_LISTS}`); - setCities(response.data.data); - setLoadingCities(false); - } catch (e) { - console.error("Error fetching cities:", e); - setErrorCities(e); - setLoadingCities(false); + const response = await requestServer(`${GET_CITY_LISTS}/${id}`); + dispatch({ type: ACTIONS.SUCCESS, payload: response.data.data }); + } catch (error) { + dispatch({ type: ACTIONS.ERROR, payload: error }); } }; - fetchCities(); - }, []); + fetchCityLists(); + }, [id]); - return { cities, loadingCities, errorCities }; + return { + cityList: state.data, + loadingCityList: state.loading, + errorCityList: state.error, + }; }; export default useCities; diff --git a/src/lib/hooks/useDamageItemList.js b/src/lib/hooks/useDamageItemList.js index a353627..8349f06 100644 --- a/src/lib/hooks/useDamageItemList.js +++ b/src/lib/hooks/useDamageItemList.js @@ -9,7 +9,7 @@ const useDamageItemList = () => { const [errorDamageItemList, setErrorDamageItemList] = useState(null); useEffect(() => { - const fetchCities = async () => { + const fetchDamageItem = async () => { try { const response = await requestServer(`${GET_DAMAGE_ITEM_LIST}`); setDamageItemList(response.data.data); @@ -20,7 +20,7 @@ const useDamageItemList = () => { } }; - fetchCities(); + fetchDamageItem(); }, []); return { damageItemList, loadingDamageItemList, errorDamageItemList }; diff --git a/src/lib/hooks/usePrevStateopinion.js b/src/lib/hooks/usePrevStateopinion.js index 9104366..0d5ce73 100644 --- a/src/lib/hooks/usePrevStateopinion.js +++ b/src/lib/hooks/usePrevStateopinion.js @@ -10,19 +10,19 @@ const usePrevStateOpinion = () => { const [errorMessage, setErrorMessage] = useState(null); useEffect(() => { - const fetchCities = async () => { + const fetchStateOpnion = async () => { try { const response = await requestServer(`${GET_PREV_STATE_OPINION}`); setMessage(response.data.message); setLoadingMessage(false); } catch (e) { - console.error("Error fetching cities:", e); + console.error("Error fetching state:", e); setErrorMessage(e); setLoadingMessage(false); } }; - fetchCities(); + fetchStateOpnion(); }, []); return { message, loadingMessage, errorMessage }; diff --git a/src/lib/hooks/useProvince.js b/src/lib/hooks/useProvince.js index a36df7d..a670efd 100644 --- a/src/lib/hooks/useProvince.js +++ b/src/lib/hooks/useProvince.js @@ -9,7 +9,7 @@ const useProvinces = () => { const [errorProvinces, setErrorProvinces] = useState(null); useEffect(() => { - const fetchCities = async () => { + const fetchProvinces = async () => { try { const response = await requestServer(`${GET_PROVINCE_LISTS}`); setProvinces(response.data.data); @@ -20,7 +20,7 @@ const useProvinces = () => { } }; - fetchCities(); + fetchProvinces(); }, []); return { provinces, loadingProvinces, errorProvinces }; diff --git a/src/lib/hooks/useRequest.js b/src/lib/hooks/useRequest.js index 8e6d121..0587778 100644 --- a/src/lib/hooks/useRequest.js +++ b/src/lib/hooks/useRequest.js @@ -2,6 +2,7 @@ import { errorResponse } from "@/core/utils/errorResponse"; import { successRequest } from "@/core/utils/successRequest"; import axios from "axios"; import { useAuth } from "../contexts/auth"; +import { useSidebarBadge } from "./useSidebarBadge"; const defaultOptions = { data: {}, @@ -11,13 +12,22 @@ const defaultOptions = { notificationShow: true, notificationSuccess: false, notificationFailed: true, + hasSidebarUpdate: false, }; const useRequest = (initOptions) => { + const { mutate: sidebarUpdate } = useSidebarBadge(); const { logout } = useAuth(); const _options = Object.assign({}, defaultOptions, initOptions); + /** + * Performs an HTTP request. + * @param {string} url - The URL to send the request to. + * @param {'get' | 'post' | 'put' | 'delete'} [method='get'] - HTTP method. + * @param {object} [options] - Additional request options. + * @returns {Promise} - Axios response. + */ return async (url = "", method = "get", options) => { const mergedOptions = Object.assign({}, _options, options); try { @@ -28,6 +38,9 @@ const useRequest = (initOptions) => { withCredentials: true, ...mergedOptions.requestOptions, }); + if (mergedOptions.hasSidebarUpdate) { + if (method === "post" || method === "put" || method === "delete") sidebarUpdate(); + } successRequest(mergedOptions.notificationShow && mergedOptions.notificationSuccess, "request_data"); return response; } catch (error) { diff --git a/src/lib/hooks/useSidebarBadge.js b/src/lib/hooks/useSidebarBadge.js index 994cf21..e74e358 100644 --- a/src/lib/hooks/useSidebarBadge.js +++ b/src/lib/hooks/useSidebarBadge.js @@ -1,18 +1,16 @@ import { GET_SIDEBAR_BADGE_ROUTE } from "@/core/utils/routes"; +import axios from "axios"; import useSWR from "swr"; -import useRequest from "./useRequest"; export const useSidebarBadge = () => { - const request = useRequest(); - const fetcher = async (url) => { try { - const response = await request(url); + const response = await axios({ url, method: "get", withCredentials: true }); return response.data.data; } catch (error) { throw new Error(); } }; - return useSWR(GET_SIDEBAR_BADGE_ROUTE, fetcher, { keepPreviousData: true, dedupingInterval: 10000 }); + return useSWR(GET_SIDEBAR_BADGE_ROUTE, fetcher, { keepPreviousData: true, dedupingInterval: 30000 }); };