implemented rahdaran's page

This commit is contained in:
2025-07-13 16:51:43 +03:30
parent ac2270f99d
commit fb84d5a188
19 changed files with 540 additions and 39 deletions

View File

@@ -0,0 +1,10 @@
import RahdaranTablePage from "@/components/dashboard/rahdaran";
import WithPermission from "@/core/middlewares/withPermission";
export default function page() {
return (
<WithPermission permission_name={["show-rahdaran"]}>
<RahdaranTablePage />
</WithPermission>
);
}

View File

@@ -7,7 +7,13 @@ export const metadata = {
const Page = () => {
return (
<>
<WithPermission permission_name={["manage-request-portal-country", "manage-request-portal-province", "manage-request-portal-city"]}>
<WithPermission
permission_name={[
"manage-request-portal-country",
"manage-request-portal-province",
"manage-request-portal-city",
]}
>
<OperatorPage />
{/* <ActivityCodeLog activity_code={1152} /> */}
</WithPermission>

View File

@@ -0,0 +1,105 @@
import PersianTextField from "@/core/components/PersianTextField";
import StyledForm from "@/core/components/StyledForm";
import validateNationalCode from "@/core/utils/nationalCodeValidation";
import { yupResolver } from "@hookform/resolvers/yup";
import { Beenhere, ExitToApp } from "@mui/icons-material";
import { Button, DialogActions, DialogContent, Grid, Stack } from "@mui/material";
import { Controller, useForm } from "react-hook-form";
import { object, string } from "yup";
const validationSchema = object({
name: string().required("وارد کردن نام فارسی الزامیست!"),
code: string()
.required("لطفا کد ملی را وارد کنید!!!")
.test("max", "کد ملی باید شامل 10 رقم باشد", (value) => value.toString().length === 10)
.test("validation", "کد ملی صحیح نمی باشد", (value) => validateNationalCode(value)),
});
const CreateFormContent = ({ setOpen, defaultValues, onSubmitBase }) => {
const {
control,
handleSubmit,
formState: { isSubmitting },
} = useForm({
defaultValues,
resolver: yupResolver(validationSchema),
mode: "all",
});
const handleOnSubmit = async (data) => {
await onSubmitBase(data);
};
return (
<>
<StyledForm onSubmit={handleSubmit(handleOnSubmit)}>
<DialogContent dividers>
<Stack spacing={3}>
<Stack>
<Grid spacing={2}>
<Grid item xs={12} sm={6}>
<Controller
name="name"
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<PersianTextField
fullWidth
size="small"
label="نام فارسی"
value={value}
onChange={onChange}
placeholder="نام فارسی را وارد کنید"
error={!!error}
helperText={error ? error.message : null}
variant="outlined"
InputLabelProps={{ shrink: true }}
/>
)}
/>
</Grid>
</Grid>
</Stack>
<Stack>
<Grid spacing={2}>
<Grid item xs={12} sm={6}>
<Controller
name="code"
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<PersianTextField
fullWidth
size="small"
label="کد ملی"
value={value}
onChange={onChange}
placeholder="کد ملی را وارد کنید"
error={!!error}
helperText={error ? error.message : null}
variant="outlined"
InputLabelProps={{ shrink: true }}
/>
)}
/>
</Grid>
</Grid>
</Stack>
</Stack>
</DialogContent>
<DialogActions>
<Button
onClick={() => setOpen(false)}
variant="outlined"
color="secondary"
startIcon={<ExitToApp />}
>
بستن
</Button>
<Button variant="contained" disabled={isSubmitting} type="submit" endIcon={<Beenhere />}>
{" "}
{isSubmitting ? "در حال ثبت راهدار" : "ثبت راهدار"}{" "}
</Button>
</DialogActions>
</StyledForm>
</>
);
};
export default CreateFormContent;

View File

