Feature/amiriis missions

This commit is contained in:
AmirHossein Mahmoodi
2025-06-25 07:41:03 +00:00
parent 9ae5b8934c
commit 12d6d08bef
734 changed files with 48930 additions and 46048 deletions

View File

@@ -0,0 +1,55 @@
import MapLayer from "@/core/components/MapLayer";
import { Close, RemoveRedEye } from "@mui/icons-material";
import { Box, Dialog, DialogContent, DialogTitle, IconButton, Tooltip, Typography } from "@mui/material";
import { useMemo, useState } from "react";
import ShowBound from "../showBound";
import L from "leaflet";
const ShowArea = ({ area }) => {
const bound = useMemo(() => L.polygon([...area]), [area]);
const [openAreaDialog, setOpenAreaDialog] = useState(false);
return (
<>
<Tooltip title="محدوده">
<IconButton color="primary" onClick={() => setOpenAreaDialog(true)}>
<RemoveRedEye />
</IconButton>
</Tooltip>
<Dialog
open={openAreaDialog}
onClose={() => setOpenAreaDialog(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"
>
<DialogTitle sx={{ display: "flex", justifyContent: "space-between", padding: "16px 24px" }}>
<Typography variant="body1" sx={{ fontWeight: "bold", fontSize: "large" }}>
محدوده
</Typography>
<IconButton onClick={() => setOpenAreaDialog(false)} size="small">
<Close />
</IconButton>
</DialogTitle>
<DialogContent>
<Box sx={{ flex: 1 }}>
<Box sx={{ width: "100%", height: "400px" }}>
<MapLayer style={{ borderRadius: "4px" }}>
<ShowBound bound={bound} />
</MapLayer>
</Box>
</Box>
</DialogContent>
</Dialog>
</>
);
};
export default ShowArea;

View File

@@ -0,0 +1,29 @@
import { useEffect, useRef } from "react";
import { FeatureGroup, useMap } from "react-leaflet";
const ShowBound = ({ bound }) => {
const map = useMap();
const featureRef = useRef(null);
const safeFitBounds = (layer) => {
if (!layer || !map) return;
try {
const latLngs = layer.getLatLngs();
map.fitBounds(latLngs, {
paddingTopLeft: [20, 20],
paddingBottomRight: [20, 20],
});
} catch (e) {
console.warn("fitBounds failed:", e);
}
};
useEffect(() => {
bound?.editing?.disable();
featureRef.current.addLayer(bound);
safeFitBounds(bound);
}, []);
return <FeatureGroup ref={featureRef} />;
};
export default ShowBound;

View File

@@ -0,0 +1,125 @@
"use client";
import DataTableWithAuth from "@/core/components/DataTableWithAuth";
import { GET_ROAD_MISSIONS_CONTROL_LIST } from "@/core/utils/routes";
import { Box, Stack } from "@mui/material";
import moment from "jalali-moment";
import { useMemo } from "react";
import ShowArea from "./Actions/showArea";
import RowActions from "./RowActions";
const ControlList = () => {
const typeOptions = [
{ value: 1, label: "روزانه" },
{ value: 2, label: "ساعتی" },
];
const columns = useMemo(
() => [
{
accessorKey: "id",
header: "کد یکتا",
id: "id",
enableColumnFilter: true,
datatype: "text",
filterMode: "equals",
columnFilterModeOptions: ["equals", "contains"],
grow: false,
size: 100,
},
{
accessorKey: "type",
header: "نوع",
id: "type",
enableColumnFilter: true,
datatype: "numeric",
filterMode: "equals",
sortDescFirst: true,
grow: false,
size: 100,
columnSelectOption: () => {
return typeOptions.map((type) => ({
value: type.value,
label: type.label,
}));
},
Cell: ({ row }) => row.original.type_fa,
},
{
accessorFn: (row) => moment(row.start_date).locale("fa").format("HH:mm | yyyy/MM/DD"),
header: "تاریخ شروع",
id: "start_date",
enableColumnFilter: true,
datatype: "date",
filterMode: "between",
grow: false,
size: 100,
},
{
accessorFn: (row) => moment(row.end_date).locale("fa").format("HH:mm | yyyy/MM/DD"),
header: "تاریخ پایان",
id: "end_date",
enableColumnFilter: true,
datatype: "date",
filterMode: "between",
grow: false,
size: 100,
},
{
accessorKey: "end_point",
header: "مقصد",
id: "end_point",
enableColumnFilter: false,
datatype: "numeric",
filterMode: "equals",
grow: false,
size: 100,
},
{
header: "محدوده",
id: "area",
enableColumnFilter: false,
datatype: "numeric",
filterMode: "equals",
grow: false,
size: 100,
muiTableBodyCellProps: {
sx: {
borderLeft: "1px solid #e1e1e1",
py: 0,
"&:first-of-type": {
borderLeft: "unset",
},
},
},
Cell: ({ row }) =>
row.original.area ? (
<Stack alignItems={"center"} justifyContent={"center"}>
<ShowArea area={row.original.area} />
</Stack>
) : (
"-"
),
},
],
[]
);
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_ROAD_MISSIONS_CONTROL_LIST}
page_name={"roadMissionsControl"}
table_name={"roadMissionsControlList"}
enableRowActions
positionActionsColumn={"first"}
RowActions={RowActions}
/>
</Box>
</>
);
};
export default ControlList;

