complete rahdaran

This commit is contained in:
AmirHossein Mahmoodi
2025-10-04 09:02:04 +03:30
parent 55b1f45e7c
commit c9976c9f92
11 changed files with 286 additions and 21 deletions

View File

@@ -3,7 +3,7 @@ import WithPermission from "@/core/middlewares/withPermission";
export default function page() {
return (
<WithPermission permission_name={["show-rahdaran"]}>
<WithPermission permission_name={["manage-rahdaran-country","manage-rahdaran-province", "manage-rahdaran-edareh"]}>
<RahdaranTablePage />
</WithPermission>
);

View File

@@ -1,8 +1,6 @@
import { Controller } from "react-hook-form";
import MuiDatePicker from "@/core/components/MuiDatePicker";
import React from "react";
import { FormControl, FormHelperText, InputLabel, LinearProgress, MenuItem, Select, Typography } from "@mui/material";
import useProvinces from "@/lib/hooks/useProvince";
import { FormControl, FormHelperText, InputLabel, MenuItem, Select } from "@mui/material";
import { Controller } from "react-hook-form";
const SelectProvince = ({ control }) => {
const { provinces, loadingProvinces, errorProvinces } = useProvinces();

View File

@@ -0,0 +1,33 @@
import SelectBox from "@/core/components/SelectBox";
import useEdaratLists from "@/lib/hooks/useEdaratLists";
import { useEffect, useState } from "react";
const CityContent = ({ provinceID, field, fieldState: { error } }) => {
const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(provinceID);
const [prevDependency, setPrevDependency] = useState(provinceID);
useEffect(() => {
if (prevDependency === provinceID) return;
field.onChange(null);
setPrevDependency(provinceID);
}, [provinceID]);
return (
<SelectBox
value={
!provinceID ? "dependency" : errorEdaratList ? "error" : loadingEdaratList ? "loading" : field.value || ""
}
label={"اداره"}
selectors={edaratList}
schema={{ name: "name_fa", value: "id" }}
error={error}
onChange={field.onChange}
helperText={error?.message}
isLoading={loadingEdaratList}
errorEcured={errorEdaratList}
isDependency={!provinceID}
dependencyLabel={"ابتدا استان را انتخاب کنید"}
/>
);
};
export default CityContent;

View File

@@ -0,0 +1,14 @@
import { Controller, useWatch } from "react-hook-form";
import CityContent from "./CityContent";
const CityController = ({ control }) => {
const provinceID = useWatch({ control, name: "provinceId" });
return (
<Controller
name="edarehShahriId"
control={control}
render={(props) => <CityContent provinceID={provinceID} {...props} />}
/>
);
};
export default CityController;

View File

@@ -3,11 +3,23 @@ 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, TextField } from "@mui/material";
import {
Button,
DialogActions,
DialogContent,
Grid,
Stack,
TextField,
ToggleButton,
ToggleButtonGroup,
} from "@mui/material";
import { Controller, useForm } from "react-hook-form";
import { object, string } from "yup";
import { number, object, string } from "yup";
import LogesticController from "./LogesticController";
const validationSchema = object({
provinceId: number().required("لطفا استان را وارد کنید!!!"),
edarehShahriId: number().required("لطفا اداره را وارد کنید!!!"),
name: string().required("وارد کردن نام و نام خانوادگی الزامیست!"),
code: string()
.required("لطفا کد ملی را وارد کنید!!!")
@@ -37,6 +49,32 @@ const CreateFormContent = ({ setOpen, defaultValues, onSubmitBase }) => {
<StyledForm onSubmit={handleSubmit(handleOnSubmit)}>
<DialogContent dividers>
<Stack spacing={3}>
<Stack justifyContent={"center"} alignItems={"center"}>
<Controller
name="isDriver"
control={control}
render={({ field, fieldState: { error } }) => (
<ToggleButtonGroup
size="small"
value={field.value}
exclusive
onChange={(event, newAlignment) => {
if (newAlignment !== null) {
field.onChange(newAlignment);
}
}}
>
<ToggleButton sx={{ px: 3 }} value="0">
راهدار
</ToggleButton>
<ToggleButton sx={{ px: 3 }} value="1">
راننده
</ToggleButton>
</ToggleButtonGroup>
)}
/>
</Stack>
<LogesticController control={control} />
<Stack>
<Grid spacing={2}>
<Grid item xs={12} sm={6}>

View File

@@ -0,0 +1,42 @@
import { Grid, Stack } from "@mui/material";
import CityController from "./CityController";
import useProvinces from "@/lib/hooks/useProvince";
import { Controller, useWatch } from "react-hook-form";
import SelectBox from "@/core/components/SelectBox";
const LogesticController = ({ control }) => {
const { provinces, errorProvinces, loadingProvinces } = useProvinces();
const isProvince = useWatch({ control, name: "isProvince" });
return (
<>
{!isProvince ? (
<Stack>
<Controller
name="provinceId"
control={control}
render={({ field, fieldState: { error } }) => {
return (
<SelectBox
value={errorProvinces ? "error" : loadingProvinces ? "loading" : field.value || ""}
label="استان"
selectors={provinces}
schema={{ name: "name_fa", value: "id" }}
error={error}
disabled={isProvince}
onChange={field.onChange}
helperText={error?.message}
isLoading={loadingProvinces}
errorEcured={errorProvinces}
/>
);
}}
/>
</Stack>
) : null}
<Stack>
<CityController control={control} />
</Stack>
</>
);
};
export default LogesticController;

View File

@@ -1,19 +1,32 @@
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 { useAuth } from "@/lib/contexts/auth";
import { usePermissions } from "@/lib/hooks/usePermissions";
import useRequest from "@/lib/hooks/useRequest";
import CloseIcon from "@mui/icons-material/Close";
import { Dialog, DialogTitle, IconButton } from "@mui/material";
import { DialogHeader } from "next/dist/client/components/react-dev-overlay/internal/components/Dialog";
import CreateFormContent from "./CreateFormContent";
const CreateForm = ({ open, setOpen, mutate }) => {
const requestServer = useRequest({ notificationSuccess: true });
const { data: userPermissions } = usePermissions();
const { user } = useAuth();
const hasCountryPermission = userPermissions?.includes("manage-rahdaran-country");
const defaultValues = {
isDriver: "0",
isProvince: !hasCountryPermission,
provinceId: !hasCountryPermission ? user.province_id : "",
edarehShahriId: "",
name: "",
code: "",
phone_number: "",
};
const onSubmit = async (result) => {
const formData = new FormData();
formData.append("is_driver", result.isDriver);
formData.append("province_id", result.provinceId);
formData.append("edareh_shahri_id", result.edarehShahriId);
formData.append("name", result.name);
formData.append("code", result.code);
formData.append("mobile", result.phone_number);

View File

@@ -8,12 +8,18 @@ const EditController = ({ rowId, mutate, setOpen, row }) => {
const requestServer = useRequest({ notificationSuccess: true });
const defaultData = {
isDriver: `${row.original?.is_driver}` || "0",
provinceId: row.original?.province_id || "",
edarehShahriId: row.original?.edareh_shahri_id || "",
name: row.original?.name || "",
code: row.original?.code || "",
phone_number: row.original?.mobile || "",
};
const handleSubmit = async (result) => {
const formData = new FormData();
formData.append("is_driver", result.isDriver);
formData.append("province_id", result.provinceId);
formData.append("edareh_shahri_id", result.edarehShahriId);
formData.append("name", result.name);
formData.append("code", result.code);
formData.append("mobile", result.phone_number);

View File

@@ -2,13 +2,57 @@
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 { useEffect, useMemo, useState } from "react";
import RowActions from "./RowActions";
import Toolbar from "./Toolbar";
import { useAuth } from "@/lib/contexts/auth";
import useProvinces from "@/lib/hooks/useProvince";
import CustomSelectByDependency from "@/core/components/DataTable/filter/fieldsType/CustomSelectByDependency";
import useEdaratLists from "@/lib/hooks/useEdaratLists";
import { usePermissions } from "@/lib/hooks/usePermissions";
const RahdaranList = () => {
const columns = useMemo(
() => [
const { data: userPermissions } = usePermissions();
const { user } = useAuth();
const hasCountryPermission = userPermissions?.includes("manage-rahdaran-country");
const columns = useMemo(() => {
const provinceColumn = {
accessorKey: "province_id",
header: "استان",
id: "province_id",
enableColumnFilter: true,
datatype: "numeric",
filterMode: "equals",
grow: false,
size: 130,
ColumnSelectComponent: (props) => {
const { provinces, errorProvinces, loadingProvinces } = useProvinces();
const getColumnSelectOptions = useMemo(() => {
if (loadingProvinces) {
return [{ value: "loading", label: "در حال بارگذاری..." }];
}
if (errorProvinces) {
return [{ value: "error", label: "خطا در بارگذاری" }];
}
return [
{ value: "", label: "کل کشور" },
...provinces.map((province) => ({
value: province.id,
label: province.name_fa,
})),
];
}, [provinces, errorProvinces, loadingProvinces]);
return (
<CustomSelectByDependency
{...props}
value={loadingProvinces ? "loading" : props.filterParameters.value}
columnSelectOption={getColumnSelectOptions}
/>
);
},
Cell: ({ row }) => <>{row.original.province_name}</>,
};
return [
{
accessorKey: "id",
header: "کد یکتا",
@@ -21,6 +65,64 @@ const RahdaranList = () => {
grow: false,
size: 100,
},
...(hasCountryPermission ? [provinceColumn] : []),
{
header: "اداره",
id: "edareh_shahri_id",
enableColumnFilter: true,
datatype: "numeric",
filterMode: "equals",
dependencyId: hasCountryPermission ? "province_id" : null,
grow: false,
size: 120,
ColumnSelectComponent: (props) => {
const { edaratList, loadingEdaratList, errorEdaratList } = useEdaratLists(
hasCountryPermission ? props.dependencyFieldValue.value : user.province_id
);
const [prevDependency, setPrevDependency] = useState(
hasCountryPermission ? props.dependencyFieldValue.value : user.province_id
);
const getColumnSelectOptions = useMemo(() => {
if (hasCountryPermission && props.dependencyFieldValue.value === "") {
return [{ value: "empty", label: "ابتدا استان را انتخاب کنید" }];
}
if (loadingEdaratList) {
return [{ value: "loading", label: "در حال بارگذاری..." }];
}
if (errorEdaratList) {
return [{ value: "error", label: "خطا در بارگذاری" }];
}
return [
{ value: "", label: "کل ادارات" },
...edaratList.map((edare) => ({
value: edare.id,
label: edare.name_fa,
})),
];
}, [edaratList, loadingEdaratList, errorEdaratList]);
useEffect(() => {
if (hasCountryPermission) return;
if (prevDependency === props.dependencyFieldValue?.value) return;
props.handleChange({ ...props.filterParameters, value: "" });
setPrevDependency(props.dependencyFieldValue?.value);
}, [props.dependencyFieldValue?.value, hasCountryPermission]);
return (
<CustomSelectByDependency
{...props}
value={
props.dependencyFieldValue?.value === ""
? "empty"
: loadingEdaratList
? "loading"
: props.filterParameters.value
}
columnSelectOption={getColumnSelectOptions}
/>
);
},
Cell: ({ renderedCellValue, row }) => <>{row.original.edareh_shahri_name}</>,
},
{
accessorKey: "name",
header: "نام و نام خانوادگی",
@@ -54,9 +156,8 @@ const RahdaranList = () => {
grow: false,
size: 100,
},
],
[]
);
];
}, []);
return (
<>

View File

@@ -12,6 +12,7 @@ import { missionTypes } from "@/core/utils/missionTypes";
import moment from "jalali-moment";
import MachinesDialog from "./MachinesDialog";
import DriversDialog from "./DriversDialog";
import LtrTextField from "@/core/components/LtrTextField";
const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => {
const bound = useMemo(
() =>
@@ -21,14 +22,16 @@ const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => {
const requestServer = useRequest({ notificationSuccess: true });
const [machine, setMachine] = useState();
const [driver, setDriver] = useState();
const [kilometer, setKilometer] = useState();
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (_machine, _driver) => {
const handleSubmit = async (_machine, _driver, _kilometer) => {
setIsSubmitting(true);
await requestServer(`${ALLOCATE_REQUEST_MISSION}/${row.id}`, "post", {
data: {
machines: [_machine.id],
driver: _driver.id,
km: kilometer,
},
hasSidebarUpdate: true,
})
@@ -107,6 +110,23 @@ const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => {
)}
</Stack>
)}
{driver && (
<Stack direction={"row"} alignItems={"center"} spacing={1}>
<LtrTextField
autoComplete="off"
type="tel"
value={kilometer}
onChange={(e) => {
if (isNaN(Number(e.target.value))) return;
setKilometer(e.target.value);
}}
size="small"
fullWidth
label="کیلومتر خودرو"
InputLabelProps={{ shrink: true }}
/>
</Stack>
)}
</Stack>
</Paper>
<Stack spacing={1}>
@@ -188,10 +208,10 @@ const AllocationForm = ({ row, mutate, setOpenAllocationDialog }) => {
</Stack>
<Box>
<Button
onClick={() => handleSubmit(machine, driver)}
onClick={() => handleSubmit(machine, driver, kilometer)}
variant="contained"
color="primary"
disabled={!machine || !driver || isSubmitting}
disabled={!machine || !driver || !kilometer || isSubmitting}
autoFocus
>
تایید و تخصیص

View File

@@ -580,7 +580,7 @@ export const pageMenuDev = [
type: "page",
route: "/dashboard/rahdaran",
icon: <HandymanIcon sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["show-rahdaran"],
permissions: ["manage-rahdaran-country","manage-rahdaran-province", "manage-rahdaran-edareh"],
},
{
id: "adminManagement",