@@ -0,0 +1,57 @@
import { Dialog, DialogTitle, IconButton } from "@mui/material";
import CreateFormContent from "./CreateFormContent";
import CloseIcon from "@mui/icons-material/Close";
import useRequest from "@/lib/hooks/useRequest";
import { CREATE_RAHDARAN_ITEM } from "@/core/utils/routes";
import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog";
const CreateForm = ({ open, setOpen, mutate }) => {
const requestServer = useRequest({ notificationSuccess: true });
const defaultValues = {
name: "",
code: "",
};
const onSubmit = async (result) => {
const formData = new FormData();
formData.append("name", result.name);
formData.append("code", result.code);
await requestServer(CREATE_RAHDARAN_ITEM, "post", {
data: formData,
})
.then(() => {
mutate();
setOpen(false);
})
.catch(() => {});
};
return (
<Dialog open={open} fullWidth maxWidth="xs">
<IconButton
aria-label="close"
onClick={() => setOpen(false)}
sx={(theme) => ({
position: "absolute",
right: 8,
top: 8,
zIndex: 50,
color: theme.palette.grey[500],
})}
>
<CloseIcon />
</IconButton>
<DialogHeader>
<DialogTitle>ثبت راهدار جدید</DialogTitle>
</DialogHeader>
{open && (
<CreateFormContent
defaultValues={defaultValues}
onSubmitBase={onSubmit}
setOpen={setOpen}
mutate={mutate}
/>
)}
</Dialog>
);
};
export default CreateForm;

View File

@@ -0,0 +1,36 @@
import { AddCircle } from "@mui/icons-material";
import { Button, IconButton, useMediaQuery, useTheme } from "@mui/material";
import CreateForm from "./Form";
import { useState } from "react";
const CreateRahdar = ({ mutate }) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [open, setOpen] = useState(false);
const handleOpen = () => {
setOpen(true);
};
return (
<>
{isMobile ? (
<IconButton aria-label="ثبت راهدار" color="primary" onClick={handleOpen}>
<AddCircle sx={{ fontSize: "25px" }} />
</IconButton>
) : (
<Button
size={"small"}
variant="contained"
color="primary"
startIcon={<AddCircle />}
onClick={handleOpen}
>
ثبت راهدار
</Button>
)}
<CreateForm open={open} setOpen={setOpen} mutate={mutate} />
</>
);
};
export default CreateRahdar;

View File

@@ -0,0 +1,37 @@
import { UPDATE_RAHDARAN_ITEM } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
import { DialogTitle } from "@mui/material";
import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog";
import CreateFormContent from "../Create/Form/CreateFormContent";
const EditController = ({ rowId, mutate, setOpen, row }) => {
const requestServer = useRequest({ notificationSuccess: true });
const defaultData = {
name: row.original?.name || "",
code: row.original?.code || "",
};
const handleSubmit = async (result) => {
const formData = new FormData();
formData.append("name", result.name);
formData.append("code", result.code);
await requestServer(`${UPDATE_RAHDARAN_ITEM}/${rowId}`, "post", {
data: formData,
})
.then(() => {
mutate();
setOpen(false);
})
.catch(() => {});
};
return (
<>
<DialogHeader>
<DialogTitle>ویرایش راهدار</DialogTitle>
</DialogHeader>
<CreateFormContent setOpen={setOpen} defaultValues={defaultData} onSubmitBase={handleSubmit} />
</>
);
};
export default EditController;

View File