View File

@@ -0,0 +1,42 @@
import { FINISH_MISSION } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
import { useState } from "react";
const FinishMissionContent = ({ rowId, mutate, setOpenFinishMissionDialog }) => {
const [submitting, setSubmitting] = useState(false);
const requestServer = useRequest({ notificationSuccess: true });
const handleClick = () => {
setSubmitting(true);
requestServer(`${FINISH_MISSION}/${rowId}`, "post", {
hasSidebarUpdate: true,
})
.then(() => {
mutate();
setOpenFinishMissionDialog(false);
setSubmitting(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={() => setOpenFinishMissionDialog(false)} variant="outlined" color="secondary">
خیر !
</Button>
<Button onClick={handleClick} variant="contained" color="primary" disabled={submitting}>
{submitting ? "درحال ثبت..." : "بله اطمینان دارم"}
</Button>
</DialogActions>
</>
);
};
export default FinishMissionContent;

View File

@@ -0,0 +1,36 @@
import { Login } from "@mui/icons-material";
import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
import FinishMissionContent from "./FinishMissionContent";
const FinishMission = ({ rowId, mutate }) => {
const [openFinishMissionDialog, setOpenFinishMissionDialog] = useState(false);
return (
<>
<Tooltip title="ورود">
<IconButton color="primary" onClick={() => setOpenFinishMissionDialog(true)}>
<Login />
</IconButton>
</Tooltip>
<Dialog
open={openFinishMissionDialog}
onClose={() => setOpenFinishMissionDialog(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>
<FinishMissionContent
rowId={rowId}
mutate={mutate}
setOpenFinishMissionDialog={setOpenFinishMissionDialog}
/>
</Dialog>
</>
);
};
export default FinishMission;

View File

@@ -0,0 +1,42 @@
import { START_MISSION } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
import { Button, DialogActions, DialogContent, Stack, Typography } from "@mui/material";
import { useState } from "react";
const StartMissionContent = ({ rowId, mutate, setOpenStartMissionDialog }) => {
const [submitting, setSubmitting] = useState(false);
const requestServer = useRequest({ notificationSuccess: true });
const handleClick = () => {
setSubmitting(true);
requestServer(`${START_MISSION}/${rowId}`, "post", {
hasSidebarUpdate: true,
})
.then(() => {
mutate();
setOpenStartMissionDialog(false);
setSubmitting(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={() => setOpenStartMissionDialog(false)} variant="outlined" color="secondary">
خیر !
</Button>
<Button onClick={handleClick} variant="contained" color="primary" disabled={submitting}>
{submitting ? "درحال ثبت..." : "بله اطمینان دارم"}
</Button>
</DialogActions>
</>
);
};
export default StartMissionContent;

View File

@@ -0,0 +1,36 @@
import { Logout } from "@mui/icons-material";
import { Dialog, DialogTitle, IconButton, Tooltip } from "@mui/material";
import { useState } from "react";
import StartMissionContent from "./StartMissionContent";
const StartMission = ({ rowId, mutate }) => {
const [openStartMissionDialog, setOpenStartMissionDialog] = useState(false);
return (
<>
<Tooltip title="خروج">
<IconButton color="primary" onClick={() => setOpenStartMissionDialog(true)}>
<Logout />
</IconButton>
</Tooltip>
<Dialog
open={openStartMissionDialog}
onClose={() => setOpenStartMissionDialog(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>
<StartMissionContent
rowId={rowId}
mutate={mutate}
setOpenStartMissionDialog={setOpenStartMissionDialog}
/>
</Dialog>
</>
);
};
export default StartMission;

View File

@@ -0,0 +1,13 @@
import { Box } from "@mui/material";
import FinishMission from "./FinishMission";
import StartMission from "./StartMission";
const RowActions = ({ row, mutate }) => {
return (
<Box sx={{ display: "flex", gap: 1 }}>
{row.original.state_id == 2 && <StartMission rowId={row.getValue("id")} mutate={mutate} />}
{row.original.state_id == 3 && <FinishMission rowId={row.getValue("id")} mutate={mutate} />}
</Box>
);
};
export default RowActions;

View File

@@ -0,0 +1,13 @@
import PageTitle from "@/core/components/PageTitle";
import { Stack } from "@mui/material";
import ControlList from "./ControlList";
const ControlPage = () => {
return (
<Stack spacing={1}>
<PageTitle title={"کارتابل کنترل"} />
<ControlList />
</Stack>
);
};
export default ControlPage;