Feature/map modify
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
"react-dom": "^18",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-leaflet-draw": "^0.20.4",
|
||||
"react-toastify": "^10.0.5",
|
||||
"sass": "^1.75.0",
|
||||
"sharp": "^0.33.4",
|
||||
|
||||
28
src/assets/scss/map-styles.scss
Normal file
28
src/assets/scss/map-styles.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
.leaflet-draw-tooltip {
|
||||
background-color: #4f4f4f;
|
||||
font-family: IRANSans;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
padding: 2px 8px;
|
||||
margin-right: 15px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.leaflet-draw-tooltip:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 103%;
|
||||
transform: rotate(90deg);
|
||||
margin-left: -6px;
|
||||
border-width: 6px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent #333 transparent;
|
||||
}
|
||||
|
||||
.leaflet-draw-tooltip-subtext {
|
||||
font-size: 12px;
|
||||
color: #ffeb3b;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Card, CardContent, FormControl, FormLabel, Stack, Typography} from "@mui/material";
|
||||
import { Card, CardContent, FormControl, FormLabel, Stack, Typography } from "@mui/material";
|
||||
|
||||
const QuestionSafetyForm = () => {
|
||||
return (
|
||||
<Stack sx={{py: 2}}>
|
||||
<Stack sx={{ py: 2 }}>
|
||||
<Card
|
||||
sx={{display: "flex", justifyContent: "center", alignItems: "center", borderColor: "primary.main"}}
|
||||
sx={{ display: "flex", justifyContent: "center", alignItems: "center", borderColor: "primary.main" }}
|
||||
variant="outlined"
|
||||
>
|
||||
<CardContent>
|
||||
@@ -15,13 +15,11 @@ const QuestionSafetyForm = () => {
|
||||
</Typography>
|
||||
</FormLabel>
|
||||
<Stack
|
||||
sx={{pt: 2, display: "flex", justifyContent: "center", alignItems: "center"}}
|
||||
sx={{ pt: 2, display: "flex", justifyContent: "center", alignItems: "center" }}
|
||||
spacing={1}
|
||||
direction={"row"}
|
||||
>
|
||||
<Typography variant={"subtitle1"}>
|
||||
دفتر حریم راه :{" "}
|
||||
</Typography>
|
||||
<Typography variant={"subtitle1"}>دفتر حریم راه : </Typography>
|
||||
<Typography variant={"h5"} fontWeight={"bolder"}>
|
||||
بله
|
||||
</Typography>
|
||||
|
||||
@@ -40,7 +40,7 @@ const RoadSafetyFormContext = ({ rowId, mutate, setOpenRoadSafetyForm }) => {
|
||||
const defaultValues = {
|
||||
description: "",
|
||||
};
|
||||
const requestServer = useRequest({ notification : {success : true, show : true} });
|
||||
const requestServer = useRequest({ notification: { success: true, show: true } });
|
||||
const { message, loadingMessage, errorMessage } = usePrevStateOpinion();
|
||||
const {
|
||||
register,
|
||||
|
||||
@@ -31,7 +31,7 @@ const ReferFormContext = ({ setOpenReferDialog, rowId, mutate }) => {
|
||||
city: "",
|
||||
};
|
||||
const { cities, loadingCities, errorCities } = useCities();
|
||||
const requestServer = useRequest({notification : {show : true, success : true}});
|
||||
const requestServer = useRequest({ notification: { show: true, success: true } });
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Card, CardContent, FormControl, FormLabel, Stack, Typography} from "@mui/material";
|
||||
import { Card, CardContent, FormControl, FormLabel, Stack, Typography } from "@mui/material";
|
||||
|
||||
const QuestionSafetyForm = () => {
|
||||
return (
|
||||
<Stack sx={{py: 2}}>
|
||||
<Stack sx={{ py: 2 }}>
|
||||
<Card
|
||||
sx={{display: "flex", justifyContent: "center", alignItems: "center", borderColor: "primary.main"}}
|
||||
sx={{ display: "flex", justifyContent: "center", alignItems: "center", borderColor: "primary.main" }}
|
||||
variant="outlined"
|
||||
>
|
||||
<CardContent>
|
||||
@@ -15,13 +15,11 @@ const QuestionSafetyForm = () => {
|
||||
</Typography>
|
||||
</FormLabel>
|
||||
<Stack
|
||||
sx={{pt: 2, display: "flex", justifyContent: "center", alignItems: "center"}}
|
||||
sx={{ pt: 2, display: "flex", justifyContent: "center", alignItems: "center" }}
|
||||
spacing={1}
|
||||
direction={"row"}
|
||||
>
|
||||
<Typography variant={"subtitle1"}>
|
||||
دفتر حریم راه :{" "}
|
||||
</Typography>
|
||||
<Typography variant={"subtitle1"}>دفتر حریم راه : </Typography>
|
||||
<Typography variant={"h5"} fontWeight={"bolder"}>
|
||||
بله
|
||||
</Typography>
|
||||
|
||||
@@ -40,7 +40,7 @@ const RoadSafetyFormContext = ({ rowId, mutate, setOpenRoadSafetyForm }) => {
|
||||
const defaultValues = {
|
||||
description: "",
|
||||
};
|
||||
const requestServer = useRequest({ notification : {success : true, show : true} });
|
||||
const requestServer = useRequest({ notification: { success: true, show: true } });
|
||||
const { message, loadingMessage, errorMessage } = usePrevStateOpinion();
|
||||
const {
|
||||
register,
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import {Box, Button, ButtonGroup} from "@mui/material";
|
||||
import AutoModeIcon from "@mui/icons-material/AutoMode";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import EditLocationAltIcon from "@mui/icons-material/EditLocationAlt";
|
||||
import SaveIcon from "@mui/icons-material/Save";
|
||||
import CancelIcon from "@mui/icons-material/Cancel";
|
||||
|
||||
const MapActionButtons = ({
|
||||
disableToCreate,
|
||||
disableToEditAndDelete,
|
||||
disableToSaveAndCancel,
|
||||
onDraw,
|
||||
onEdit,
|
||||
onRemove,
|
||||
onSave,
|
||||
onCancel,
|
||||
}) => (
|
||||
<>
|
||||
<Box sx={{
|
||||
background: "#ffffff",
|
||||
position: "absolute",
|
||||
zIndex: "1999",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
p: 2,
|
||||
pl: 0.5,
|
||||
pb: 0.5,
|
||||
borderTopRightRadius: 8,
|
||||
}}>
|
||||
<ButtonGroup variant="contained" orientation="vertical" aria-label="map action group button">
|
||||
<Button
|
||||
sx={{justifyContent: "space-between"}}
|
||||
startIcon={<AutoModeIcon/>}
|
||||
color="primary"
|
||||
disabled={disableToCreate}
|
||||
onClick={onDraw}
|
||||
>
|
||||
اصلاح
|
||||
</Button>
|
||||
<Button
|
||||
sx={{justifyContent: "space-between"}}
|
||||
startIcon={<EditLocationAltIcon/>}
|
||||
color="warning"
|
||||
disabled={disableToEditAndDelete}
|
||||
onClick={onEdit}
|
||||
>
|
||||
ویرایش
|
||||
</Button>
|
||||
<Button
|
||||
sx={{justifyContent: "space-between"}}
|
||||
startIcon={<DeleteIcon/>}
|
||||
color="error"
|
||||
disabled={disableToEditAndDelete}
|
||||
onClick={onRemove}
|
||||
>
|
||||
حذف
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
position: "absolute",
|
||||
zIndex: "1999",
|
||||
right: 10,
|
||||
top: 10,
|
||||
background: "#fff",
|
||||
borderRadius: 1,
|
||||
padding: 0.5,
|
||||
}}>
|
||||
<Button
|
||||
sx={{justifyContent: "space-between", mr: 1}}
|
||||
startIcon={<SaveIcon/>}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
color="success"
|
||||
disabled={disableToSaveAndCancel}
|
||||
onClick={onSave}
|
||||
>
|
||||
ذخیره
|
||||
</Button>
|
||||
<Button
|
||||
sx={{justifyContent: "space-between"}}
|
||||
startIcon={<CancelIcon/>}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
color="error"
|
||||
disabled={disableToSaveAndCancel}
|
||||
onClick={onCancel}
|
||||
>
|
||||
لغو تغییرات
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
export default MapActionButtons;
|
||||
@@ -0,0 +1,91 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Divider,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
FormLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import dynamic from "next/dynamic";
|
||||
import MapLoading from "@/core/components/MapLayer/Loading";
|
||||
import "leaflet-draw/dist/leaflet.draw.css";
|
||||
import GppMaybeIcon from "@mui/icons-material/GppMaybe";
|
||||
import PolygonForDesign from "./PolygonForDesign";
|
||||
|
||||
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
|
||||
loading: () => <MapLoading/>,
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const MapModifyDialog = ({mapModifyModal, setMapModifyModal}) => {
|
||||
const handleClose = () => {
|
||||
setMapModifyModal(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={mapModifyModal} onClose={handleClose} maxWidth="lg" fullWidth>
|
||||
<DialogContent sx={{p: 2}} dividers>
|
||||
<Divider sx={{my: 3}}>
|
||||
<Chip color="primary" sx={{mx: 1, fontSize: "13px", fontWeight: 500}}
|
||||
label="بررسی پلیگان طرح متقاضی"/>
|
||||
</Divider>
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
height: "400px",
|
||||
borderRadius: 3,
|
||||
borderBottomLeftRadius: 0,
|
||||
overflow: "hidden",
|
||||
}}>
|
||||
<MapLayer>
|
||||
<PolygonForDesign/>
|
||||
</MapLayer>
|
||||
</Box>
|
||||
<Box sx={{display: "flex", my: 2, alignItems: "center"}}>
|
||||
<GppMaybeIcon color="warning"/>
|
||||
<Typography variant="button" sx={{color: "warning.main", ml: 1}}>
|
||||
نکته: اصلاح ننمودن طرح به منزله تایید طرح میباشد.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
border: "1px solid #e1e1e1",
|
||||
p: 2,
|
||||
borderRadius: 1,
|
||||
}}>
|
||||
<FormControl sx={{flexDirection: "row", alignItems: "center", gap: 2}}>
|
||||
<FormLabel id="way-status" sx={{fontWeight: 500, fontSize: "16px", color: "warning.main"}}>آیا
|
||||
طرح نیاز به ایجاد راه
|
||||
دسترسی
|
||||
دارد؟</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
aria-labelledby="way-status"
|
||||
defaultValue={0}
|
||||
name="radio-buttons-group"
|
||||
>
|
||||
<FormControlLabel value={1} control={<Radio color="warning"/>} label="بله"/>
|
||||
<FormControlLabel value={0} control={<Radio color="warning"/>} label="خیر"/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{justifyContent: "center", py: 2}}>
|
||||
<Button size="large" variant="outlined">بستن</Button>
|
||||
<Button size="large" variant="contained">ثبت</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default MapModifyDialog;
|
||||
@@ -0,0 +1,118 @@
|
||||
"use client";
|
||||
|
||||
import "leaflet-draw/dist/leaflet.draw.css";
|
||||
import "@/assets/scss/map-styles.scss"
|
||||
import {FeatureGroup, useMap} from "react-leaflet";
|
||||
import {EditControl} from "react-leaflet-draw";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import MapActionButtons from "./MapActionButtons";
|
||||
|
||||
|
||||
const PolygonForDesign = () => {
|
||||
const map = useMap();
|
||||
const editableFG = useRef(null);
|
||||
const [polygonLayer, setPolygonLayer] = useState(null);
|
||||
const [disableToCreate, setDisableToCreate] = useState(false);
|
||||
const [disableToEditAndDelete, setDisableToEditAndDelete] = useState(true);
|
||||
const [disableToSaveAndCancel, setDisableToSaveAndCancel] = useState(true);
|
||||
const [editToolbar, setEditToolbar] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const {L} = window;
|
||||
L.drawLocal.draw.handlers.polygon.tooltip = {
|
||||
start: "برای شروع کشیدن پلیگان، کلیک کنید.",
|
||||
cont: "گوشه های دیگر پلیگان را نیز انتخاب کنید.",
|
||||
end: "برای اتمام رسم پلیگان، نقطه آخر را به اول متصل نمایید.",
|
||||
};
|
||||
L.drawLocal.edit.handlers.edit.tooltip = {
|
||||
text: "برای ذخیر یا لغو تغییرات، از دکمه های بالا سمت چپ استفاده نمایید.",
|
||||
subtext: "نقاط مشخص شده را گرفته و برای ویرایش جابجا نمایید.",
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleDrawPolygon = () => {
|
||||
const {L} = window;
|
||||
const drawControl = new L.Draw.Polygon(map);
|
||||
drawControl.enable();
|
||||
setDisableToCreate(true);
|
||||
};
|
||||
|
||||
const handleEditPolygon = () => {
|
||||
const {L} = window;
|
||||
if (editableFG.current && polygonLayer) {
|
||||
const editControlInstance = new L.EditToolbar.Edit(map, {
|
||||
featureGroup: editableFG.current,
|
||||
});
|
||||
editControlInstance.enable();
|
||||
setEditToolbar(editControlInstance);
|
||||
setDisableToEditAndDelete(true);
|
||||
setDisableToSaveAndCancel(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemovePolygon = () => {
|
||||
if (polygonLayer && editableFG.current) {
|
||||
editableFG.current.removeLayer(polygonLayer);
|
||||
setPolygonLayer(null);
|
||||
setDisableToEditAndDelete(true);
|
||||
setDisableToCreate(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSavePolygon = () => {
|
||||
if (editToolbar) {
|
||||
editToolbar.save();
|
||||
editToolbar.disable();
|
||||
setDisableToEditAndDelete(false);
|
||||
setDisableToSaveAndCancel(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
if (editToolbar) {
|
||||
editToolbar.revertLayers();
|
||||
editToolbar.disable();
|
||||
setDisableToEditAndDelete(false);
|
||||
setDisableToSaveAndCancel(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePolygonCreate = (e) => {
|
||||
const layer = e.layer;
|
||||
setPolygonLayer(layer);
|
||||
editableFG.current.addLayer(layer);
|
||||
setDisableToEditAndDelete(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FeatureGroup ref={editableFG}>
|
||||
<EditControl
|
||||
position="bottomright"
|
||||
onCreated={handlePolygonCreate}
|
||||
draw={{
|
||||
polygon: false,
|
||||
circlemarker: false,
|
||||
marker: false,
|
||||
circle: false,
|
||||
rectangle: false,
|
||||
polyline: false,
|
||||
}}
|
||||
edit={{edit: false, remove: false}}
|
||||
/>
|
||||
</FeatureGroup>
|
||||
<MapActionButtons
|
||||
disableToCreate={disableToCreate}
|
||||
disableToEditAndDelete={disableToEditAndDelete}
|
||||
disableToSaveAndCancel={disableToSaveAndCancel}
|
||||
onDraw={handleDrawPolygon}
|
||||
onEdit={handleEditPolygon}
|
||||
onRemove={handleRemovePolygon}
|
||||
onSave={handleSavePolygon}
|
||||
onCancel={handleCancelEdit}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PolygonForDesign;
|
||||
@@ -0,0 +1,25 @@
|
||||
import {IconButton, Tooltip} from "@mui/material";
|
||||
import MapIcon from "@mui/icons-material/Map";
|
||||
import {useState} from "react";
|
||||
import MapModifyDialog from "./MapModifyDialog";
|
||||
|
||||
const MapModify = ({rowId, mutate}) => {
|
||||
const [mapModifyModal, setMapModifyModal] = useState(false);
|
||||
|
||||
const openMapModifyDialog = () => {
|
||||
setMapModifyModal(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="بررسی پلیگان طرح" arrow placement="right">
|
||||
<IconButton color="primary" onClick={openMapModifyDialog}>
|
||||
<MapIcon/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<MapModifyDialog mapModifyModal={mapModifyModal} setMapModifyModal={setMapModifyModal} rowId={rowId}
|
||||
mutate={mutate}/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default MapModify;
|
||||
@@ -31,7 +31,7 @@ const ReferFormContext = ({ setOpenReferDialog, rowId, mutate }) => {
|
||||
province: "",
|
||||
};
|
||||
const { provinces, loadingProvinces, errorProvinces } = useProvinces();
|
||||
const requestServer = useRequest({notification : {show : true, success : true}});
|
||||
const requestServer = useRequest({ notification: { show: true, success: true } });
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
|
||||
@@ -41,7 +41,7 @@ const RoadSafetyFormContext = ({ rowId, mutate, setOpenRoadSafetyForm }) => {
|
||||
description: "",
|
||||
RadioGroup: "",
|
||||
};
|
||||
const requestServer = useRequest({ notification : {success : true, show : true} });
|
||||
const requestServer = useRequest({ notification: { success: true, show: true } });
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import ReferForm from "./Refer";
|
||||
import TaskDetail from "./TaskDetail";
|
||||
import { Box } from "@mui/material";
|
||||
import {Box} from "@mui/material";
|
||||
import RoadSafetyForm from "./RoadSafetyForm";
|
||||
import MapModify from "./MapModify";
|
||||
|
||||
const RowActions = ({ row, mutate }) => {
|
||||
const RowActions = ({row, mutate}) => {
|
||||
return (
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
<ReferForm mutate={mutate} rowId={row.getValue("id")} />
|
||||
<TaskDetail mutate={mutate} rowId={row.getValue("id")} />
|
||||
<RoadSafetyForm rowId={row.getValue("id")} mutate={mutate} />
|
||||
<Box sx={{display: "flex", gap: 1}}>
|
||||
<MapModify mutate={mutate} rowId={row.getValue("id")}/>
|
||||
<ReferForm mutate={mutate} rowId={row.getValue("id")}/>
|
||||
<TaskDetail mutate={mutate} rowId={row.getValue("id")}/>
|
||||
<RoadSafetyForm rowId={row.getValue("id")} mutate={mutate}/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { GET_CITY_LISTS } from "@/core/utils/routes";
|
||||
import useRequest from "@/lib/hooks/useRequest";
|
||||
|
||||
const useCities = () => {
|
||||
const requestServer = useRequest()
|
||||
const requestServer = useRequest();
|
||||
const [cities, setCities] = useState([]);
|
||||
const [loadingCities, setLoadingCities] = useState(true);
|
||||
const [errorCities, setErrorCities] = useState(null);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { GET_PREV_STATE_OPINION } from "@/core/utils/routes";
|
||||
import useRequest from "@/lib/hooks/useRequest";
|
||||
|
||||
const usePrevStateOpinion = () => {
|
||||
const requestServer = useRequest()
|
||||
const requestServer = useRequest();
|
||||
const [message, setMessage] = useState([]);
|
||||
const [loadingMessage, setLoadingMessage] = useState(true);
|
||||
const [errorMessage, setErrorMessage] = useState(null);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { GET_PROVINCE_LISTS } from "@/core/utils/routes";
|
||||
import useRequest from "@/lib/hooks/useRequest";
|
||||
|
||||
const useProvinces = () => {
|
||||
const requestServer = useRequest()
|
||||
const requestServer = useRequest();
|
||||
const [provinces, setProvinces] = useState([]);
|
||||
const [loadingProvinces, setLoadingProvinces] = useState(true);
|
||||
const [errorProvinces, setErrorProvinces] = useState(null);
|
||||
|
||||
Reference in New Issue
Block a user