@@ -0,0 +1,51 @@
import BorderColorIcon from "@mui/icons-material/BorderColor";
import CloseIcon from "@mui/icons-material/Close";
import { Dialog, IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
import EditController from "./EditController";
const EditRahdar = ({ mutate, row, rowId }) => {
const [open, setOpen] = useState(false);
return (
<>
<Tooltip title="ویرایش راهدار" arrow placement="right">
<IconButton
color="primary"
sx={{ textTransform: "unset", alignSelf: "center" }}
onClick={() => {
setOpen(true);
}}
>
<BorderColorIcon />
</IconButton>
</Tooltip>
<Dialog
fullWidth
open={open}
maxWidth={"xs"}
PaperProps={{
sx: {
boxShadow: "rgba(60, 64, 67, 0.3) 0px 1px 16px 0px, rgba(60, 64, 67, 0.15) 0px 1px 10px 0px",
},
}}
>
<IconButton
aria-label="close"
onClick={() => setOpen(false)}
sx={(theme) => ({
position: "absolute",
right: 8,
top: 8,
zIndex: 50,
color: theme.palette.grey[500],
})}
>
<CloseIcon />
</IconButton>
{open && <EditController setOpen={setOpen} rowId={rowId} row={row} mutate={mutate} />}
</Dialog>
</>
);
};
export default EditRahdar;

View File

@@ -0,0 +1,69 @@
"use client";
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
import { GET_RAHDARAN_TABLE_LIST } from "@/core/utils/routes";
import { Box } from "@mui/material";
import { useMemo } from "react";
import RowActions from "./RowActions";
import Toolbar from "./Toolbar";
const RahdaranList = () => {
const columns = useMemo(
() => [
{
accessorKey: "id",
header: "کد یکتا",
id: "id",
enableColumnFilter: true,
datatype: "text",
filterMode: "equals",
sortDescFirst: false,
columnFilterModeOptions: ["equals", "contains"],
grow: false,
size: 100,
},
{
accessorKey: "name",
header: "نام و نام خانوادگی",
id: "name",
enableColumnFilter: true,
datatype: "text",
filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
grow: false,
size: 100,
},
{
accessorKey: "code",
header: "کد ملی",
id: "code",
enableColumnFilter: true,
datatype: "text",
filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
grow: false,
size: 100,
},
],
[]
);
return (
<>
<Box sx={{ p: 1, border: 1, borderColor: "divider", borderRadius: 1 }}>
<DataTableWithAuth
need_filter={true}
columns={columns}
sorting={[{ id: "id", desc: true }]}
table_url={GET_RAHDARAN_TABLE_LIST}
page_name={"permissionTablePage"}
table_name={"permissionTableList"}
TableToolbar={Toolbar}
enableRowActions
positionActionsColumn={"last"}
RowActions={RowActions}
/>
</Box>
</>
);
};
export default RahdaranList;

View File

@@ -0,0 +1,39 @@
import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
import React, { useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import { DELETE_RAHDARAN_ITEM } from "@/core/utils/routes";
const DeleteContent = ({ rowId, mutate, setOpenDeleteDialog }) => {
const [submitting, setSubmitting] = useState(false);
const requestServer = useRequest({ notificationSuccess: true });
const handleClick = () => {
setSubmitting(true);
requestServer(`${DELETE_RAHDARAN_ITEM}/${rowId}`, "delete")
.then(() => {
mutate();
setSubmitting(false);
setOpenDeleteDialog(false);
})
.catch(() => {
setSubmitting(false);
});
};
return (
<>
<DialogContent>
<Stack alignItems="center" spacing={2}>
<Typography mt={2}>آیا از حذف راهدار اطمینان دارید؟</Typography>
</Stack>
</DialogContent>
<DialogActions sx={{ justifyContent: "center", paddingBottom: 2, width: "100%" }}>
<Button onClick={() => setOpenDeleteDialog(false)} variant="outlined" color="secondary">
خیر !
</Button>
<Button onClick={handleClick} variant="contained" color="error" disabled={submitting}>
{submitting ? "درحال حذف..." : "بله اطمینان دارم"}
</Button>
</DialogActions>
</>
);
};
export default DeleteContent;

View File

@@ -0,0 +1,34 @@
import DeleteIcon from "@mui/icons-material/Delete";
import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
import DeleteContent from "./DeleteContent";
const DeleteDialog = ({ rowId, mutate }) => {
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
return (
<>
<Tooltip title="حذف">
<IconButton color="error" onClick={() => setOpenDeleteDialog(true)}>
<DeleteIcon />
</IconButton>
</Tooltip>
<Dialog
open={openDeleteDialog}
onClose={() => 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"}
>
<DialogTitle>حذف راهدار</DialogTitle>
{openDeleteDialog && (
<DeleteContent rowId={rowId} mutate={mutate} setOpenDeleteDialog={setOpenDeleteDialog} />
)}
</Dialog>
</>
);
};
export default DeleteDialog;

View File

@@ -0,0 +1,12 @@
import { Box } from "@mui/material";
import EditRahdar from "../Actions/Edit";
import DeleteDialog from "./DeleteDialog";
const RowActions = ({ row, mutate }) => {
return (
<Box sx={{ display: "flex", gap: 1, alignItems: "center" }}>
<EditRahdar row={row} mutate={mutate} rowId={row.getValue("id")} />
<DeleteDialog mutate={mutate} rowId={row.getValue("id")} />
</Box>
);
};
export default RowActions;

View File

@@ -0,0 +1,9 @@
import CreateRahdar from "./Actions/Create";
const Toolbar = ({ mutate }) => {
return (
<>
<CreateRahdar mutate={mutate} />
</>
);
};
export default Toolbar;

View File

@@ -0,0 +1,13 @@
import PageTitle from "@/core/components/PageTitle";
import { Stack } from "@mui/material";
import RahdaranList from "./RahdaranList";
const RahdaranTablePage = () => {
return (
<Stack spacing={1}>
<PageTitle title={"راهداران"} />
<RahdaranList />
</Stack>
);
};
export default RahdaranTablePage;

View File

@@ -32,13 +32,13 @@ const Create = ({ mutate }) => {
end_point: result.end_point,
...(result.type == 1
? {
start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`,
end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`,
}
start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`,
end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`,
}
: {
start_date: result.start_date,
end_date: result.end_date,
}),
start_date: result.start_date,
end_date: result.end_date,
}),
},
hasSidebarUpdate: true,
})
@@ -46,7 +46,7 @@ const Create = ({ mutate }) => {
mutate();
setOpen(false);
})
.catch((error) => { })
.catch((error) => {})
.finally(() => {
setSubmitting(false);
});

