From 4b25071e4190aabdce3100bd55c96776cb8bebfa Mon Sep 17 00:00:00 2001 From: Amin Ghasempoor Date: Mon, 23 Dec 2024 08:20:46 +0000 Subject: [PATCH] Feature/faaliyat rozane cartable --- next.config.mjs | 8 + package.json | 1 + .../dashboard/road-items/operator/page.js | 11 + .../dashboard/road-items/supervisor/page.js | 13 + src/app/(withAuth)/layout.js | 4 +- src/app/api/fake-road-items/operator/route.js | 84 ++++++ .../api/fake-road-items/supervisor/route.js | 84 ++++++ .../operator}/route.js | 0 .../supervisor}/route.js | 0 .../ShowLocation/ShowLocationMarker.jsx | 104 -------- .../dashboard/azmayesh/ShowLocation/index.jsx | 4 +- .../Create/Forms/CreateFormContent.jsx | 225 +++++++++++++++++ .../Actions/Create/Forms/GetItemInfo.jsx | 111 ++++++++ .../Actions/Create/Forms/GetItemsForm.jsx | 64 +++++ .../Actions/Create/Forms/GetSubItemsForm.jsx | 53 ++++ .../Actions/Create/Forms/ImageUpload.jsx | 131 ++++++++++ .../Create/Forms/PreviousStatesInfo.jsx | 43 ++++ .../operator/Actions/Create/Forms/index.jsx | 12 + .../operator/Actions/Create/index.jsx | 38 +++ .../roadItems/operator/ExcelPrint/index.jsx | 71 ++++++ .../roadItems/operator/OperatorList.jsx | 220 ++++++++++++++++ .../RowActions/EditForm/EditFormContent.jsx | 211 ++++++++++++++++ .../operator/RowActions/EditForm/index.jsx | 54 ++++ .../RowActions/ImageForm/ImageFormContent.jsx | 41 +++ .../operator/RowActions/ImageForm/index.jsx | 41 +++ .../LocationForm/LocationFormContent.jsx | 36 +++ .../RowActions/LocationForm/index.jsx | 38 +++ .../RahdaranForm/RahdaranContent.jsx | 48 ++++ .../RowActions/RahdaranForm/index.jsx | 38 +++ .../roadItems/operator/RowActions/index.jsx | 11 + .../dashboard/roadItems/operator/Toolbar.jsx | 13 + .../dashboard/roadItems/operator/index.jsx | 14 + .../roadItems/supervisor/ExcelPrint/index.jsx | 70 +++++ .../RowActions/ConfirmForm/ConfirmContent.jsx | 69 +++++ .../RowActions/ConfirmForm/index.jsx | 29 +++ .../RowActions/DeleteForm/DeleteContent.jsx | 42 +++ .../RowActions/DeleteForm/index.jsx | 33 +++ .../RowActions/ImageForm/ImageFormContent.jsx | 41 +++ .../supervisor/RowActions/ImageForm/index.jsx | 41 +++ .../LocationForm/LocationFormContent.jsx | 36 +++ .../RowActions/LocationForm/index.jsx | 38 +++ .../RahdaranForm/RahdaranContent.jsx | 48 ++++ .../RowActions/RahdaranForm/index.jsx | 38 +++ .../RowActions/RejectForm/RejectContent.jsx | 81 ++++++ .../RowActions/RejectForm/index.jsx | 29 +++ .../RowActions/RestoreForm/RestoreContent.jsx | 42 +++ .../RowActions/RestoreForm/index.jsx | 32 +++ .../roadItems/supervisor/RowActions/index.jsx | 27 ++ .../roadItems/supervisor/SupervisorList.jsx | 239 ++++++++++++++++++ .../roadItems/supervisor/Toolbar.jsx | 10 + .../dashboard/roadItems/supervisor/index.jsx | 14 + .../roadPatrols/operator/ExcelPrint/index.jsx | 4 +- .../roadPatrols/operator/OperatorList.jsx | 4 +- .../supervisor/ExcelPrint/index.jsx | 4 +- .../RowActions/DeleteForm/index.jsx | 17 +- .../roadPatrols/supervisor/SupervisorList.jsx | 4 +- src/core/components/CarCode.jsx | 56 ++-- .../DataTable/filter/FilterBody.jsx | 24 +- .../DataTable/filter/FilterBodyController.jsx | 25 ++ .../DataTable/reset/ResetStorage.jsx | 20 +- src/core/components/MapInfoOneMarker.jsx | 137 ++++++++++ src/core/components/MapInfoTwoMarker.jsx | 191 ++++++++++++++ src/core/components/MuiDatePicker.jsx | 107 ++++---- src/core/components/MuiTimePicker.jsx | 75 ++++++ src/core/components/RahdarCode.jsx | 90 +++++++ src/core/components/ShowLocationMarker.jsx | 181 +++++++++++++ src/core/components/Toasts/error.jsx | 29 +++ src/core/components/UploadSystem.jsx | 94 +++++++ src/core/utils/errorResponse.js | 4 + src/core/utils/pageMenu.js | 4 +- src/core/utils/routes.js | 26 +- src/lib/contexts/DataTable.js | 14 +- src/lib/hooks/useDataTable.js | 5 +- src/lib/hooks/useRoadItemGetISubtems.js | 29 +++ src/lib/hooks/useRoadItemGetItems.js | 29 +++ 75 files changed, 3722 insertions(+), 236 deletions(-) create mode 100644 src/app/(withAuth)/dashboard/road-items/operator/page.js create mode 100644 src/app/(withAuth)/dashboard/road-items/supervisor/page.js create mode 100644 src/app/api/fake-road-items/operator/route.js create mode 100644 src/app/api/fake-road-items/supervisor/route.js rename src/app/api/{fake-operator-list => fake-road-patrol/operator}/route.js (100%) rename src/app/api/{fake-supervisor-list => fake-road-patrol/supervisor}/route.js (100%) delete mode 100644 src/components/dashboard/azmayesh/ShowLocation/ShowLocationMarker.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/Actions/Create/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/OperatorList.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/LocationForm/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/RahdaranContent.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/RowActions/index.jsx create mode 100644 src/components/dashboard/roadItems/operator/Toolbar.jsx create mode 100644 src/components/dashboard/roadItems/operator/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/LocationFormContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/RahdaranContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/RowActions/index.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/SupervisorList.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/Toolbar.jsx create mode 100644 src/components/dashboard/roadItems/supervisor/index.jsx create mode 100644 src/core/components/DataTable/filter/FilterBodyController.jsx create mode 100644 src/core/components/MapInfoOneMarker.jsx create mode 100644 src/core/components/MapInfoTwoMarker.jsx create mode 100644 src/core/components/MuiTimePicker.jsx create mode 100644 src/core/components/RahdarCode.jsx create mode 100644 src/core/components/ShowLocationMarker.jsx create mode 100644 src/core/components/UploadSystem.jsx create mode 100644 src/lib/hooks/useRoadItemGetISubtems.js create mode 100644 src/lib/hooks/useRoadItemGetItems.js diff --git a/next.config.mjs b/next.config.mjs index 29212c9..7b0b855 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,6 +1,14 @@ /** @type {import('next').NextConfig} */ const nextConfig = { basePath: '/v3', + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'rms.witel.ir', + }, + ], + }, }; export default nextConfig; diff --git a/package.json b/package.json index 0d256e9..c5b2139 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "yup": "^1.4.0" }, "devDependencies": { + "@types/node": "22.10.1", "eslint": "^8", "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", diff --git a/src/app/(withAuth)/dashboard/road-items/operator/page.js b/src/app/(withAuth)/dashboard/road-items/operator/page.js new file mode 100644 index 0000000..9c3d8ca --- /dev/null +++ b/src/app/(withAuth)/dashboard/road-items/operator/page.js @@ -0,0 +1,11 @@ +import WithPermission from "@/core/middlewares/withPermission"; +import OperatorPage from "@/components/dashboard/roadItems/operator"; +const Page = () => { + return ( + + + + ); +}; + +export default Page; diff --git a/src/app/(withAuth)/dashboard/road-items/supervisor/page.js b/src/app/(withAuth)/dashboard/road-items/supervisor/page.js new file mode 100644 index 0000000..58c47d8 --- /dev/null +++ b/src/app/(withAuth)/dashboard/road-items/supervisor/page.js @@ -0,0 +1,13 @@ +import WithPermission from "@/core/middlewares/withPermission"; +import SupervisorPage from "@/components/dashboard/roadItems/supervisor"; +const Page = () => { + return ( + + + + ); +}; + +export default Page; diff --git a/src/app/(withAuth)/layout.js b/src/app/(withAuth)/layout.js index 35e60c5..62cbf6d 100644 --- a/src/app/(withAuth)/layout.js +++ b/src/app/(withAuth)/layout.js @@ -1,8 +1,8 @@ "use client"; -import WithAuthMiddleware from "@/core/middlewares/withAuth"; -import LoadingHardPage from "@/core/components/LoadingHardPage"; import { useAuth } from "@/lib/contexts/auth"; import { Typography, Stack } from "@mui/material"; +import WithAuthMiddleware from "@/core/middlewares/withAuth"; +import LoadingHardPage from "@/core/components/LoadingHardPage"; const Layout = ({ children }) => { const { isAuth, initAuthState, errorState } = useAuth(); diff --git a/src/app/api/fake-road-items/operator/route.js b/src/app/api/fake-road-items/operator/route.js new file mode 100644 index 0000000..49424bc --- /dev/null +++ b/src/app/api/fake-road-items/operator/route.js @@ -0,0 +1,84 @@ +const data = [ + { + id: 1, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 0, // Replace with actual status + car_id: 1, + person_id: [0, 1, 2], + description: "بررسی شد", // Replace with actual description + }, + { + id: 2, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 2, // Replace with actual status + car_id: 10, + person_id: [10, 11, 12], + description: "بررسی شد", // Replace with actual description + }, + { + id: 3, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, // Add URLs to images if needed + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 0, // Replace with actual status + car_id: 11, + person_id: [20, 15, 29], + description: "بررسی شد", // Replace with actual description + }, + { + id: 4, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, // Add URLs to images if needed + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 1, // Replace with actual status + car_id: 26, + person_id: [78, 18, 56], + description: "بررسی شد", // Replace with actual description + }, +]; + +export async function GET() { + return new Response(JSON.stringify({ data }), { + headers: { "Content-Type": "application/json" }, + }); +} diff --git a/src/app/api/fake-road-items/supervisor/route.js b/src/app/api/fake-road-items/supervisor/route.js new file mode 100644 index 0000000..f2ab2bc --- /dev/null +++ b/src/app/api/fake-road-items/supervisor/route.js @@ -0,0 +1,84 @@ +const data = [ + { + id: 1, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, // Add URLs to images if needed + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 0, // Replace with actual status + car_id: 1, + person_id: [0, 1, 2], + description: "بررسی شد", // Replace with actual description + }, + { + id: 2, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, // Add URLs to images if needed + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 2, // Replace with actual status + car_id: 10, + person_id: [10, 11, 12], + description: "بررسی شد", // Replace with actual description + }, + { + id: 3, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, // Add URLs to images if needed + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 0, // Replace with actual status + car_id: 11, + person_id: [20, 15, 29], + description: "بررسی شد", // Replace with actual description + }, + { + id: 4, + province: "تهران", + office: "اداره کل", + action_item: "بررسی خودرو", // Replace with the correct label + observed_subject: "تنظیم سطح بستر حریم", // Replace with the correct label + value: "1 کیلومتر", // Replace with actual value + images: { + before: "https://cdn-icons-png.flaticon.com/512/783/783154.png", + after: "https://cdn-icons-png.flaticon.com/512/1651/1651833.png", + }, // Add URLs to images if needed + location: [{ lat: 35.6892, lng: 51.389 }], // Add URLs to images if needed + register_date: "2024-11-03T11:01:14.000000Z", + start_date: "2024-11-02T11:48:22.000000Z", + status: 1, // Replace with actual status + car_id: 26, + person_id: [78, 18, 56], + description: "بررسی شد", // Replace with actual description + }, +]; + +export async function GET() { + return new Response(JSON.stringify({ data }), { + headers: { "Content-Type": "application/json" }, + }); +} diff --git a/src/app/api/fake-operator-list/route.js b/src/app/api/fake-road-patrol/operator/route.js similarity index 100% rename from src/app/api/fake-operator-list/route.js rename to src/app/api/fake-road-patrol/operator/route.js diff --git a/src/app/api/fake-supervisor-list/route.js b/src/app/api/fake-road-patrol/supervisor/route.js similarity index 100% rename from src/app/api/fake-supervisor-list/route.js rename to src/app/api/fake-road-patrol/supervisor/route.js diff --git a/src/components/dashboard/azmayesh/ShowLocation/ShowLocationMarker.jsx b/src/components/dashboard/azmayesh/ShowLocation/ShowLocationMarker.jsx deleted file mode 100644 index 20b44cb..0000000 --- a/src/components/dashboard/azmayesh/ShowLocation/ShowLocationMarker.jsx +++ /dev/null @@ -1,104 +0,0 @@ -"use client"; - -import { Marker, useMap } from "react-leaflet"; -import { useEffect } from "react"; -import L from "leaflet"; -import AzmayeshActiveIcon from "@/assets/images/examine_marker_active.png"; -import { Box, FormControl, InputAdornment, InputLabel, OutlinedInput, Stack, useMediaQuery } from "@mui/material"; -import VerifiedIcon from "@mui/icons-material/Verified"; -import { useTheme } from "@emotion/react"; - -const ShowLocationMarker = ({ lat, lng }) => { - const map = useMap(); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down("sm")); - - const defaultIconSize = [35, 35]; - const createCustomIcon = (size, iconUrl) => { - return L.icon({ - iconUrl: iconUrl, - iconSize: size, - iconAnchor: [size[0] / 2, size[1]], - popupAnchor: [0, -size[1]], - }); - }; - - useEffect(() => { - map.flyTo({ lat: lat, lng: lng }, 14); - }, []); - - return ( - <> - - - - - - طول جغرافیایی - - - - - } - label="طول جغرافیایی" - /> - - - - عرض جغرافیایی - - - - - } - label="عرض جغرافیایی" - /> - - - - - ); -}; - -export default ShowLocationMarker; diff --git a/src/components/dashboard/azmayesh/ShowLocation/index.jsx b/src/components/dashboard/azmayesh/ShowLocation/index.jsx index c260da7..3289871 100644 --- a/src/components/dashboard/azmayesh/ShowLocation/index.jsx +++ b/src/components/dashboard/azmayesh/ShowLocation/index.jsx @@ -5,7 +5,7 @@ import { Box, Button, Dialog, DialogActions, DialogContent, IconButton } from "@ import { useState } from "react"; import dynamic from "next/dynamic"; import MapLoading from "@/core/components/MapLayer/Loading"; -import ShowLocationMarker from "./ShowLocationMarker"; +import ShowLocationMarker from "@/core/components/ShowLocationMarker"; const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { loading: () => , @@ -36,7 +36,7 @@ const ShowLocation = ({ lat, lng }) => { - + diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx new file mode 100644 index 0000000..c05d6d8 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent.jsx @@ -0,0 +1,225 @@ +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { array, mixed, object, string } from "yup"; +import { useTheme } from "@emotion/react"; +import useRequest from "@/lib/hooks/useRequest"; +import { Box, Button, DialogActions, DialogContent, Tab, Tabs, useMediaQuery } from "@mui/material"; +import StyledForm from "@/core/components/StyledForm"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; +import FileCopyIcon from "@mui/icons-material/FileCopy"; +import InfoIcon from "@mui/icons-material/Info"; +import ExitToAppIcon from "@mui/icons-material/ExitToApp"; +import KeyboardDoubleArrowRightIcon from "@mui/icons-material/KeyboardDoubleArrowRight"; +import BeenhereIcon from "@mui/icons-material/Beenhere"; +import GetItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm"; +import GetSubItemsForm from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm"; +import GetItemInfo from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo"; +import { CREATE_ROAD_ITEMS } from "@/core/utils/routes"; + +function TabPanel(props) { + const { children, value, index } = props; + return ( + + ); +} + +const defaultValues = { + itemId: null, + itemSubId: null, + amount: "", + action_date: "", + start_date: "", + before_image: null, + after_image: null, + cmms_machine_id: null, + rahdaran_id: null, + start_point: "", + end_point: "", +}; +const validationSchema = object({ + itemId: string().required("نوع آیتم را مشخص کنید!"), + itemSubId: string().required("موضوع مشاهده شده را مشخص کنید!"), + amount: string().required("وارد کردن مقدار الزامیست!"), + cmms_machine_id: object().required("وارد کردن کد خودرو الزامیست!"), + rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), // بررسی حداقل یک آیتم, + action_date: string().required("لطفا زمان فعالیت را انتخاب کنید!"), + start_date: string().required("لطفاً تاریخ شروع فعالیت را انتخاب کنید!"), + before_image: mixed() + .nullable() + .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) { + const { subItemsList } = this.options.context; + const needsImage = subItemsList?.needs_image === 1; + if (needsImage) { + return !!value; + } + return true; + }), + after_image: mixed() + .nullable() + .test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) { + const { subItemsList } = this.options.context; + const needsImage = subItemsList?.needs_image === 1; + if (needsImage) { + return !!value; + } + return true; + }), + start_point: mixed() + .test("start-point-required", "لطفاً نقطه شروع را مشخص کنید!", function (value) { + return !!value; // چک می‌کند که مقدار موجود است + }) + .required("لطفاً نقطه شروع را مشخص کنید!"), + end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) { + const { subItemsList } = this.options.context; + const needsEndPoint = subItemsList?.needs_end_point === 1; + if (needsEndPoint) { + return !!value; // چک می‌کند که مقدار موجود است + } + return true; + }), +}); + +const CreateFormContent = ({ setOpen, mutate, rowId }) => { + const [tabState, setTabState] = useState(0); + const [itemsList, setItemsList] = useState(); + const [subItemsList, setSubItemsList] = useState([]); + const theme = useTheme(); + const requestServer = useRequest({ notification: { success: true } }); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const handleClose = () => { + setOpen(false); + }; + + const handleChangeTab = (event, newValue) => { + setTabState(newValue); + }; + + const handlePrev = () => { + if (tabState === 0) { + handleClose(); + } else { + setTabState(tabState - 1); + } + }; + const { + control, + watch, + getValues, + register, + handleSubmit, + setValue, + formState: { errors, isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onBlur", + context: { subItemsList }, + }); + + const onSubmit = async (data) => { + console.log(data); + let endPoint; + let startPoint = `${data.start_point.lat},${data.start_point.lng}`; + data.end_point !== "" && (endPoint = `${data.end_point.lat},${data.end_point.lng}`); + const formData = new FormData(); + data.rahdaran_id.map((rahdar, index) => formData.append(`rahdaran_id[${index}]`, rahdar.id)); + formData.append("item_id", data.itemId); + formData.append("sub_item_id", data.itemSubId); + formData.append("amount", data.amount); + formData.append("activity_time", data.action_date); + formData.append("activity_date", data.start_date); + formData.append("cmms_machine_id", data.cmms_machine_id.id); + data.before_image !== null && formData.append("before_image", data.before_image); + data.after_image !== null && formData.append("after_image", data.after_image); + data.start_point !== "" && formData.append("start_point", startPoint); + data.end_point !== "" && formData.append("end_point", endPoint); + requestServer(CREATE_ROAD_ITEMS, "post", { + data: formData, + }) + .then((response) => { + mutate(); + setOpen(false); + }) + .catch((error) => {}); + }; + return ( + + + } label="انتخاب آیتم" /> + } label="انتخاب موضوع مشاهده شده" /> + } label="اطلاعات فعالیت" /> + + + + + + + + + + + + + + + + + {tabState === 2 && ( + + )} + + + ); +}; +export default CreateFormContent; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx new file mode 100644 index 0000000..54885c1 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemInfo.jsx @@ -0,0 +1,111 @@ +import { Stack, TextField, Grid } from "@mui/material"; +import ImageUpload from "./ImageUpload"; +import MuiDatePicker from "@/core/components/MuiDatePicker"; +import MuiTimePicker from "@/core/components/MuiTimePicker"; +import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import { Controller } from "react-hook-form"; +import CarCode from "@/core/components/CarCode"; +import RahdarCode from "@/core/components/RahdarCode"; +import PreviousStatesInfo from "./PreviousStatesInfo"; + +const GetItemInfo = ({ register, itemsList, subItemsList, control, setValue, errors, watch, getValues }) => { + return ( + + + + + + { + if (!isNaN(event.target.value)) { + setValue("amount", event.target.value); + } else { + setValue("amount", watch("amount")); + } + }} + helperText={errors.amount ? errors.amount.message : null} + variant="outlined" + fullWidth + /> + + + { + return ( + field.onChange(value || null)} + error={error} + /> + ); + }} + name={"cmms_machine_id"} + /> + + + { + return ( + field.onChange(value || [])} + error={error} + /> + ); + }} + name={"rahdaran_id"} + /> + + + {subItemsList.needs_image ? ( + + ) : null} + + + + + + + + + + + + + {subItemsList?.needs_end_point === 1 ? ( + + ) : ( + + )} + + + ); +}; +export default GetItemInfo; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm.jsx new file mode 100644 index 0000000..0396b71 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetItemsForm.jsx @@ -0,0 +1,64 @@ +import React from "react"; +import { Card, CardActionArea, Grid, LinearProgress, Typography } from "@mui/material"; +import Image from "next/image"; +import useRoadItemGetItems from "@/lib/hooks/useRoadItemGetItems"; + +const GetItemsForm = ({ setValue, setItemsList, tabState, setTabState }) => { + const { itemsList, loadingItemsList, errorItemsList } = useRoadItemGetItems(); + + return ( + <> + {errorItemsList ? ( + + خطا در دریافت لیست آیتم ها + + ) : loadingItemsList ? ( + + ) : ( + + {itemsList.map((item) => ( + + { + setValue("itemId", item.id); + setItemsList(item.name); + setTabState(tabState + 1); + }} + > + + {"icon"} + {item.name} + + + + ))} + + )} + + ); +}; +export default GetItemsForm; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm.jsx new file mode 100644 index 0000000..8b36594 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/GetSubItemsForm.jsx @@ -0,0 +1,53 @@ +import React from "react"; +import { Card, CardActionArea, Grid, LinearProgress, Typography } from "@mui/material"; +import useRoadItemGetSubItems from "@/lib/hooks/useRoadItemGetISubtems"; + +const GetSubItemsForm = ({ setValue, watch, setSubItemsList, tabState, setTabState }) => { + const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetSubItems(watch("itemId")); + + return ( + <> + {errorSubItemsList ? ( + + خطا در دریافت موضوع مشاهده شده + + ) : loadingSubItemsList ? ( + + ) : ( + + {subItemsList.map((item) => ( + + { + setValue("itemSubId", item.sub_item); + setSubItemsList(item); + setTabState(tabState + 1); + }} + > + + {item.name} + + + + ))} + + )} + + ); +}; +export default GetSubItemsForm; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx new file mode 100644 index 0000000..3efd1d9 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload.jsx @@ -0,0 +1,131 @@ +import { FormControl, FormHelperText, Grid } from "@mui/material"; +import UploadSystem from "@/core/components/UploadSystem"; +import { Controller } from "react-hook-form"; +import React, { useEffect, useState } from "react"; + +const ImageUpload = ({ control, setValue, errors, getValues, beforeImage = null, afterImage = null }) => { + const [beforeImg, setBeforeImg] = useState(beforeImage ? beforeImage : null); + const [beforeFileType, setBeforeFileType] = useState(beforeImage ? "image/" : null); + const [beforeFileName, setBeforeFileName] = useState(null); + const [showBeforeImage, setShowBeforeImage] = useState(!beforeImage); + + const [afterImg, setAfterImg] = useState(afterImage ? afterImage : null); + const [afterFileType, setAfterFileType] = useState(afterImage ? "image/" : null); + const [afterFileName, setAfterFileName] = useState(null); + const [showAfterImage, setShowAfterImage] = useState(!afterImage); + + useEffect(() => { + if (getValues("before_image")) { + setShowBeforeImage(false); + setBeforeImg(getValues("before_image")); + setBeforeFileType("image/"); + } + if (getValues("after_image")) { + setShowAfterImage(false); + setAfterImg(getValues("after_image")); + setAfterFileType("image/"); + } + }, [getValues]); + + const handleBeforeFileChange = (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); + setValue("before_image", uploadedFile); + setShowBeforeImage(false); + } + }; + const handleAfterFileChange = (event) => { + const uploadedFile = event.target?.files?.[0]; + if (uploadedFile) { + const fileType = event.target?.files?.[0].type; + const fileName = event.target?.files?.[0].name; + setAfterImg(URL.createObjectURL(uploadedFile)); + setAfterFileType(fileType); + setAfterFileName(fileName); + setValue("after_image", uploadedFile); + setShowAfterImage(false); + } + }; + return ( + + + { + return ( + + + عکس قبل از اقدام + + + + {errors.before_image ? errors.before_image.message : null} + + + ); + }} + /> + + + { + return ( + + + عکس بعد از اقدام + + + + {errors.after_image ? errors.after_image.message : null} + + + ); + }} + /> + + + ); +}; +export default ImageUpload; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx new file mode 100644 index 0000000..6f3cc86 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/PreviousStatesInfo.jsx @@ -0,0 +1,43 @@ +import { Box, Chip, Divider, Grid, Typography } from "@mui/material"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; +import FileCopyIcon from "@mui/icons-material/FileCopy"; + +const PreviousStatesInfo = ({ itemsList, subItemsList }) => { + return ( + + + + } + label={ + + آیتم انتخاب ‌شده + + } + /> + + + + {itemsList || "هیچ آیتمی انتخاب نشده است"} + + + + + } + label={ + + موضوع مشاهده‌شده + + } + /> + + + + {subItemsList?.name || "هیچ موضوعی انتخاب نشده است"} + + + + ); +}; +export default PreviousStatesInfo; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx new file mode 100644 index 0000000..52c2d42 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/Forms/index.jsx @@ -0,0 +1,12 @@ +"use client"; +import { Dialog } from "@mui/material"; +import CreateFormContent from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/CreateFormContent"; + +const OperatorCreateForm = ({ open, setOpen, mutate, rowId }) => { + return ( + + + + ); +}; +export default OperatorCreateForm; diff --git a/src/components/dashboard/roadItems/operator/Actions/Create/index.jsx b/src/components/dashboard/roadItems/operator/Actions/Create/index.jsx new file mode 100644 index 0000000..ce764e7 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Actions/Create/index.jsx @@ -0,0 +1,38 @@ +"use client"; +import { Button, IconButton, useMediaQuery } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import { useState } from "react"; +import { AddCircle } from "@mui/icons-material"; +import OperatorCreateForm from "./Forms"; + +const OperatorCreate = ({ mutate }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + {open && } + + ); +}; +export default OperatorCreate; diff --git a/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx new file mode 100644 index 0000000..ad5d8b3 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/ExcelPrint/index.jsx @@ -0,0 +1,71 @@ +"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 useRequest from "@/lib/hooks/useRequest"; +import { EXPORT_ROAD_ITEMS_OPERATOR_LIST } from "@/core/utils/routes"; +import { useTheme } from "@emotion/react"; + +const PrintExcel = ({ table, filterData }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [loading, setLoading] = useState(false); + const requestServer = useRequest({ notification: { success: true } }); + const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { + if (filter.value) { + // Check if there's an active filter + acc.push({ id: key, 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); + requestServer(`${EXPORT_ROAD_ITEMS_OPERATOR_LIST}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی کارتابل فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + ); +}; + +export default PrintExcel; diff --git a/src/components/dashboard/roadItems/operator/OperatorList.jsx b/src/components/dashboard/roadItems/operator/OperatorList.jsx new file mode 100644 index 0000000..aed25de --- /dev/null +++ b/src/components/dashboard/roadItems/operator/OperatorList.jsx @@ -0,0 +1,220 @@ +"use client"; +import { useMemo } from "react"; +import { Box, Stack, Typography } from "@mui/material"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import Toolbar from "./Toolbar"; +import { GET_ROAD_ITEMS_OPERATOR_LIST } from "@/core/utils/routes"; +import moment from "jalali-moment"; +import RowActions from "./RowActions"; +import LocationForm from "./RowActions/LocationForm"; +import ImageDialog from "./RowActions/ImageForm"; +import RahdaranDialog from "./RowActions/RahdaranForm"; + +const OperatorList = () => { + const columns = useMemo( + () => [ + { + accessorKey: "id", + header: "کد یکتا", + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "item_fa", + header: "آیتم فعالیت", + id: "item_fa", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "sub_item_fa", + header: "موضوع مشاهده شده", // Observed Subject + id: "sub_item_fa", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "value", + header: "مقدار", // Value + id: "value", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + Cell: ({ renderedCellValue, row }) => { + return ( + + {row.original.sub_item_data} + {`(${row.original.unit_fa})`} + + ); + }, + }, + { + accessorKey: "files", + header: "تصاویر", // Images + id: "files", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue.length > 0) { + return ( + + + + ); + } + return ( + + بدون تصویر + + ); + }, + }, + { + accessorKey: "location", + header: "موقعیت", // Location + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue, row }) => { + return ( + + + + ); + }, + }, + { + accessorKey: "cmms_machine_code", + header: "کد خودرو", // Car ID + id: "cmms_machine_code", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + enableSorting: false, + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "rahdaran", + header: "راهداران", + id: "rahdaran", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون شخص; + }, + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", // Register Date + id: "created_at", + enableColumnFilter: true, + datatype: "date", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorFn: (row) => moment(row.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ فعالیت", // Start Date + id: "activity_date_time", + enableColumnFilter: true, + datatype: "date", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "status_fa", + header: "وضعیت", // Status + id: "status_fa", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: true, + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "supervisor_description", + header: "توضیحات", // Description + id: "supervisor_description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + ], + [] + ); + + return ( + <> + + + + + ); +}; +export default OperatorList; diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx new file mode 100644 index 0000000..9b3a12c --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent.jsx @@ -0,0 +1,211 @@ +import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material"; +import useRequest from "@/lib/hooks/useRequest"; +import { Controller, useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { array, mixed, object, string } from "yup"; +import StyledForm from "@/core/components/StyledForm"; +import React from "react"; +import ImageUpload from "@/components/dashboard/roadItems/operator/Actions/Create/Forms/ImageUpload"; +import MapInfoTwoMarker from "@/core/components/MapInfoTwoMarker"; +import MapInfoOneMarker from "@/core/components/MapInfoOneMarker"; +import { UPDATE_ROAD_ITEMS } from "@/core/utils/routes"; +import CarCode from "@/core/components/CarCode"; +import RahdarCode from "@/core/components/RahdarCode"; +import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems"; + +const validationSchema = object({ + amount: string().required("وارد کردن مقدار الزامیست!"), + cmms_machine_id: object().required("وارد کردن کد خودرو الزامیست!"), + rahdaran_id: array().required("وارد کردن کد راهداران الزامیست!").min(1, "حداقل یک کد راهدار باید وارد شود!"), + before_image: mixed() + .nullable() + .test("before-image-required", "لطفا عکس قبل از اقدام را بارگذاری کنید!", function (value) { + const { subItem } = this.options.context; + const needsImage = subItem?.needs_image === 1; + if (needsImage) { + return !!value; + } + return true; + }), + after_image: mixed() + .nullable() + .test("after-image-required", "لطفا عکس بعد از اقدام را بارگذاری کنید!", function (value) { + const { subItem } = this.options.context; + const needsImage = subItem?.needs_image === 1; + if (needsImage) { + return !!value; + } + return true; + }), + start_point: mixed() + .test("start-point-required", "لطفاً نقطه شروع را مشخص کنید!", function (value) { + return !!value; // چک می‌کند که مقدار موجود است + }) + .required("لطفاً نقطه شروع را مشخص کنید!"), + end_point: mixed().test("end-point-required", "لطفاً نقطه پایان را مشخص کنید!", function (value) { + const { subItem } = this.options.context; + const needsEndPoint = subItem?.needs_end_point === 1; + if (needsEndPoint) { + return !!value; // چک می‌کند که مقدار موجود است + } + return true; + }), +}); + +const EditFormContent = ({ row, mutate, setOpenEditDialog, rowId, subItem }) => { + const defaultValues = { + before_image: row.original?.files[0]?.full_path_for_fast_react || null, + after_image: row.original?.files[1]?.full_path_for_fast_react || null, + amount: row.original.sub_item_data || null, + start_point: { lat: row.original.start_lat || "", lng: row.original.start_lng || "" }, + end_point: { lat: row.original.end_lat || "", lng: row.original.end_lng || "" }, + cmms_machine_id: row.original.cmms_machine, + rahdaran_id: row.original.rahdaran || null, + }; + const requestServer = useRequest(); + + const { + control, + getValues, + watch, + register, + handleSubmit, + setValue, + formState: { errors, isSubmitting }, + } = useForm({ + defaultValues, + resolver: yupResolver(validationSchema), + mode: "onBlur", + context: { subItem }, + }); + + const onSubmit = async (data) => { + let endPoint; + let startPoint = `${data.start_point.lat},${data.start_point.lng}`; + subItem.needs_end_point === 1 && (endPoint = `${data.end_point.lat},${data.end_point.lng}`); + const formData = new FormData(); + subItem.needs_image === 1 && + data.before_image !== defaultValues.before_image && + formData.append("before_image", data.before_image); + subItem.needs_image === 1 && + data.after_image !== defaultValues.after_image && + formData.append("after_image", data.after_image); + formData.append("amount", data.amount); + formData.append("cmms_machine_id", data.cmms_machine_id.id); + data.rahdaran_id.map((rahdar, index) => formData.append(`rahdaran_id[${index}]`, rahdar.id)); + formData.append("start_point", startPoint); + subItem.needs_end_point === 1 && formData.append("end_point", endPoint); + + requestServer(`${UPDATE_ROAD_ITEMS}/${rowId}`, "post", { + notification: { success: true }, + data: formData, + }) + .then((res) => { + mutate(); + setOpenEditDialog(false); + }) + .catch((err) => {}); + }; + return ( + <> + + + + + {subItem.needs_image === 1 ? ( + + ) : null} + + + { + if (!isNaN(event.target.value)) { + setValue("amount", event.target.value); + } else { + setValue("amount", watch("amount")); + } + }} + error={!!errors.amount} + helperText={errors.amount ? errors.amount.message : null} + variant="outlined" + fullWidth + /> + + + { + return ( + field.onChange(value || "")} + inputValueDefault={row.original.cmms_machine_code} + error={error} // اگر خطا وجود داشته باشد + /> + ); + }} + name={"cmms_machine_id"} + /> + + + { + return ( + field.onChange(value || [])} + error={error} + /> + ); + }} + name={"rahdaran_id"} + /> + + + {subItem.needs_end_point === 1 ? ( + + ) : ( + + )} + + + + + + + + + + ); +}; +export default EditFormContent; diff --git a/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx new file mode 100644 index 0000000..92652e9 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/EditForm/index.jsx @@ -0,0 +1,54 @@ +import { Dialog, DialogTitle, IconButton, LinearProgress, Tooltip, Typography } from "@mui/material"; +import { useState } from "react"; +import BorderColorIcon from "@mui/icons-material/BorderColor"; +import EditFormContent from "@/components/dashboard/roadItems/operator/RowActions/EditForm/EditFormContent"; +import useRoadItemGetISubtems from "@/lib/hooks/useRoadItemGetISubtems"; + +const EditForm = ({ row, mutate, rowId }) => { + const [openEditDialog, setOpenEditDialog] = useState(false); + const { subItemsList, loadingSubItemsList, errorSubItemsList } = useRoadItemGetISubtems(row.original?.item); + const subItem = subItemsList.find((subItem) => subItem.sub_item === row.original?.sub_item); + return ( + <> + + { + setOpenEditDialog(true); + }} + > + + + + + ویرایش اطلاعات + {errorSubItemsList ? ( + + خطا در دریافت اطلاعات!!! + + ) : loadingSubItemsList ? ( + + ) : ( + + )} + + + ); +}; +export default EditForm; diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx new file mode 100644 index 0000000..c9f4e2e --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/ImageFormContent.jsx @@ -0,0 +1,41 @@ +import { DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; + +const ImageFormContent = ({ image, title }) => { + const theme = useTheme(); + return ( + + + + + {title} + + Image + + + + ); +}; +export default ImageFormContent; diff --git a/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx new file mode 100644 index 0000000..8bdb7e9 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/ImageForm/index.jsx @@ -0,0 +1,41 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; +import ImageFormContent from "./ImageFormContent"; + +const ImageDialog = ({ images }) => { + const [openImageDialog, setOpenImageDialog] = useState(false); + + return ( + <> + + 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", + }, + }} + dir="rtl" + maxWidth={"md"} + > + تصاویر + + + + + + + + ); +}; + +export default ImageDialog; diff --git a/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx new file mode 100644 index 0000000..6c17f3a --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx @@ -0,0 +1,36 @@ +"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/dashboard/roadItems/operator/RowActions/LocationForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/index.jsx new file mode 100644 index 0000000..f578ca8 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/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/dashboard/roadItems/operator/RowActions/RahdaranForm/RahdaranContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/RahdaranContent.jsx new file mode 100644 index 0000000..d1e9b15 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/RahdaranContent.jsx @@ -0,0 +1,48 @@ +import { DialogContent, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"; + +const RahdaranContent = ({ rahdarLists }) => { + return ( + <> + + + + + + + کد راهدار + نام + + + + {rahdarLists.map((rahdar) => { + return ( + + {rahdar.code} + {rahdar.name} + + ); + })} + +
+
+
+
+ + ); +}; +export default RahdaranContent; diff --git a/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/index.jsx new file mode 100644 index 0000000..2c73d80 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/RahdaranForm/index.jsx @@ -0,0 +1,38 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import GroupsIcon from "@mui/icons-material/Groups"; +import { useState } from "react"; +import RahdaranContent from "./RahdaranContent"; + +const RahdaranDialog = ({ rahdarLists }) => { + const [openRahdaranDialog, setOpenRahdaranDialog] = useState(false); + return ( + <> + + setOpenRahdaranDialog(true)}> + + + + setOpenRahdaranDialog(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 RahdaranDialog; diff --git a/src/components/dashboard/roadItems/operator/RowActions/index.jsx b/src/components/dashboard/roadItems/operator/RowActions/index.jsx new file mode 100644 index 0000000..c5051d8 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/RowActions/index.jsx @@ -0,0 +1,11 @@ +import { Box } from "@mui/material"; +import EditForm from "./EditForm"; + +const RowActions = ({ row, mutate }) => { + return ( + + {row.original.status === 2 && } + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/roadItems/operator/Toolbar.jsx b/src/components/dashboard/roadItems/operator/Toolbar.jsx new file mode 100644 index 0000000..c98ae05 --- /dev/null +++ b/src/components/dashboard/roadItems/operator/Toolbar.jsx @@ -0,0 +1,13 @@ +import PrintExcel from "./ExcelPrint"; +import OperatorCreate from "./Actions/Create"; +import { Stack } from "@mui/material"; + +const Toolbar = ({ table, filterData, mutate }) => { + return ( + + + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/roadItems/operator/index.jsx b/src/components/dashboard/roadItems/operator/index.jsx new file mode 100644 index 0000000..a2b328c --- /dev/null +++ b/src/components/dashboard/roadItems/operator/index.jsx @@ -0,0 +1,14 @@ +"use client"; +import PageTitle from "@/core/components/PageTitle"; +import { Stack } from "@mui/material"; +import OperatorList from "./OperatorList"; + +const OperatorPage = () => { + return ( + + + + + ); +}; +export default OperatorPage; diff --git a/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx new file mode 100644 index 0000000..1cc0b2c --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/ExcelPrint/index.jsx @@ -0,0 +1,70 @@ +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 useRequest from "@/lib/hooks/useRequest"; +import { EXPORT_ROAD_ITEMS_SUPERVISOR_LIST } from "@/core/utils/routes"; +import { useTheme } from "@emotion/react"; + +const PrintExcel = ({ table, filterData }) => { + const [loading, setLoading] = useState(false); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const requestServer = useRequest(); + const activeFilters = Object.entries(filterData).reduce((acc, [key, filter]) => { + if (filter.value) { + // Check if there's an active filter + acc.push({ id: key, 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); + requestServer(`${EXPORT_ROAD_ITEMS_SUPERVISOR_LIST}?${filterParams}`, "get", { + requestOptions: { responseType: "blob" }, + }) + .then((response) => { + const filename = `خروجی کارتابل فعالیت روزانه تاریخ_${moment().format("jYYYY_jMM_jDD")}.xlsx`; + FileSaver.saveAs(response.data, filename); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); + }; + + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + ); +}; + +export default PrintExcel; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx new file mode 100644 index 0000000..2f7af71 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/ConfirmContent.jsx @@ -0,0 +1,69 @@ +import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material"; +import { useForm } from "react-hook-form"; +import useRequest from "@/lib/hooks/useRequest"; +import { VERIFY_BY_SUPERVISOR } from "@/core/utils/routes"; + +const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => { + const requestServer = useRequest({ notification: { success: true } }); + + const { + register, + handleSubmit, + formState: { isSubmitting }, + reset, + } = useForm({ + defaultValues: { + description: "", + }, + }); + + const onSubmit = async (data) => { + const formData = new FormData(); + data.description !== "" && formData.append("description", data.description); + formData.append("verify", 1); + requestServer(`${VERIFY_BY_SUPERVISOR}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenConfirmDialog(false); + }) + .catch(() => {}); + }; + + return ( + <> + + + + + + + + + + + + + ); +}; + +export default ConfirmContent; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/index.jsx new file mode 100644 index 0000000..a542a30 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/ConfirmForm/index.jsx @@ -0,0 +1,29 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ConfirmContent from "./ConfirmContent"; +import DoneIcon from "@mui/icons-material/Done"; +const ConfirmForm = ({ rowId, mutate }) => { + const [openConfirmDialog, setOpenConfirmDialog] = useState(false); + return ( + <> + + setOpenConfirmDialog(true)}> + + + + + تایید + + + + ); +}; +export default ConfirmForm; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx new file mode 100644 index 0000000..9e61c1b --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/DeleteContent.jsx @@ -0,0 +1,42 @@ +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { DELETE_BY_SUPERVISOR } from "@/core/utils/routes"; + +const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notification: { success: true } }); + const handleClick = () => { + setSubmitting(true); + requestServer(`${DELETE_BY_SUPERVISOR}/${rowId}`, "post") + .then(() => { + mutate(); + setOpenDeleteDialog(false); + setSubmitting(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + + آیا از حذف فعالیت اطمینان دارید؟ + + + + + + + + + ); +}; + +export default DeleteContent; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx new file mode 100644 index 0000000..c8c8375 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/DeleteForm/index.jsx @@ -0,0 +1,33 @@ +import React, { useState } from "react"; +import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; +import DeleteIcon from "@mui/icons-material/Delete"; +import DeleteContent from "./DeleteContent"; +import useRequest from "@/lib/hooks/useRequest"; +const DeleteForm = ({ 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"} + > + حذف فعالیت + + + + ); +}; +export default DeleteForm; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx new file mode 100644 index 0000000..c9f4e2e --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/ImageFormContent.jsx @@ -0,0 +1,41 @@ +import { DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; + +const ImageFormContent = ({ image, title }) => { + const theme = useTheme(); + return ( + + + + + {title} + + Image + + + + ); +}; +export default ImageFormContent; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/index.jsx new file mode 100644 index 0000000..0e70c1d --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/ImageForm/index.jsx @@ -0,0 +1,41 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; +import ImageFormContent from "./ImageFormContent"; + +const ImageDialog = ({ images }) => { + const [openImageDialog, setOpenImageDialog] = useState(false); + + return ( + <> + + 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", + }, + }} + dir="rtl" + maxWidth={"md"} + > + تصاویر + + + + + + + + ); +}; + +export default ImageDialog; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/LocationFormContent.jsx new file mode 100644 index 0000000..6c17f3a --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/LocationFormContent.jsx @@ -0,0 +1,36 @@ +"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/dashboard/roadItems/supervisor/RowActions/LocationForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/LocationForm/index.jsx new file mode 100644 index 0000000..f578ca8 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/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/dashboard/roadItems/supervisor/RowActions/RahdaranForm/RahdaranContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/RahdaranContent.jsx new file mode 100644 index 0000000..d1e9b15 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/RahdaranContent.jsx @@ -0,0 +1,48 @@ +import { DialogContent, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"; + +const RahdaranContent = ({ rahdarLists }) => { + return ( + <> + + + + + + + کد راهدار + نام + + + + {rahdarLists.map((rahdar) => { + return ( + + {rahdar.code} + {rahdar.name} + + ); + })} + +
+
+
+
+ + ); +}; +export default RahdaranContent; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/index.jsx new file mode 100644 index 0000000..2c73d80 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RahdaranForm/index.jsx @@ -0,0 +1,38 @@ +import { Button, Dialog, DialogActions, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import GroupsIcon from "@mui/icons-material/Groups"; +import { useState } from "react"; +import RahdaranContent from "./RahdaranContent"; + +const RahdaranDialog = ({ rahdarLists }) => { + const [openRahdaranDialog, setOpenRahdaranDialog] = useState(false); + return ( + <> + + setOpenRahdaranDialog(true)}> + + + + setOpenRahdaranDialog(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 RahdaranDialog; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx new file mode 100644 index 0000000..087e020 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/RejectContent.jsx @@ -0,0 +1,81 @@ +import { Button, DialogActions, DialogContent, Stack, TextField } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import * as Yup from "yup"; +import useRequest from "@/lib/hooks/useRequest"; +import { REJECT_BY_SUPERVISOR } from "@/core/utils/routes"; + +const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => { + const requestServer = useRequest({ notification: { success: true } }); + + const validationSchema = Yup.object().shape({ + description: Yup.string().required("توضیحات الزامیست!!!"), + }); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + reset, + } = useForm({ + defaultValues: { + description: "", + }, + resolver: yupResolver(validationSchema), + mode: "onBlur", + }); + + const onSubmit = async (data) => { + const formData = new FormData(); + formData.append("description", data.description); + formData.append("verify", 2); + requestServer(`${REJECT_BY_SUPERVISOR}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenRejectDialog(false); + }) + .catch(() => {}) + .finally(() => { + reset(); + }); + }; + + return ( + <> + + + + + + + + + + + + + ); +}; +export default RejectContent; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/index.jsx new file mode 100644 index 0000000..e05288c --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RejectForm/index.jsx @@ -0,0 +1,29 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import RejectContent from "./RejectContent"; +import ClearIcon from "@mui/icons-material/Clear"; +const RejectForm = ({ rowId, mutate }) => { + const [openRejectDialog, setOpenRejectDialog] = useState(false); + return ( + <> + + setOpenRejectDialog(true)}> + + + + + عدم تایید + + + + ); +}; +export default RejectForm; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx new file mode 100644 index 0000000..b412aa0 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/RestoreContent.jsx @@ -0,0 +1,42 @@ +import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material"; +import React, { useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { RESTORE_BY_SUPERVISOR } from "@/core/utils/routes"; + +const RestoreContent = ({ rowId, mutate, setOpenRestoreDialog }) => { + const [submitting, setSubmitting] = useState(false); + const requestServer = useRequest({ notification: { success: true } }); + const handleClick = () => { + setSubmitting(true); + requestServer(`${RESTORE_BY_SUPERVISOR}/${rowId}`, "post") + .then(() => { + mutate(); + setOpenRestoreDialog(false); + setSubmitting(false); + }) + .catch(() => { + setSubmitting(false); + }); + }; + return ( + <> + + + + آیا از بازگردانی فعالیت اطمینان دارید؟ + + + + + + + + + ); +}; + +export default RestoreContent; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/index.jsx new file mode 100644 index 0000000..2632c20 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/RestoreForm/index.jsx @@ -0,0 +1,32 @@ +import React, { useState } from "react"; +import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; +import ReplyIcon from "@mui/icons-material/Reply"; +import RestoreContent from "./RestoreContent"; +const RestoreForm = ({ rowId, mutate }) => { + const [openRestoreDialog, setOpenRestoreDialog] = useState(false); + + return ( + <> + + setOpenRestoreDialog(true)}> + + + + setOpenRestoreDialog(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 RestoreForm; diff --git a/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx b/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx new file mode 100644 index 0000000..b18a1da --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/RowActions/index.jsx @@ -0,0 +1,27 @@ +import { Box } from "@mui/material"; +import DeleteForm from "./DeleteForm"; +import RejectForm from "./RejectForm"; +import ConfirmForm from "./ConfirmForm"; +import RestoreForm from "./RestoreForm"; +import { usePermissions } from "@/lib/hooks/usePermissions"; + +const RowActions = ({ row, mutate }) => { + const { data: userPermissions } = usePermissions(); + const hasDeletePermission = userPermissions.includes("delete-road-item"); + const hasRestorePermission = userPermissions.includes("restore-road-item"); + return ( + + {row.original.status === 0 && row.original.can_supervise === 1 && ( + + )} + {row.original.status === 0 && row.original.can_supervise === 1 && ( + + )} + {hasRestorePermission && row.original.status !== 0 && ( + + )} + {hasDeletePermission && } + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx new file mode 100644 index 0000000..1166ed6 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx @@ -0,0 +1,239 @@ +import { useMemo } from "react"; +import { Box, Stack, Typography } from "@mui/material"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import Toolbar from "./Toolbar"; +import { GET_ROAD_ITEMS_SUPERVISOR_LIST } from "@/core/utils/routes"; +import moment from "jalali-moment"; +import RowActions from "./RowActions"; +import LocationForm from "./RowActions/LocationForm"; +import ImageDialog from "./RowActions/ImageForm"; +import RahdaranDialog from "./RowActions/RahdaranForm"; + +const SupervisorList = () => { + const columns = useMemo( + () => [ + { + accessorKey: "id", + header: "کد یکتا", // Unique Code + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "province_fa", + header: "استان", // Province + id: "province_fa", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "edarat_name", + header: "اداره", // Office + id: "edarat_name", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "item_fa", + header: "آیتم فعالیت", + id: "item_fa", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "sub_item_fa", + header: "موضوع مشاهده شده", // Observed Subject + id: "sub_item_fa", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "value", + header: "مقدار", // Value + id: "value", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + Cell: ({ renderedCellValue, row }) => { + return ( + + {row.original.sub_item_data} + {`(${row.original.unit_fa})`} + + ); + }, + }, + { + accessorKey: "files", + header: "تصاویر", // Images + id: "files", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue.length > 0) { + return ( + + + + ); + } + return ( + + بدون تصویر + + ); + }, + }, + { + accessorKey: "location", + header: "موقعیت", // Location + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue, row }) => { + return ( + + + + ); + }, + }, + { + accessorKey: "cmms_machine_code", + header: "کد خودرو", // Car ID + id: "cmms_machine_code", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + enableSorting: false, + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "rahdaran", + header: "راهداران", + id: "rahdaran", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون شخص; + }, + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", // Register Date + id: "created_at", + enableColumnFilter: true, + datatype: "date", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorFn: (row) => moment(row.activity_date_time).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ فعالیت", // Start Date + id: "activity_date_time", + enableColumnFilter: true, + datatype: "date", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "status_fa", + header: "وضعیت", // Status + id: "status_fa", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: true, + columnFilterModeOptions: ["equals", "contains"], + }, + { + accessorKey: "supervisor_description", + header: "توضیحات", // Description + id: "supervisor_description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + columnFilterModeOptions: ["equals", "contains"], + }, + ], + [] + ); + + return ( + <> + + + + + ); +}; + +export default SupervisorList; diff --git a/src/components/dashboard/roadItems/supervisor/Toolbar.jsx b/src/components/dashboard/roadItems/supervisor/Toolbar.jsx new file mode 100644 index 0000000..32c4395 --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/Toolbar.jsx @@ -0,0 +1,10 @@ +import PrintExcel from "./ExcelPrint"; + +const Toolbar = ({ table, filterData }) => { + return ( + <> + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/roadItems/supervisor/index.jsx b/src/components/dashboard/roadItems/supervisor/index.jsx new file mode 100644 index 0000000..3fa490b --- /dev/null +++ b/src/components/dashboard/roadItems/supervisor/index.jsx @@ -0,0 +1,14 @@ +"use client"; +import PageTitle from "@/core/components/PageTitle"; +import { Stack } from "@mui/material"; +import SupervisorList from "./SupervisorList"; + +const SupervisorPage = () => { + return ( + + + + + ); +}; +export default SupervisorPage; diff --git a/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx index 8fdc753..54df4da 100644 --- a/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadPatrols/operator/ExcelPrint/index.jsx @@ -4,7 +4,7 @@ import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; -import { EXPORT_OPERATOR_LIST } from "@/core/utils/routes"; +import { EXPORT_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes"; const PrintExcel = ({ table, filterData }) => { const [loading, setLoading] = useState(false); @@ -28,7 +28,7 @@ const PrintExcel = ({ table, filterData }) => { const clickHandler = () => { setLoading(true); - requestServer(`${EXPORT_OPERATOR_LIST}?${filterParams}`, "get", { + requestServer(`${EXPORT_ROAD_PATROL_OPERATOR_LIST}?${filterParams}`, "get", { requestOptions: { responseType: "blob" }, }) .then((response) => { diff --git a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx index 1d7118a..05f083b 100644 --- a/src/components/dashboard/roadPatrols/operator/OperatorList.jsx +++ b/src/components/dashboard/roadPatrols/operator/OperatorList.jsx @@ -3,7 +3,7 @@ import { useMemo } from "react"; import { Box, Typography } from "@mui/material"; import DataTableWithAuth from "@/core/components/DataTableWithAuth"; import Toolbar from "./Toolbar"; -import { GET_OPERATOR_LIST } from "@/core/utils/routes"; +import { GET_ROAD_PATROL_OPERATOR_LIST } from "@/core/utils/routes"; import moment from "jalali-moment"; import RowActions from "./RowActions"; @@ -99,7 +99,7 @@ const OperatorList = () => { table_title={"عملیات"} need_filter={true} columns={columns} - table_url={GET_OPERATOR_LIST} + table_url={GET_ROAD_PATROL_OPERATOR_LIST} page_name={"operator"} table_name={"operatorList"} TableToolbar={Toolbar} diff --git a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx index 49c05eb..e5ba95a 100644 --- a/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/ExcelPrint/index.jsx @@ -4,7 +4,7 @@ import moment from "jalali-moment"; import FileSaver from "file-saver"; import DescriptionIcon from "@mui/icons-material/Description"; import useRequest from "@/lib/hooks/useRequest"; -import { EXPORT_SUPERVISOR_LIST } from "@/core/utils/routes"; +import { EXPORT_ROAD_PATROL_SUPERVISOR_LIST } from "@/core/utils/routes"; const PrintExcel = ({ table, filterData }) => { const [loading, setLoading] = useState(false); @@ -28,7 +28,7 @@ const PrintExcel = ({ table, filterData }) => { const clickHandler = () => { setLoading(true); - requestServer(`${EXPORT_SUPERVISOR_LIST}?${filterParams}`, "get", { + requestServer(`${EXPORT_ROAD_PATROL_SUPERVISOR_LIST}?${filterParams}`, "get", { requestOptions: { responseType: "blob" }, }) .then((response) => { diff --git a/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx b/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx index 46cad99..a7218df 100644 --- a/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/index.jsx @@ -1,19 +1,7 @@ import React, { useState } from "react"; -import { - Tooltip, - IconButton, - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, - Box, - Typography, -} from "@mui/material"; +import { Tooltip, IconButton, Dialog, DialogTitle } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; -import CloseIcon from "@mui/icons-material/Close"; -import DeleteContent from "@/components/dashboard/roadPatrols/supervisor/RowActions/DeleteForm/DeleteContent"; - +import DeleteContent from "./DeleteContent"; const DeleteForm = ({ rowId, mutate }) => { const [openDeleteDialog, setOpenDeleteDialog] = useState(false); @@ -41,5 +29,4 @@ const DeleteForm = ({ rowId, mutate }) => { ); }; - export default DeleteForm; diff --git a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx index 6768a91..a5e15f4 100644 --- a/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/roadPatrols/supervisor/SupervisorList.jsx @@ -3,7 +3,7 @@ import { useMemo } from "react"; import { Box, Typography } from "@mui/material"; import DataTableWithAuth from "@/core/components/DataTableWithAuth"; import Toolbar from "./Toolbar"; -import { GET_SUPERVISOR_LIST } from "@/core/utils/routes"; +import { GET_ROAD_PATROL_SUPERVISOR_LIST } from "@/core/utils/routes"; import moment from "jalali-moment"; import RowActions from "./RowActions"; import useProvinces from "@/lib/hooks/useProvince"; @@ -140,7 +140,7 @@ const OperatorList = () => { table_title={"ارزیابی"} need_filter={true} columns={columns} - table_url={GET_SUPERVISOR_LIST} + table_url={GET_ROAD_PATROL_SUPERVISOR_LIST} page_name={"supervisor"} table_name={"supervisorList"} TableToolbar={Toolbar} diff --git a/src/core/components/CarCode.jsx b/src/core/components/CarCode.jsx index 503a826..32a4c20 100644 --- a/src/core/components/CarCode.jsx +++ b/src/core/components/CarCode.jsx @@ -1,28 +1,26 @@ -"use client"; - -import {Autocomplete, CircularProgress, TextField} from "@mui/material"; -import {useEffect, useState} from "react"; +import { Autocomplete, CircularProgress, TextField } from "@mui/material"; +import { useEffect, useState } from "react"; import useRequest from "@/lib/hooks/useRequest"; -import {debounce} from "@mui/material/utils"; +import { debounce } from "@mui/material/utils"; +import { GET_CAR_LIST_SEARCH } from "@/core/utils/routes"; -const CarCode = ({carCode, setCarCode}) => { - const [inputValue, setInputValue] = useState(''); +const CarCode = ({ carCode, setCarCode, inputValueDefault = "", error }) => { + const [inputValue, setInputValue] = useState(inputValueDefault); const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); const requestServer = useRequest(); - const fetchCarCodes = (inputValue, controller) => { const debouncer = debounce((query) => { - if (query.length < 2) { + if (!query || query?.length < 3) { setOptions([]); return; } setLoading(true); - requestServer(`car-code/api?${query}`, "get", { - requestOptions: {signal: controller.signal} + requestServer(`${GET_CAR_LIST_SEARCH}?machine_code=${query}`, "get", { + requestOptions: { signal: controller.signal }, }) .then((response) => { - setOptions(response || []); + setOptions(response.data.data || []); }) .catch(() => { setOptions([]); @@ -30,9 +28,9 @@ const CarCode = ({carCode, setCarCode}) => { .finally(() => { setLoading(false); }); - }, 500) - debouncer(inputValue) - } + }, 500); + debouncer(inputValue); + }; useEffect(() => { const controller = new AbortController(); @@ -45,34 +43,35 @@ const CarCode = ({carCode, setCarCode}) => { value={carCode} size="small" onChange={(event, newValue) => { - setCarCode(newValue); + setCarCode(newValue || null); // Set an empty string if no value is selected }} inputValue={inputValue} onInputChange={(event, newInputValue) => { - setInputValue(newInputValue); + setInputValue(newInputValue || ""); // Prevent inputValue from being null or undefined }} options={options} - getOptionLabel={(option) => - typeof option === 'string' ? option : option.name || option.code - } + getOptionLabel={(option) => (typeof option === "string" ? option : option.machine_code)} + isOptionEqualToValue={(option, value) => { + if (!value) return false; // Handle null/undefined values + if (value === "") return option.machine_code === ""; // Handle empty string case + return option.machine_code === (value?.machine_code || value); + }} loading={loading} loadingText="درحال جستجوی کد خودرو" - noOptionsText={ - inputValue.length < 2 - ? "حداقل دو رقم از کد خودرو را وارد کنید" - : "کد خودرویی یافت نشد" - } - sx={{width: 300}} + noOptionsText={inputValue?.length < 3 ? "حداقل سه رقم از کد خودرو را وارد کنید" : "کد خودرویی یافت نشد"} + fullWidth renderInput={(params) => ( - {loading ? : null} + {loading ? : null} {params.InputProps.endAdornment} ), @@ -82,5 +81,4 @@ const CarCode = ({carCode, setCarCode}) => { /> ); }; - -export default CarCode; \ No newline at end of file +export default CarCode; diff --git a/src/core/components/DataTable/filter/FilterBody.jsx b/src/core/components/DataTable/filter/FilterBody.jsx index 2bc6d9c..d0eb7cf 100644 --- a/src/core/components/DataTable/filter/FilterBody.jsx +++ b/src/core/components/DataTable/filter/FilterBody.jsx @@ -8,6 +8,7 @@ import { Box, Button, Drawer, styled } from "@mui/material"; import { useEffect, useState } from "react"; import { Controller, useForm, useWatch } from "react-hook-form"; import * as Yup from "yup"; +import FilterBodyController from "./FilterBodyController"; const ScrollBox = styled(Box)({ flexGrow: 1, @@ -95,27 +96,12 @@ function FilterBody({ columns, drawerState, setDrawerState }) { {columns.map( (column) => column.enableColumnFilter && ( - { - let dependencyField; - if (column.dependencyId) { - dependencyField = useWatch({ control, name: column.dependencyId }); - } - return ( - reset()} - errors={errors} - /> - ); - }} + errors={errors} + reset={reset} /> ) )} diff --git a/src/core/components/DataTable/filter/FilterBodyController.jsx b/src/core/components/DataTable/filter/FilterBodyController.jsx new file mode 100644 index 0000000..df3ba62 --- /dev/null +++ b/src/core/components/DataTable/filter/FilterBodyController.jsx @@ -0,0 +1,25 @@ +import { Controller, useWatch } from "react-hook-form"; +import FilterBodyField from "./FilterBodyField"; + +const FilterBodyController = ({ column, control, reset, errors }) => { + const dependencyField = useWatch({ control, name: column?.dependencyId }); + + return ( + ( + reset()} + errors={errors} + /> + )} + /> + ); +}; +export default FilterBodyController; diff --git a/src/core/components/DataTable/reset/ResetStorage.jsx b/src/core/components/DataTable/reset/ResetStorage.jsx index 9f6abd5..8e83d3f 100644 --- a/src/core/components/DataTable/reset/ResetStorage.jsx +++ b/src/core/components/DataTable/reset/ResetStorage.jsx @@ -1,11 +1,12 @@ "use client"; - -import { IconButton, Tooltip } from "@mui/material"; +import { IconButton, Tooltip, Badge } from "@mui/material"; import AutorenewIcon from "@mui/icons-material/Autorenew"; import useTableSetting from "@/lib/hooks/useTableSetting"; +import useDataTable from "@/lib/hooks/useDataTable"; function ResetStorage({ user_id, page_name, table_name }) { const { resetAction } = useTableSetting(); + const { isDirty } = useDataTable(); const reset = () => { resetAction(user_id, page_name, table_name); }; @@ -13,7 +14,20 @@ function ResetStorage({ user_id, page_name, table_name }) { return ( - + {isDirty ? ( + + + + ) : ( + + )} ); diff --git a/src/core/components/MapInfoOneMarker.jsx b/src/core/components/MapInfoOneMarker.jsx new file mode 100644 index 0000000..7132d5d --- /dev/null +++ b/src/core/components/MapInfoOneMarker.jsx @@ -0,0 +1,137 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Marker, useMapEvents } from "react-leaflet"; +import "leaflet/dist/leaflet.css"; +import L from "leaflet"; +import { Box, Button, Typography } from "@mui/material"; +import dynamic from "next/dynamic"; +import MapLoading from "@/core/components/MapLayer/Loading"; +import HereIcon from "@/assets/images/examine_marker_active.png"; + +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); + +const createCustomIcon = (size, iconUrl, labelText) => { + if (labelText) { + return L.divIcon({ + html: ` +
+ icon +
+ ${labelText} +
+
+ `, + iconSize: size, + iconAnchor: [size[0] / 2, size[1]], + popupAnchor: [0, -size[1]], + }); + } + + return L.icon({ + iconUrl: iconUrl, + iconSize: size, + iconAnchor: [size[0] / 2, size[1]], + popupAnchor: [0, -size[1]], + }); +}; + +const MapInteraction = ({ setValue, startLat, startLng }) => { + const [isMarkerLocked, setIsMarkerLocked] = useState(!!(startLat && startLng)); // وضعیت قفل مارکر + const [markerPosition, setMarkerPosition] = useState( + startLat && startLng ? { lat: startLat, lng: startLng } : null + ); + const markerRef = useRef(); + + const map = useMapEvents({ + move(e) { + if (!isMarkerLocked && markerRef.current) { + markerRef.current.setLatLng(e.target.getCenter()); + } + }, + zoom(e) { + if (!isMarkerLocked && markerRef.current) { + markerRef.current.setLatLng(e.target.getCenter()); + } + }, + }); + + useEffect(() => { + if (startLat && startLng) { + const position = { lat: startLat, lng: startLng }; + map.setView(position, 15); + } + }, [startLat, startLng, map]); + + const handleMarkerClick = () => { + if (!isMarkerLocked) { + const center = map.getCenter(); + setValue("start_point", { lat: center.lat.toString(), lng: center.lng.toString() }); + setMarkerPosition({ lat: center.lat, lng: center.lng }); // به‌روزرسانی موقعیت مارکر + setIsMarkerLocked(true); + } + }; + + const handleUnlockMarker = () => { + setValue("start_point", null); // حذف مقدار قبلی + setIsMarkerLocked(false); // باز کردن قفل مارکر + setMarkerPosition(null); // تنظیم مارکر به مرکز نقشه + }; + + return ( + <> + + {isMarkerLocked && ( + + + + )} + + ); +}; + +const MapInfoOneMarker = ({ setValue, errors, StartPoint = null }) => { + return ( + + + + + + + + {errors.start_point && ( + + {errors.start_point.message} + + )} + + + ); +}; + +export default MapInfoOneMarker; diff --git a/src/core/components/MapInfoTwoMarker.jsx b/src/core/components/MapInfoTwoMarker.jsx new file mode 100644 index 0000000..f4bd24b --- /dev/null +++ b/src/core/components/MapInfoTwoMarker.jsx @@ -0,0 +1,191 @@ +import React, { useEffect, useRef, useState } from "react"; +import { Marker, useMapEvents } from "react-leaflet"; +import "leaflet/dist/leaflet.css"; +import L from "leaflet"; +import { Box, Button, Typography } from "@mui/material"; +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"; + +const MapLayer = dynamic(() => import("@/core/components/MapLayer"), { + loading: () => , + ssr: false, +}); +const createCustomIcon = (size, iconUrl, labelText) => { + if (labelText) { + return L.divIcon({ + html: ` +
+ icon +
+ ${labelText} +
+
+ `, + iconSize: size, + iconAnchor: [size[0] / 2, size[1]], + popupAnchor: [0, -size[1]], + }); + } + + return L.icon({ + iconUrl: iconUrl, + iconSize: size, + iconAnchor: [size[0] / 2, size[1]], + popupAnchor: [0, -size[1]], + }); +}; + +const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => { + const [isStartLocked, setIsStartLocked] = useState(!!(startLat && startLng)); // وضعیت قفل نقطه آغاز + const [isEndLocked, setIsEndLocked] = useState(!!(endLat && endLng)); // وضعیت قفل نقطه پایان + const [startPosition, setStartPosition] = useState(startLat && startLng ? { lat: startLat, lng: startLng } : null); + const [endPosition, setEndPosition] = useState(endLat && endLng ? { lat: endLat, lng: endLng } : null); + const startRef = useRef(); + const endRef = useRef(); + + const map = useMapEvents({ + move(e) { + if (!isStartLocked && startRef.current) { + startRef.current.setLatLng(e.target.getCenter()); + } else if (isStartLocked && !isEndLocked && endRef.current) { + endRef.current.setLatLng(e.target.getCenter()); + } + }, + zoom(e) { + if (!isStartLocked && startRef.current) { + startRef.current.setLatLng(e.target.getCenter()); + } else if (isStartLocked && !isEndLocked && endRef.current) { + endRef.current.setLatLng(e.target.getCenter()); + } + }, + }); + + useEffect(() => { + if (startLat && endLng) { + map.fitBounds( + [ + { lat: startLat, lng: startLng }, + { lat: endLat, lng: endLng }, + ], + { paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] } + ); + } + }, [startLat, map, endLng]); + + const handleUnlockStart = () => { + setIsStartLocked(false); + setStartPosition(null); + setValue("start_point", null); + }; + + const handleUnlockEnd = () => { + setIsEndLocked(false); + setIsStartLocked(false); + setEndPosition(null); + setStartPosition(null); + setValue("end_point", ""); + setValue("start_point", null); + }; + + return ( + <> + { + if (!isStartLocked) { + const center = map.getCenter(); + setValue("start_point", { lat: center.lat.toString(), lng: center.lng.toString() }); + setStartPosition({ lat: center.lat, lng: center.lng }); + setIsStartLocked(true); + } + }, + }} + /> + {isStartLocked && !isEndLocked && ( + + + + )} + {isStartLocked && ( + { + if (!isEndLocked) { + const center = map.getCenter(); + setValue("end_point", { lat: center.lat.toString(), lng: center.lng.toString() }); + setEndPosition({ lat: center.lat, lng: center.lng }); + setIsEndLocked(true); + } + }, + }} + /> + )} + {isEndLocked && ( + + + + )} + + ); +}; + +const MapInfoTwoMarker = ({ setValue, errors, StartPoint = null, EndPoint = null }) => { + return ( + + + + + + + + {errors.start_point && ( + + {errors.start_point.message} + + )} + {errors.end_point && ( + + {errors.end_point.message} + + )} + + + ); +}; +export default MapInfoTwoMarker; diff --git a/src/core/components/MuiDatePicker.jsx b/src/core/components/MuiDatePicker.jsx index 1be8fdb..78ffe58 100644 --- a/src/core/components/MuiDatePicker.jsx +++ b/src/core/components/MuiDatePicker.jsx @@ -1,53 +1,70 @@ -import { LocalizationProvider, MobileDateTimePicker } from "@mui/x-date-pickers"; +import React from "react"; +import { LocalizationProvider, MobileDatePicker } from "@mui/x-date-pickers"; import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali"; -import moment from "jalali-moment"; import { faIR } from "@mui/x-date-pickers/locales"; -import { Box, IconButton, FormControl, FormHelperText, InputAdornment, Stack } from "@mui/material"; +import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material"; import ClearIcon from "@mui/icons-material/Clear"; +import moment from "jalali-moment"; -export default function PickerWithButtonField({ formik, name, value, error, touched, disabled }) { +function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error }) { return ( - - - - { - const formattedDate = moment(newValue).locale("en").format("YYYY-MM-DD HH:mm"); - formik.setFieldValue(name, formattedDate); - }} - slotProps={{ - textField: { - placeholder: - name === "start_date" ? "تاریخ شروع را وارد کنید" : "تاریخ اتمام را وارد کنید", - variant: "outlined", - disabled, - fullWidth: true, - error: !!error, - helperText: touched && error, - size: "small", // Ensure that the TextField is small - InputProps: { - endAdornment: ( - - formik.setFieldValue(name, null)} - > - - - - ), - }, + + + { + const date = new Date(value); + const formattedDate = moment(date).locale("en").format("YYYY-MM-DD"); + setFieldValue(name, formattedDate); + }} + minDate={minDate ? new Date(minDate) : null} + maxDate={maxDate ? new Date(maxDate) : null} + slotProps={{ + textField: { + error: error, + size: "small", + placeholder: placeholder, + InputProps: { + endAdornment: ( + + { + event.stopPropagation(); + setFieldValue(name, ""); + }} + sx={{ + color: "#bfbfbf", + "&:hover": { + backgroundColor: "rgba(189, 189, 189, 0.1)", + color: "#363434", + }, + }} + > + + + + ), }, - }} - /> - - - + }, + }} + /> + + {helperText ? helperText : ""} + + + ); } + +export default MuiDatePicker; diff --git a/src/core/components/MuiTimePicker.jsx b/src/core/components/MuiTimePicker.jsx new file mode 100644 index 0000000..35532fd --- /dev/null +++ b/src/core/components/MuiTimePicker.jsx @@ -0,0 +1,75 @@ +import React from "react"; +import { LocalizationProvider, MobileTimePicker } 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 { parseISO, format } from "date-fns"; + +function MuiTimePicker({ value, setFieldValue, name, minTime, maxTime, helperText, placeholder, error }) { + // Ensure value, minTime, maxTime are Date objects + const parsedValue = value ? (typeof value === "string" ? parseISO(value) : value) : null; + const parsedMinTime = minTime ? (typeof minTime === "string" ? parseISO(minTime) : minTime) : null; + const parsedMaxTime = maxTime && (typeof maxTime === "string" ? parseISO(maxTime) : null); + + return ( + + + { + const formattedTime = newValue ? format(new Date(newValue), "HH:mm") : ""; + setFieldValue(name, formattedTime); + }} + minTime={parsedMinTime} + maxTime={parsedMaxTime} + slotProps={{ + textField: { + placeholder: placeholder, + size: "small", + error: error, + InputProps: { + endAdornment: ( + + { + event.stopPropagation(); + setFieldValue(name, ""); + }} + sx={{ + color: "#bfbfbf", + "&:hover": { + backgroundColor: "rgba(189, 189, 189, 0.1)", + color: "#363434", + }, + }} + > + + + + ), + }, + }, + }} + /> + + {helperText ? helperText : ""} + + + + ); +} + +export default MuiTimePicker; diff --git a/src/core/components/RahdarCode.jsx b/src/core/components/RahdarCode.jsx new file mode 100644 index 0000000..9dfeeb7 --- /dev/null +++ b/src/core/components/RahdarCode.jsx @@ -0,0 +1,90 @@ +"use client"; +import { Autocomplete, CircularProgress, TextField } from "@mui/material"; +import { useEffect, useState } from "react"; +import useRequest from "@/lib/hooks/useRequest"; +import { debounce } from "@mui/material/utils"; +import { GET_RAHDARANS_LIST_SEARCH } from "@/core/utils/routes"; + +const RahdarCode = ({ rahdarsCode, setRahdarsCode, error }) => { + const [inputValue, setInputValue] = useState(""); // مدیریت مقدار تایپ‌شده + const [options, setOptions] = useState([]); + const [loading, setLoading] = useState(false); + const requestServer = useRequest(); + + const fetchRahdarCodes = (inputValue, controller) => { + const debouncer = debounce((query) => { + if ((query || "").length < 3) { + setOptions([]); + return; + } + setLoading(true); + requestServer(`${GET_RAHDARANS_LIST_SEARCH}?code=${query}`, "get", { + requestOptions: { signal: controller.signal }, + }) + .then((response) => { + setOptions(response.data.data || []); + }) + .catch(() => { + setOptions([]); + }) + .finally(() => { + setLoading(false); + }); + }, 500); + debouncer(inputValue); + }; + + useEffect(() => { + const controller = new AbortController(); + fetchRahdarCodes(inputValue, controller); + return () => controller.abort(); + }, [inputValue]); + + return ( + { + setRahdarsCode(newValue || []); // به‌روزرسانی موارد انتخاب‌شده + }} + inputValue={inputValue} // مقدار تایپ‌شده + onInputChange={(event, newInputValue) => { + setInputValue(newInputValue || ""); // به‌روزرسانی مقدار تایپ‌شده + }} + options={options} // گزینه‌های موجود + getOptionLabel={(option) => (typeof option === "string" ? option : `${option.code} - ${option.name}`)} + isOptionEqualToValue={(option, value) => { + if (!value) return false; + if (value === "") return option.code === ""; + return option.code === (value?.code || value); + }} + loading={loading} + loadingText="درحال جستجوی کد راهدار" + noOptionsText={ + (inputValue || "").length < 3 ? "حداقل سه رقم از کد راهدار را وارد کنید" : "کد راهداری یافت نشد" + } + fullWidth + renderInput={(params) => ( + + {loading ? : null} + {params.InputProps.endAdornment} + + ), + }} + /> + )} + /> + ); +}; + +export default RahdarCode; diff --git a/src/core/components/ShowLocationMarker.jsx b/src/core/components/ShowLocationMarker.jsx new file mode 100644 index 0000000..e30a4e4 --- /dev/null +++ b/src/core/components/ShowLocationMarker.jsx @@ -0,0 +1,181 @@ +"use client"; + +import { Marker, useMap } from "react-leaflet"; +import { useEffect } from "react"; +import L from "leaflet"; +import AzmayeshActiveIcon from "@/assets/images/examine_marker_active.png"; +import { Box, FormControl, InputAdornment, InputLabel, OutlinedInput, Stack, useMediaQuery } from "@mui/material"; +import VerifiedIcon from "@mui/icons-material/Verified"; +import { useTheme } from "@emotion/react"; + +const ShowLocationMarker = ({ start_lat, start_lng, end_lat, end_lng }) => { + const map = useMap(); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + + const defaultIconSize = [35, 35]; + + const createCustomIcon = (size, iconUrl) => { + return L.icon({ + iconUrl: iconUrl, + iconSize: size, + iconAnchor: [size[0] / 2, size[1]], + popupAnchor: [0, -size[1]], + }); + }; + + useEffect(() => { + if (start_lat && end_lng) { + map.fitBounds( + [ + { lat: start_lat, lng: start_lng }, + { lat: end_lat, lng: end_lng }, + ], + { paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] } + ); + } else if (start_lat) { + map.setView({ lat: start_lat, lng: start_lng }, 15); + } + }, [start_lat, start_lng, map, end_lng, end_lat]); + + return ( + <> + {start_lat && start_lng && ( + + )} + {end_lat && end_lng && ( + + )} + + + {start_lat && start_lng && ( + + + + {end_lat && end_lng ? "طول جغرافیایی شروع" : "طول جغرافیایی"} + + + + + } + label={end_lat && end_lng ? "طول جغرافیایی شروع" : "طول جغرافیایی"} + /> + + + + {end_lat && end_lng ? "عرض جغرافیایی شروع" : "عرض جغرافیایی"} + + + + + } + label={end_lat && end_lng ? "عرض جغرافیایی شروع" : "عرض جغرافیایی"} + /> + + + )} + {end_lat && end_lng && ( + + + + طول جغرافیایی پایان + + + + + } + label="طول جغرافیایی پایان" + /> + + + + عرض جغرافیایی پایان + + + + + } + label="عرض جغرافیایی پایان" + /> + + + )} + + + + ); +}; + +export default ShowLocationMarker; diff --git a/src/core/components/Toasts/error.jsx b/src/core/components/Toasts/error.jsx index e93138b..dbc25e8 100644 --- a/src/core/components/Toasts/error.jsx +++ b/src/core/components/Toasts/error.jsx @@ -61,6 +61,35 @@ export const errorUnauthorizedToast = (toastContainer) => draggable: true, } ); +export const errorAccessDeniedToast = (message, toastContainer) => + toast.error( + () => ( + + + + + {message || "Access Denied"} + + + + ), + { + icon: false, + containerId: toastContainer, + autoClose: 3000, + hideProgressBar: true, + pauseOnHover: true, + closeOnClick: false, + draggable: true, + } + ); export const errorLogicToast = (message, toastContainer) => toast.error( diff --git a/src/core/components/UploadSystem.jsx b/src/core/components/UploadSystem.jsx new file mode 100644 index 0000000..98ac8c3 --- /dev/null +++ b/src/core/components/UploadSystem.jsx @@ -0,0 +1,94 @@ +import { Box, Button, Paper, Typography } from "@mui/material"; +import AddIcon from "@mui/icons-material/Add"; +import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; +import { useRef } from "react"; + +const UploadSystem = ({ selectedImage, handleUploadChange, imageSize, fileType, showAddIcon }) => { + const fileInputRef = useRef(null); + + const handleClick = () => { + fileInputRef.current.click(); + }; + + return ( + + {showAddIcon ? ( + + + + فرمت قابل قبول : png, jpg +
+ حداکثر 3Mb +
+
+ ) : ( + <> + {fileType && fileType.startsWith("image/") && ( + + + + )} + + )} + +
+ ); +}; +export default UploadSystem; diff --git a/src/core/utils/errorResponse.js b/src/core/utils/errorResponse.js index c71bfed..5de1e77 100644 --- a/src/core/utils/errorResponse.js +++ b/src/core/utils/errorResponse.js @@ -1,6 +1,7 @@ "use client"; import { toast } from "react-toastify"; import { + errorAccessDeniedToast, errorClientToast, errorLogicToast, errorServerToast, @@ -22,6 +23,9 @@ const errorClient = (response, notification, toastContainer, logout) => { logout(); if (notification) errorUnauthorizedToast(toastContainer); break; + case 403: + if (notification) errorAccessDeniedToast(response.data.message, toastContainer); + break; case 422: if ("type" in response.data) { if (Array.isArray(response.data.message)) { diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index 6000f64..798d28b 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -108,7 +108,7 @@ export const pageMenu = [ id: "roadItemManagmentSupervisorCartable", label: "کارتابل", type: "page", - route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/supervisor/cartable", + route: "/dashboard/road-items/supervisor", permissions: [ "show-road-item-supervise-cartable", "show-road-item-supervise-cartable-province", @@ -135,7 +135,7 @@ export const pageMenu = [ id: "roadItemManagmentOparationCartable", label: "کارتابل", type: "page", - route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/operator/cartable", + route: "/dashboard/road-items/operator", permissions: ["create-road-item"], }, ], diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index db24fd6..9548f91 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -27,7 +27,25 @@ export const DELETE_SAMPLE_LIST = api + "/api/v3/azmayesh_samples/delete"; export const DELETE_AZMAYESH_TYPE_LIST = api + "/api/v3/azmayesh_types/delete"; export const ADD_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/store"; export const UPDATE_AZMAYESH_TYPE = api + "/api/v3/azmayesh_types/update"; -export const GET_OPERATOR_LIST = "/v3/api/fake-operator-list"; -export const EXPORT_OPERATOR_LIST = "https://rms.rmto.ir/v2/road_patrols/operator/cartable/report"; -export const GET_SUPERVISOR_LIST = "/v3/api/fake-supervisor-list"; -export const EXPORT_SUPERVISOR_LIST = "https://rms.rmto.ir/v2/road_patrols/supervisor/cartable/report"; + +//road patrol +export const GET_ROAD_PATROL_OPERATOR_LIST = "/v3/api/fake-road-patrol/operator"; +export const EXPORT_ROAD_PATROL_OPERATOR_LIST = "https://rms.rmto.ir/v2/road_patrols/operator/cartable/report"; +export const GET_ROAD_PATROL_SUPERVISOR_LIST = "/v3/api/fake-road-patrol/supervisor"; +export const EXPORT_ROAD_PATROL_SUPERVISOR_LIST = "https://rms.rmto.ir/v2/road_patrols/supervisor/cartable/report"; + +// road items +export const GET_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_index"; +export const EXPORT_ROAD_ITEMS_SUPERVISOR_LIST = api + "/api/v3/road_items/supervisor_report"; +export const GET_ROAD_ITEMS_OPERATOR_LIST = api + "/api/v3/road_items/operator_index"; +export const EXPORT_ROAD_ITEMS_OPERATOR_LIST = api + "/api/v3/road_items/operator_report"; +export const GET_ROAD_ITEMS_ITEM = "https://rms.witel.ir/v2/items"; +export const GET_ROAD_ITEMS_SUB_ITEM = "https://rms.witel.ir/v2/sub_items"; +export const CREATE_ROAD_ITEMS = api + "/api/v3/road_items/store"; +export const UPDATE_ROAD_ITEMS = api + "/api/v3/road_items/update"; +export const VERIFY_BY_SUPERVISOR = api + "/api/v3/road_items/verify_by_supervisor"; +export const REJECT_BY_SUPERVISOR = api + "/api/v3/road_items/verify_by_supervisor"; +export const DELETE_BY_SUPERVISOR = api + "/api/v3/road_items/delete"; +export const RESTORE_BY_SUPERVISOR = api + "/api/v3/road_items/restore"; +export const GET_CAR_LIST_SEARCH = api + "/api/v3/cmms_machines/search"; +export const GET_RAHDARANS_LIST_SEARCH = api + "/api/v3/rahdaran/search"; diff --git a/src/lib/contexts/DataTable.js b/src/lib/contexts/DataTable.js index dcadd83..ec22020 100644 --- a/src/lib/contexts/DataTable.js +++ b/src/lib/contexts/DataTable.js @@ -17,6 +17,16 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns, const [sortData, setSortData] = useState(initialSort || []); const [initHide, setInitHide] = useState({}); const [hideData, setHideData] = useState({}); + const [isDirty, setIsDirty] = useState(false); + + useEffect(() => { + const hasConflict = + JSON.stringify(hideData) !== JSON.stringify(initHide) || + JSON.stringify(sortData) !== JSON.stringify(initSort) || + JSON.stringify(filterData) !== JSON.stringify(initFilter); + + setIsDirty(hasConflict); + }, [hideData, sortData, filterData, initHide, initSort, initFilter]); useEffect(() => { if (!settingStore) return; @@ -125,7 +135,9 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns, if (!isInitStates) return ; return ( - + {children} ); diff --git a/src/lib/hooks/useDataTable.js b/src/lib/hooks/useDataTable.js index 9bef124..9026caa 100644 --- a/src/lib/hooks/useDataTable.js +++ b/src/lib/hooks/useDataTable.js @@ -2,8 +2,9 @@ import { useContext } from "react"; import { DataTableContext } from "@/lib/contexts/DataTable"; const useTableSetting = () => { - const { filterData, setFilterData, sortData, setSortData, hideData, setHideData } = useContext(DataTableContext); - return { filterData, setFilterData, sortData, setSortData, hideData, setHideData }; + const { filterData, setFilterData, sortData, setSortData, hideData, setHideData, isDirty } = + useContext(DataTableContext); + return { filterData, setFilterData, sortData, setSortData, hideData, setHideData, isDirty }; }; export default useTableSetting; diff --git a/src/lib/hooks/useRoadItemGetISubtems.js b/src/lib/hooks/useRoadItemGetISubtems.js new file mode 100644 index 0000000..f315fd3 --- /dev/null +++ b/src/lib/hooks/useRoadItemGetISubtems.js @@ -0,0 +1,29 @@ +import { useEffect, useState } from "react"; +import { GET_ROAD_ITEMS_SUB_ITEM } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; + +const useRoadItemGetItems = (id) => { + const requestServer = useRequest(); + const [subItemsList, setSubItemsList] = useState([]); + const [loadingSubItemsList, setLoadingSubItemsList] = useState(true); + const [errorSubItemsList, setErrorSubItemsList] = useState(null); + + useEffect(() => { + const fetchSubItems = async () => { + try { + const response = await requestServer(`${GET_ROAD_ITEMS_SUB_ITEM}/${id}`); + setSubItemsList(response.data.data); + setLoadingSubItemsList(false); + } catch (e) { + setErrorSubItemsList(e); + setLoadingSubItemsList(false); + } + }; + + fetchSubItems(); + }, []); + + return { subItemsList, loadingSubItemsList, errorSubItemsList }; +}; + +export default useRoadItemGetItems; diff --git a/src/lib/hooks/useRoadItemGetItems.js b/src/lib/hooks/useRoadItemGetItems.js new file mode 100644 index 0000000..523f7c9 --- /dev/null +++ b/src/lib/hooks/useRoadItemGetItems.js @@ -0,0 +1,29 @@ +import { useEffect, useState } from "react"; +import { GET_ROAD_ITEMS_ITEM } from "@/core/utils/routes"; +import useRequest from "@/lib/hooks/useRequest"; + +const useRoadItemGetItems = () => { + const requestServer = useRequest(); + const [itemsList, setItemsList] = useState([]); + const [loadingItemsList, setLoadingItemsList] = useState(true); + const [errorItemsList, setErrorItemsList] = useState(null); + + useEffect(() => { + const fetchItems = async () => { + try { + const response = await requestServer(`${GET_ROAD_ITEMS_ITEM}`); + setItemsList(response.data.data); + setLoadingItemsList(false); + } catch (e) { + setErrorItemsList(e); + setLoadingItemsList(false); + } + }; + + fetchItems(); + }, []); + + return { itemsList, loadingItemsList, errorItemsList }; +}; + +export default useRoadItemGetItems;