diff --git a/src/app/(withAuth)/dashboard/fast-react/supervisor/loading.jsx b/src/app/(withAuth)/dashboard/fast-react/supervisor/loading.jsx new file mode 100644 index 0000000..4535a8d --- /dev/null +++ b/src/app/(withAuth)/dashboard/fast-react/supervisor/loading.jsx @@ -0,0 +1,7 @@ +"use client"; +import PageLoading from "@/core/components/PageLoading"; + +const Loading = () => { + return ; +}; +export default Loading; diff --git a/src/app/(withAuth)/dashboard/fast-react/supervisor/page.js b/src/app/(withAuth)/dashboard/fast-react/supervisor/page.js new file mode 100644 index 0000000..820c1af --- /dev/null +++ b/src/app/(withAuth)/dashboard/fast-react/supervisor/page.js @@ -0,0 +1,13 @@ +import WithPermission from "@/core/middlewares/withPermission"; +import SupervisorPage from "@/components/dashboard/fastReact/supervisor"; +import ActivityCodeLog from "@/core/components/ActivityCodeLog"; +const Page = () => { + return ( + + + + + ); +}; + +export default Page; diff --git a/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx new file mode 100644 index 0000000..158d879 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/ComplaintListTable.jsx @@ -0,0 +1,271 @@ +import { useEffect, useMemo, useState } from "react"; +import { Box, Button, Dialog, DialogActions, Stack, Typography } from "@mui/material"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import Toolbar from "./Toolbar"; +import RowActions from "./RowActions"; +import { GET_FAST_REACT_COMPLAINTS } from "@/core/utils/routes"; +import useProvinces from "@/lib/hooks/useProvince"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import { useAuth } from "@/lib/contexts/auth"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import DescriptionForm from "./RowActions/DescriptionDialog"; +import LocationForm from "./RowActions/LocationDialog"; +import moment from "jalali-moment"; + +const ComplaintListTable = ({ open, setOpen, mutate }) => { + const { data: userPermissions } = usePermissions(); + const hasCountryPermission = userPermissions?.includes("show-fast-react"); + const { user } = useAuth(); + const columns = useMemo(() => { + const dynamicColumns = { + header: "استان", + id: "road_observeds__province_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 130, + ColumnSelectComponent: (props) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const getColumnSelectOptions = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.province_fa}, + }; + return [ + { + accessorKey: "id", + header: "کد یکتا", + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "fk_RegisteredEventMessage", + header: "کد سوانح", + id: "fk_RegisteredEventMessage", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + ...(hasCountryPermission ? [dynamicColumns] : []), + { + header: "اداره", + id: "edarat_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + dependencyId: hasCountryPermission ? "road_observeds__province_id" : null, + grow: false, + size: 120, + ColumnSelectComponent: (props) => { + const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id + ); + const [prevDependency, setPrevDependency] = useState( + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id + ); + + const getColumnSelectOptions = useMemo(() => { + if (hasCountryPermission && props.dependencyFieldValue.value === "") { + return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; + } + if (loadingEdaratList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorEdaratList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل ادارات" }, + ...edaratList.map((edare) => ({ + value: edare.id, + label: edare.name_fa, + })), + ]; + }, [edaratList, loadingEdaratList, errorEdaratList]); + useEffect(() => { + if (hasCountryPermission) return; + if (prevDependency === props.dependencyFieldValue?.value) return; + props.handleChange({ ...props.filterParameters, value: "" }); + setPrevDependency(props.dependencyFieldValue?.value); + }, [props.dependencyFieldValue?.value, hasCountryPermission]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.city_fa}, + }, + { + accessorKey: "Title", + header: "موضوع گزارش", + id: "Title", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "FeatureTypeTitle", + header: "نوع گزارش", + id: "FeatureTypeTitle", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Description", + header: "توضیح گزارش", + id: "Description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون توضیحات; + }, + }, + { + header: "موقعیت", + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + return ( + + + + ); + }, + }, + { + accessorKey: "MobileForSendEventSms", + header: "شماره تماس گیرنده", + id: "MobileForSendEventSms", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "created_at", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + ]; + }, []); + + return ( + setOpen(false)} open={open} fullWidth maxWidth={"md"}> + {open && ( + + + + )} + + + + + ); +}; +export default ComplaintListTable; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/DescriptionDialog/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/DescriptionDialog/index.jsx new file mode 100644 index 0000000..4d80ac4 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/RowActions/DescriptionDialog/index.jsx @@ -0,0 +1,50 @@ +import { Dialog, DialogContent, DialogTitle, IconButton, Tooltip, Typography, Card, CardContent } from "@mui/material"; +import { useState } from "react"; +import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye"; +import CloseIcon from "@mui/icons-material/Close"; + +const DescriptionForm = ({ description, title, icon: IconComponent = RemoveRedEyeIcon }) => { + const [openOfficerDescriptionDialog, setOpenOfficerDescriptionDialog] = useState(false); + + return ( + <> + + setOpenOfficerDescriptionDialog(true)}> + + + + setOpenOfficerDescriptionDialog(false)} + PaperProps={{ + sx: { + transition: "all .3s", + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + borderRadius: 2, + padding: 1, + }, + }} + maxWidth="sm" + fullWidth + dir="rtl" + > + + + {title} + + setOpenOfficerDescriptionDialog(false)} size="small"> + + + + + + + {description || "هیچ توضیحی موجود نیست"} + + + + + + ); +}; +export default DescriptionForm; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/LocationFormContent.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/LocationFormContent.jsx new file mode 100644 index 0000000..94f091d --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/LocationFormContent.jsx @@ -0,0 +1,30 @@ +"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 }) => { + return ( + <> + + + + + + + + + + + + ); +}; +export default LocationFormContent; diff --git a/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/index.jsx new file mode 100644 index 0000000..09df6ad --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/RowActions/LocationDialog/index.jsx @@ -0,0 +1,36 @@ +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 }) => { + 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/fastReact/complaintList/RowActions/index.jsx b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx new file mode 100644 index 0000000..9fb298d --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/RowActions/index.jsx @@ -0,0 +1,6 @@ +import { Box } from "@mui/material"; + +const RowActions = ({ row, mutate }) => { + return ; +}; +export default RowActions; diff --git a/src/components/dashboard/fastReact/complaintList/Toolbar.jsx b/src/components/dashboard/fastReact/complaintList/Toolbar.jsx new file mode 100644 index 0000000..1f291d1 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/Toolbar.jsx @@ -0,0 +1,6 @@ +import { Stack } from "@mui/material"; + +const Toolbar = ({ table, filterData, mutate }) => { + return ; +}; +export default Toolbar; diff --git a/src/components/dashboard/fastReact/complaintList/index.jsx b/src/components/dashboard/fastReact/complaintList/index.jsx new file mode 100644 index 0000000..5c3edc6 --- /dev/null +++ b/src/components/dashboard/fastReact/complaintList/index.jsx @@ -0,0 +1,36 @@ +import { Button, IconButton, useMediaQuery } from "@mui/material"; +import ChecklistIcon from "@mui/icons-material/Checklist"; +import { useTheme } from "@emotion/react"; +import { useState } from "react"; +import ComplaintListTable from "./ComplaintListTable"; + +const ComplaintList = ({ mutate }) => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const [open, setOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + return ( + <> + {isMobile ? ( + + + + ) : ( + + )} + + + ); +}; +export default ComplaintList; diff --git a/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx b/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx new file mode 100644 index 0000000..ae703fa --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/ExcelPrint/index.jsx @@ -0,0 +1,75 @@ +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"; +import DataTableFilterDataStructure from "@/core/utils/DataTableFilterDataStructure"; +import isArrayEmpty from "@/core/utils/isArrayEmpty"; + +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 && (!Array.isArray(filter.value) || filter.value.some((item) => item.trim() !== ""))) { + acc.push({ id: key, value: filter.value }); + } + return acc; + }, []); + + const filterParams = useMemo(() => { + const params = new URLSearchParams(); + if (activeFilters.length > 0) { + activeFilters.map((filter, index) => { + const filteredFilterData = DataTableFilterDataStructure(filterData, isArrayEmpty); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); + }); + } else { + params.set("filters", JSON.stringify([])); + } + 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/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/ConfirmContent.jsx new file mode 100644 index 0000000..5944db3 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/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_FAST_REACT_SUPERVISOR } from "@/core/utils/routes"; + +const ConfirmContent = ({ rowId, mutate, setOpenConfirmDialog }) => { + const requestServer = useRequest({ notificationSuccess: true }); + + const { + register, + handleSubmit, + formState: { isSubmitting }, + } = 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_FAST_REACT_SUPERVISOR}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenConfirmDialog(false); + }) + .catch(() => {}); + }; + + return ( + <> + + + + + + + + + + + + + ); +}; +export default ConfirmContent; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/index.jsx new file mode 100644 index 0000000..10e6048 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/ConfirmDialog/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/fastReact/supervisor/RowActions/DescriptionDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/DescriptionDialog/index.jsx new file mode 100644 index 0000000..4d80ac4 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/DescriptionDialog/index.jsx @@ -0,0 +1,50 @@ +import { Dialog, DialogContent, DialogTitle, IconButton, Tooltip, Typography, Card, CardContent } from "@mui/material"; +import { useState } from "react"; +import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye"; +import CloseIcon from "@mui/icons-material/Close"; + +const DescriptionForm = ({ description, title, icon: IconComponent = RemoveRedEyeIcon }) => { + const [openOfficerDescriptionDialog, setOpenOfficerDescriptionDialog] = useState(false); + + return ( + <> + + setOpenOfficerDescriptionDialog(true)}> + + + + setOpenOfficerDescriptionDialog(false)} + PaperProps={{ + sx: { + transition: "all .3s", + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + borderRadius: 2, + padding: 1, + }, + }} + maxWidth="sm" + fullWidth + dir="rtl" + > + + + {title} + + setOpenOfficerDescriptionDialog(false)} size="small"> + + + + + + + {description || "هیچ توضیحی موجود نیست"} + + + + + + ); +}; +export default DescriptionForm; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx new file mode 100644 index 0000000..9aaa92e --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/ImageFormContent.jsx @@ -0,0 +1,44 @@ +import { Box, DialogContent, Paper, Stack, Typography, useTheme } from "@mui/material"; +import Image from "next/image"; + +const ImageFormContent = ({ image, title }) => { + const theme = useTheme(); + return ( + + + {title} + + + Image + + + ); +}; +export default ImageFormContent; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/index.jsx new file mode 100644 index 0000000..de3b76a --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/ImageDialog/index.jsx @@ -0,0 +1,46 @@ +import PhotoLibraryIcon from "@mui/icons-material/PhotoLibrary"; +import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ImageFormContent from "./ImageFormContent"; + +const ImageDialog = ({ image_before, image_after }) => { + const [openImageDialog, setOpenImageDialog] = useState(false); + + return ( + <> + + setOpenImageDialog(true)}> + + + + setOpenImageDialog(false)} + PaperProps={{ + sx: { + boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px", + }, + }} + maxWidth={"sm"} + scroll="paper" + > + تصاویر + + {openImageDialog && ( + <> + + + + )} + + + + + + + ); +}; +export default ImageDialog; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/LocationFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/LocationFormContent.jsx new file mode 100644 index 0000000..94f091d --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/LocationFormContent.jsx @@ -0,0 +1,30 @@ +"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 }) => { + return ( + <> + + + + + + + + + + + + ); +}; +export default LocationFormContent; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/index.jsx new file mode 100644 index 0000000..09df6ad --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/LocationDialog/index.jsx @@ -0,0 +1,36 @@ +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 }) => { + 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/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx new file mode 100644 index 0000000..5c51886 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/RejectFormContent.jsx @@ -0,0 +1,83 @@ +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_FAST_REACT_SUPERVISOR } from "@/core/utils/routes"; + +const RejectContent = ({ rowId, mutate, setOpenRejectDialog }) => { + const requestServer = useRequest({ notificationSuccess: 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_FAST_REACT_SUPERVISOR}/${rowId}`, "post", { + data: formData, + }) + .then(() => { + mutate(); + setOpenRejectDialog(false); + }) + .catch(() => {}) + .finally(() => { + reset(); + }); + }; + + return ( + <> + + + + + + + + + + + + + ); +}; +export default RejectContent; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/index.jsx new file mode 100644 index 0000000..5d754a5 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/RejectDialog/index.jsx @@ -0,0 +1,29 @@ +import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material"; +import { useState } from "react"; +import ClearIcon from "@mui/icons-material/Clear"; +import RejectContent from "./RejectFormContent"; +const RejectForm = ({ rowId, mutate }) => { + const [openRejectDialog, setOpenRejectDialog] = useState(false); + return ( + <> + + setOpenRejectDialog(true)}> + + + + + عدم تایید اقدام + + + + ); +}; +export default RejectForm; diff --git a/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx new file mode 100644 index 0000000..ad2e492 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/RowActions/index.jsx @@ -0,0 +1,22 @@ +import {Box} from "@mui/material"; +import ConfirmForm from "./ConfirmDialog"; +import RejectForm from "./RejectDialog"; +import {usePermissions} from "@/lib/hooks/usePermissions"; + +const RowActions = ({row, mutate}) => { + const {data: userPermissions} = usePermissions(); + const hasSupervisePermission = userPermissions.some((item) => + ["supervise-fast-react", "supervise-fast-react-province"].includes(item) + ); + return ( + + {hasSupervisePermission && row.original?.status === 0 && ( + + )} + {hasSupervisePermission && row.original?.status === 0 &&( + + )} + + ); +}; +export default RowActions; diff --git a/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx new file mode 100644 index 0000000..cb13855 --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/SupervisorList.jsx @@ -0,0 +1,405 @@ +import { useEffect, useMemo, useState } from "react"; +import { Box, Stack, Typography } from "@mui/material"; +import DataTableWithAuth from "@/core/components/DataTableWithAuth"; +import Toolbar from "./Toolbar"; +import moment from "jalali-moment"; +import RowActions from "./RowActions"; +import { GET_FAST_REACT_SUPERVISOR } from "@/core/utils/routes"; +import useProvinces from "@/lib/hooks/useProvince"; +import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency"; +import { usePermissions } from "@/lib/hooks/usePermissions"; +import { useAuth } from "@/lib/contexts/auth"; +import useEdaratLists from "@/lib/hooks/useEdaratLists"; +import DescriptionForm from "./RowActions/DescriptionDialog"; +import LocationForm from "./RowActions/LocationDialog"; +import EngineeringIcon from "@mui/icons-material/Engineering"; +import ThreePIcon from "@mui/icons-material/ThreeP"; +import ImageDialog from "./RowActions/ImageDialog"; + +const SupervisorList = () => { + const { data: userPermissions } = usePermissions(); + const hasCountryPermission = userPermissions?.includes("show-fast-react"); + const { user } = useAuth(); + const statusOptions = [ + { value: "", label: "همه وضعیت ها" }, + { value: 0, label: "درحال بررسی" }, + { value: 1, label: "تایید شده" }, + { value: 2, label: "عدم تایید" }, + ]; + + const columns = useMemo(() => { + const dynamicColumns = { + header: "استان", + id: "road_observeds__province_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + grow: false, + size: 130, + ColumnSelectComponent: (props) => { + const { provinces, errorProvinces, loadingProvinces } = useProvinces(); + const getColumnSelectOptions = useMemo(() => { + if (loadingProvinces) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorProvinces) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل کشور" }, + ...provinces.map((province) => ({ + value: province.id, + label: province.name_fa, + })), + ]; + }, [provinces, errorProvinces, loadingProvinces]); + return ( + + ); + }, + Cell: ({ row }) => <>{row.original.province_fa}, + }; + return [ + { + accessorKey: "id", + header: "کد یکتا", + id: "id", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "fk_RegisteredEventMessage", + header: "کد سوانح", + id: "fk_RegisteredEventMessage", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + ...(hasCountryPermission ? [dynamicColumns] : []), + { + header: "اداره", + id: "edarat_id", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + dependencyId: hasCountryPermission ? "road_observeds__province_id" : null, + grow: false, + size: 120, + ColumnSelectComponent: (props) => { + const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists( + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id + ); + const [prevDependency, setPrevDependency] = useState( + hasCountryPermission ? props.dependencyFieldValue.value : user.province_id + ); + + const getColumnSelectOptions = useMemo(() => { + if (hasCountryPermission && props.dependencyFieldValue.value === "") { + return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }]; + } + if (loadingEdaratList) { + return [{ value: "loading", label: "در حال بارگذاری..." }]; + } + if (errorEdaratList) { + return [{ value: "error", label: "خطا در بارگذاری" }]; + } + return [ + { value: "", label: "کل ادارات" }, + ...edaratList.map((edare) => ({ + value: edare.id, + label: edare.name_fa, + })), + ]; + }, [edaratList, loadingEdaratList, errorEdaratList]); + useEffect(() => { + if (hasCountryPermission) return; + if (prevDependency === props.dependencyFieldValue?.value) return; + props.handleChange({ ...props.filterParameters, value: "" }); + setPrevDependency(props.dependencyFieldValue?.value); + }, [props.dependencyFieldValue?.value, hasCountryPermission]); + return ( + + ); + }, + Cell: ({ renderedCellValue, row }) => <>{row.original.city_fa}, + }, + { + accessorKey: "Title", + header: "موضوع گزارش", + id: "Title", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "FeatureTypeTitle", + header: "نوع گزارش", + id: "FeatureTypeTitle", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorKey: "Description", + header: "توضیح گزارش", + id: "Description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون توضیحات; + }, + }, + { + header: "موقعیت", + id: "location", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + return ( + + + + ); + }, + }, + { + header: "تصاویر", + id: "images", + enableColumnFilter: false, + enableSorting: false, + datatype: "array", + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ row }) => { + if (!row.original?.image_before && !row.original?.image_after) return <>بدون تصویر; + return ( + + {row.original?.image_before && row.original?.image_after && ( + + )} + + ); + }, + }, + { + accessorKey: "MobileForSendEventSms", + header: "شماره تماس گیرنده", + id: "MobileForSendEventSms", + enableColumnFilter: true, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + }, + { + accessorFn: (row) => moment(row.created_at).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ ثبت", + id: "created_at", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorFn: (row) => moment(row.rms_last_activity).locale("fa").format("HH:mm | yyyy/MM/DD"), + header: "تاریخ اقدام", + id: "rms_last_activity", + enableColumnFilter: true, + datatype: "date", + filterMode: "between", + grow: false, + size: 100, + }, + { + accessorKey: "rms_description", + header: "توضیح مامور", + id: "rms_description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون توضیحات; + }, + }, + { + accessorKey: "supervisor_description", + header: "توضیح کارشناس", + id: "supervisor_description", + enableColumnFilter: false, + datatype: "text", + filterMode: "equals", + sortDescFirst: false, + columnFilterModeOptions: ["equals", "contains"], + grow: false, + size: 100, + muiTableBodyCellProps: { + sx: { + borderLeft: "1px solid #e1e1e1", + py: 0, + "&:first-of-type": { + borderLeft: "unset", + }, + }, + }, + Cell: ({ renderedCellValue }) => { + if (renderedCellValue) { + return ( + + + + ); + } + return بدون توضیحات; + }, + }, + { + accessorKey: "status", + header: "وضعیت", + id: "status", + enableColumnFilter: true, + datatype: "numeric", + filterMode: "equals", + sortDescFirst: true, + grow: false, + size: 100, + columnSelectOption: () => { + return statusOptions.map((status) => ({ + value: status.value, + label: status.label, + })); + }, + Cell: ({ row }) => <>{row.original.status_fa}, + }, + ]; + }, []); + + return ( + <> + + + + + ); +}; +export default SupervisorList; diff --git a/src/components/dashboard/fastReact/supervisor/Toolbar.jsx b/src/components/dashboard/fastReact/supervisor/Toolbar.jsx new file mode 100644 index 0000000..479ae4e --- /dev/null +++ b/src/components/dashboard/fastReact/supervisor/Toolbar.jsx @@ -0,0 +1,13 @@ +import PrintExcel from "./ExcelPrint"; +import ComplaintList from "../complaintList"; +import { Stack } from "@mui/material"; + +const Toolbar = ({ table, filterData, mutate }) => { + return ( + + + + + ); +}; +export default Toolbar; diff --git a/src/components/dashboard/fastReact/supervisor/index.jsx b/src/components/dashboard/fastReact/supervisor/index.jsx new file mode 100644 index 0000000..3fa490b --- /dev/null +++ b/src/components/dashboard/fastReact/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/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx index 1e20921..14b3db3 100644 --- a/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx +++ b/src/components/dashboard/roadItems/operator/RowActions/LocationForm/LocationFormContent.jsx @@ -32,5 +32,4 @@ const LocationFormContent = ({ setOpenLocationDialog, start_lat, start_lng, end_ ); }; - export default LocationFormContent; diff --git a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx index 314004c..f4b42cf 100644 --- a/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx +++ b/src/components/dashboard/roadItems/supervisor/SupervisorList.jsx @@ -78,7 +78,7 @@ const SupervisorList = () => { }, ...(hasCountryPermission ? [dynamicColumns] : []), { - header: "اداره", // Office + header: "اداره", id: "edarat_id", enableColumnFilter: true, datatype: "numeric", diff --git a/src/core/utils/pageMenu.js b/src/core/utils/pageMenu.js index 94569b3..2eaf65b 100644 --- a/src/core/utils/pageMenu.js +++ b/src/core/utils/pageMenu.js @@ -369,8 +369,8 @@ export const pageMenu = [ id: "roadObservationsManagmentSupervisorCartable", label: "کارتابل", type: "page", - route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations/supervisor/cartable", - permissions: ["supervise-fast-react", "supervise-fast-react-province"], + route: "/dashboard/fast-react/supervisor", + permissions: ["show-fast-react", "show-fast-react-province"], }, ], }, diff --git a/src/core/utils/routes.js b/src/core/utils/routes.js index 06c6212..21e97b8 100644 --- a/src/core/utils/routes.js +++ b/src/core/utils/routes.js @@ -110,3 +110,9 @@ export const EXPORT_PROVINCE_RECEIPT_REPORT = api + "/api/v3/receipt_reports/pro // activity code log export const ACTIVITY_LOG = api + "/api/v3/profile/add_activity"; + +// fast react +export const GET_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/supervisor_index"; +export const GET_FAST_REACT_COMPLAINTS = api + "/api/v3/road_observations/complaints_index"; +export const VERIFY_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/verify"; +export const REJECT_BY_FAST_REACT_SUPERVISOR = api + "/api/v3/road_observations/verify";