View File

@@ -40,13 +40,13 @@ const EditController = ({ row, mutate, openEditDialog, setOpenEditDialog }) => {
end_point: result.end_point,
...(result.type == 1
? {
start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`,
end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`,
}
start_date: `${result.start_date} ${moment(result.start_time).format("HH:mm")}`,
end_date: `${result.end_date} ${moment(result.end_time).format("HH:mm")}`,
}
: {
start_date: result.start_date,
end_date: result.end_date,
}),
start_date: result.start_date,
end_date: result.end_date,
}),
},
hasSidebarUpdate: true,
notificationSuccess: true,
@@ -55,7 +55,7 @@ const EditController = ({ row, mutate, openEditDialog, setOpenEditDialog }) => {
mutate();
setOpenEditDialog(false);
})
.catch((error) => { })
.catch((error) => {})
.finally(() => {
setSubmitting(false);
});

View File

@@ -13,7 +13,7 @@ function filterFalseValues(obj) {
for (const [key, value] of Object.entries(obj)) {
if (value === false) {
result[key] = false;
} else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
} else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
const nested = filterFalseValues(value);
if (Object.keys(nested).length > 0) {
result[key] = nested;

View File

@@ -103,7 +103,11 @@ export const pageMenu = [
label: "کارتابل",
type: "page",
route: "/dashboard/road-missions/operator",
permissions: ["manage-request-portal-country", "manage-request-portal-province", "manage-request-portal-city"],
permissions: [
"manage-request-portal-country",
"manage-request-portal-province",
"manage-request-portal-city",
],
},
{
id: "roadMissionsManagemnetNaqlie",

View File

@@ -1,28 +1,29 @@
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
import GroupsIcon from "@mui/icons-material/Groups";
import { Mediation } from "@mui/icons-material";
import AccountTreeIcon from "@mui/icons-material/AccountTree";
import FactCheckIcon from "@mui/icons-material/FactCheck";
import FireTruckIcon from "@mui/icons-material/FireTruck";
import RouteIcon from "@mui/icons-material/Route";
import DoorbellIcon from "@mui/icons-material/Doorbell";
import ElectricBoltIcon from "@mui/icons-material/ElectricBolt";
import CottageIcon from "@mui/icons-material/Cottage";
import GppMaybeIcon from "@mui/icons-material/GppMaybe";
import GavelIcon from "@mui/icons-material/Gavel";
import BallotIcon from "@mui/icons-material/Ballot";
import AssistantIcon from "@mui/icons-material/Assistant";
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettings";
import MapIcon from "@mui/icons-material/Map";
import SpeedIcon from "@mui/icons-material/Speed";
import EngineeringIcon from "@mui/icons-material/Engineering";
import AssessmentIcon from "@mui/icons-material/Assessment";
import AssignmentLateIcon from "@mui/icons-material/AssignmentLate";
import LockPersonIcon from "@mui/icons-material/LockPerson";
import LineAxisIcon from "@mui/icons-material/LineAxis";
import ScienceIcon from "@mui/icons-material/Science";
import DriveEtaIcon from "@mui/icons-material/DriveEta";
import AssistantIcon from "@mui/icons-material/Assistant";
import BallotIcon from "@mui/icons-material/Ballot";
import BiotechIcon from "@mui/icons-material/Biotech";
import { Mediation } from "@mui/icons-material";
import CottageIcon from "@mui/icons-material/Cottage";
import DoorbellIcon from "@mui/icons-material/Doorbell";
import DriveEtaIcon from "@mui/icons-material/DriveEta";
import ElectricBoltIcon from "@mui/icons-material/ElectricBolt";
import EngineeringIcon from "@mui/icons-material/Engineering";
import FactCheckIcon from "@mui/icons-material/FactCheck";
import FireTruckIcon from "@mui/icons-material/FireTruck";
import GavelIcon from "@mui/icons-material/Gavel";
import GppMaybeIcon from "@mui/icons-material/GppMaybe";
import GroupsIcon from "@mui/icons-material/Groups";
import HandymanIcon from "@mui/icons-material/Handyman";
import LineAxisIcon from "@mui/icons-material/LineAxis";
import LockPersonIcon from "@mui/icons-material/LockPerson";
import MapIcon from "@mui/icons-material/Map";
import RouteIcon from "@mui/icons-material/Route";
import ScienceIcon from "@mui/icons-material/Science";
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
import SpeedIcon from "@mui/icons-material/Speed";
export const pageMenuDev = [
{
@@ -102,7 +103,11 @@ export const pageMenuDev = [
label: "کارتابل",
type: "page",
route: "/dashboard/road-missions/operator",
permissions: ["manage-request-portal-country", "manage-request-portal-province", "manage-request-portal-city"],
permissions: [
"manage-request-portal-country",
"manage-request-portal-province",
"manage-request-portal-city",
],
},
{
id: "roadMissionsManagemnetNaqlie",
@@ -535,6 +540,14 @@ export const pageMenuDev = [
icon: <BiotechIcon sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["azmayesh-type-management"],
},
{
id: "rahdaran",
label: "راهداران",
type: "page",
route: "/dashboard/rahdaran",
icon: <HandymanIcon sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"],
},
{
id: "adminManagement",
label: "مدیریت سامانه",
@@ -572,7 +585,7 @@ export const pageMenuDev = [
type: "page",
route: "/dashboard/car-details",
icon: <DriveEtaIcon sx={{ width: "inherit", height: "inherit" }} />,
permissions: [""],
permissions: ["show-rahdaran"],
},
],
},

View File

@@ -191,3 +191,9 @@ export const START_MISSION = api + "/api/v3/missions/control_unit/start";
export const FINISH_MISSION = api + "/api/v3/missions/control_unit/finish";
export const GET_MACHINES_TABLE_LIST = api + "/api/v3/cmms_machines";
// rahdaran
export const GET_RAHDARAN_TABLE_LIST = api + "/api/v3/rahdaran";
export const DELETE_RAHDARAN_ITEM = api + "/api/v3/rahdaran";
export const UPDATE_RAHDARAN_ITEM = api + "/api/v3/rahdaran";
export const CREATE_RAHDARAN_ITEM = api + "/api/v3/rahdaran";