Feature/faaliyat rozane cartable

This commit is contained in:
2024-12-23 08:20:46 +00:00
committed by AmirHossein Mahmoodi
parent f2ef4cd1db
commit 4b25071e41
75 changed files with 3722 additions and 236 deletions

View File

@@ -1,28 +1,26 @@
"use client";
import {Autocomplete, CircularProgress, TextField} from "@mui/material";
import {useEffect, useState} from "react";
import { Autocomplete, CircularProgress, TextField } from "@mui/material";
import { useEffect, useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import {debounce} from "@mui/material/utils";
import { debounce } from "@mui/material/utils";
import { GET_CAR_LIST_SEARCH } from "@/core/utils/routes";
const CarCode = ({carCode, setCarCode}) => {
const [inputValue, setInputValue] = useState('');
const CarCode = ({ carCode, setCarCode, inputValueDefault = "", error }) => {
const [inputValue, setInputValue] = useState(inputValueDefault);
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
const requestServer = useRequest();
const fetchCarCodes = (inputValue, controller) => {
const debouncer = debounce((query) => {
if (query.length < 2) {
if (!query || query?.length < 3) {
setOptions([]);
return;
}
setLoading(true);
requestServer(`car-code/api?${query}`, "get", {
requestOptions: {signal: controller.signal}
requestServer(`${GET_CAR_LIST_SEARCH}?machine_code=${query}`, "get", {
requestOptions: { signal: controller.signal },
})
.then((response) => {
setOptions(response || []);
setOptions(response.data.data || []);
})
.catch(() => {
setOptions([]);
@@ -30,9 +28,9 @@ const CarCode = ({carCode, setCarCode}) => {
.finally(() => {
setLoading(false);
});
}, 500)
debouncer(inputValue)
}
}, 500);
debouncer(inputValue);
};
useEffect(() => {
const controller = new AbortController();
@@ -45,34 +43,35 @@ const CarCode = ({carCode, setCarCode}) => {
value={carCode}
size="small"
onChange={(event, newValue) => {
setCarCode(newValue);
setCarCode(newValue || null); // Set an empty string if no value is selected
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
setInputValue(newInputValue || ""); // Prevent inputValue from being null or undefined
}}
options={options}
getOptionLabel={(option) =>
typeof option === 'string' ? option : option.name || option.code
}
getOptionLabel={(option) => (typeof option === "string" ? option : option.machine_code)}
isOptionEqualToValue={(option, value) => {
if (!value) return false; // Handle null/undefined values
if (value === "") return option.machine_code === ""; // Handle empty string case
return option.machine_code === (value?.machine_code || value);
}}
loading={loading}
loadingText="درحال جستجوی کد خودرو"
noOptionsText={
inputValue.length < 2
? "حداقل دو رقم از کد خودرو را وارد کنید"
: "کد خودرویی یافت نشد"
}
sx={{width: 300}}
noOptionsText={inputValue?.length < 3 ? "حداقل سه رقم از کد خودرو را وارد کنید" : "کد خودرویی یافت نشد"}
fullWidth
renderInput={(params) => (
<TextField
{...params}
label="کد خودرو"
fullWidth
error={!!error} // نمایش خطا
helperText={error?.message} // پیام خطا
InputProps={{
...params.InputProps,
endAdornment: (
<>
{loading ? <CircularProgress size="25px" color="inherit"/> : null}
{loading ? <CircularProgress size="25px" color="inherit" /> : null}
{params.InputProps.endAdornment}
</>
),
@@ -82,5 +81,4 @@ const CarCode = ({carCode, setCarCode}) => {
/>
);
};
export default CarCode;
export default CarCode;

View File

@@ -8,6 +8,7 @@ import { Box, Button, Drawer, styled } from "@mui/material";
import { useEffect, useState } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import * as Yup from "yup";
import FilterBodyController from "./FilterBodyController";
const ScrollBox = styled(Box)({
flexGrow: 1,
@@ -95,27 +96,12 @@ function FilterBody({ columns, drawerState, setDrawerState }) {
{columns.map(
(column) =>
column.enableColumnFilter && (
<Controller
<FilterBodyController
key={column.id}
name={`${column.id}`}
column={column}
control={control}
render={({ field: { value, onBlur, onChange } }) => {
let dependencyField;
if (column.dependencyId) {
dependencyField = useWatch({ control, name: column.dependencyId });
}
return (
<FilterBodyField
column={column}
filterParameters={value}
handleChange={onChange}
handleBlur={onBlur}
dependencyFieldValue={dependencyField}
resetForm={() => reset()}
errors={errors}
/>
);
}}
errors={errors}
reset={reset}
/>
)
)}

View File

@@ -0,0 +1,25 @@
import { Controller, useWatch } from "react-hook-form";
import FilterBodyField from "./FilterBodyField";
const FilterBodyController = ({ column, control, reset, errors }) => {
const dependencyField = useWatch({ control, name: column?.dependencyId });
return (
<Controller
name={`${column.id}`}
control={control}
render={({ field: { value, onBlur, onChange } }) => (
<FilterBodyField
column={column}
filterParameters={value}
handleChange={onChange}
handleBlur={onBlur}
dependencyFieldValue={dependencyField || null}
resetForm={() => reset()}
errors={errors}
/>
)}
/>
);
};
export default FilterBodyController;

View File

@@ -1,11 +1,12 @@
"use client";
import { IconButton, Tooltip } from "@mui/material";
import { IconButton, Tooltip, Badge } from "@mui/material";
import AutorenewIcon from "@mui/icons-material/Autorenew";
import useTableSetting from "@/lib/hooks/useTableSetting";
import useDataTable from "@/lib/hooks/useDataTable";
function ResetStorage({ user_id, page_name, table_name }) {
const { resetAction } = useTableSetting();
const { isDirty } = useDataTable();
const reset = () => {
resetAction(user_id, page_name, table_name);
};
@@ -13,7 +14,20 @@ function ResetStorage({ user_id, page_name, table_name }) {
return (
<Tooltip title="بازنشانی اطلاعات">
<IconButton onClick={reset} aria-label="reset table data">
<AutorenewIcon />
{isDirty ? (
<Badge
anchorOrigin={{
vertical: "top",
horizontal: "left",
}}
color="primary"
variant="dot"
>
<AutorenewIcon />
</Badge>
) : (
<AutorenewIcon />
)}
</IconButton>
</Tooltip>
);

View File

@@ -0,0 +1,137 @@
import React, { useEffect, useRef, useState } from "react";
import { Marker, useMapEvents } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import { Box, Button, Typography } from "@mui/material";
import dynamic from "next/dynamic";
import MapLoading from "@/core/components/MapLayer/Loading";
import HereIcon from "@/assets/images/examine_marker_active.png";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => <MapLoading />,
ssr: false,
});
const createCustomIcon = (size, iconUrl, labelText) => {
if (labelText) {
return L.divIcon({
html: `
<div style="position: relative; display: inline-block; text-align: center;">
<img
src="${iconUrl}"
style="width: ${size[0]}px; height: ${size[1]}px;"
alt="icon"
/>
<div style="position: absolute; top: 100%; left: 50%; transform: translate(-50%, 0); color: black; font-size: 12px;">
${labelText}
</div>
</div>
`,
iconSize: size,
iconAnchor: [size[0] / 2, size[1]],
popupAnchor: [0, -size[1]],
});
}
return L.icon({
iconUrl: iconUrl,
iconSize: size,
iconAnchor: [size[0] / 2, size[1]],
popupAnchor: [0, -size[1]],
});
};
const MapInteraction = ({ setValue, startLat, startLng }) => {
const [isMarkerLocked, setIsMarkerLocked] = useState(!!(startLat && startLng)); // وضعیت قفل مارکر
const [markerPosition, setMarkerPosition] = useState(
startLat && startLng ? { lat: startLat, lng: startLng } : null
);
const markerRef = useRef();
const map = useMapEvents({
move(e) {
if (!isMarkerLocked && markerRef.current) {
markerRef.current.setLatLng(e.target.getCenter());
}
},
zoom(e) {
if (!isMarkerLocked && markerRef.current) {
markerRef.current.setLatLng(e.target.getCenter());
}
},
});
useEffect(() => {
if (startLat && startLng) {
const position = { lat: startLat, lng: startLng };
map.setView(position, 15);
}
}, [startLat, startLng, map]);
const handleMarkerClick = () => {
if (!isMarkerLocked) {
const center = map.getCenter();
setValue("start_point", { lat: center.lat.toString(), lng: center.lng.toString() });
setMarkerPosition({ lat: center.lat, lng: center.lng }); // به‌روزرسانی موقعیت مارکر
setIsMarkerLocked(true);
}
};
const handleUnlockMarker = () => {
setValue("start_point", null); // حذف مقدار قبلی
setIsMarkerLocked(false); // باز کردن قفل مارکر
setMarkerPosition(null); // تنظیم مارکر به مرکز نقشه
};
return (
<>
<Marker
position={markerPosition || map.getCenter()} // استفاده از موقعیت
ref={markerRef}
icon={createCustomIcon([35, 35], HereIcon.src, "اینجا")}
eventHandlers={{
click: handleMarkerClick,
}}
/>
{isMarkerLocked && (
<Box
sx={{
position: "absolute",
bottom: 10,
left: 10,
zIndex: 1000,
}}
>
<Button variant="contained" onClick={handleUnlockMarker}>
ویرایش
</Button>
</Box>
)}
</>
);
};
const MapInfoOneMarker = ({ setValue, errors, StartPoint = null }) => {
return (
<Box>
<Box sx={{ width: "100%", height: "400px", marginBottom: "16px" }}>
<MapLayer style={{ border: "1px solid #0009", borderRadius: "4px" }}>
<MapInteraction
setValue={setValue}
startLat={StartPoint ? StartPoint.lat : null}
startLng={StartPoint ? StartPoint.lng : null}
/>
</MapLayer>
</Box>
<Box display="flex" gap={2}>
{errors.start_point && (
<Typography color="error" variant="body2">
{errors.start_point.message}
</Typography>
)}
</Box>
</Box>
);
};
export default MapInfoOneMarker;

View File

@@ -0,0 +1,191 @@
import React, { useEffect, useRef, useState } from "react";
import { Marker, useMapEvents } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import { Box, Button, Typography } from "@mui/material";
import dynamic from "next/dynamic";
import MapLoading from "@/core/components/MapLayer/Loading";
import EndIcon from "@/assets/images/examine_marker_active.png";
import StartIcon from "@/assets/images/examine_marker.png";
const MapLayer = dynamic(() => import("@/core/components/MapLayer"), {
loading: () => <MapLoading />,
ssr: false,
});
const createCustomIcon = (size, iconUrl, labelText) => {
if (labelText) {
return L.divIcon({
html: `
<div style="position: relative; display: inline-block; text-align: center;">
<img
src="${iconUrl}"
style="width: ${size[0]}px; height: ${size[1]}px;"
alt="icon"
/>
<div style="position: absolute; top: 100%; left: 50%; transform: translate(-50%, 0); color: black; font-size: 12px;">
${labelText}
</div>
</div>
`,
iconSize: size,
iconAnchor: [size[0] / 2, size[1]],
popupAnchor: [0, -size[1]],
});
}
return L.icon({
iconUrl: iconUrl,
iconSize: size,
iconAnchor: [size[0] / 2, size[1]],
popupAnchor: [0, -size[1]],
});
};
const MapInteraction = ({ setValue, startLat, startLng, endLat, endLng }) => {
const [isStartLocked, setIsStartLocked] = useState(!!(startLat && startLng)); // وضعیت قفل نقطه آغاز
const [isEndLocked, setIsEndLocked] = useState(!!(endLat && endLng)); // وضعیت قفل نقطه پایان
const [startPosition, setStartPosition] = useState(startLat && startLng ? { lat: startLat, lng: startLng } : null);
const [endPosition, setEndPosition] = useState(endLat && endLng ? { lat: endLat, lng: endLng } : null);
const startRef = useRef();
const endRef = useRef();
const map = useMapEvents({
move(e) {
if (!isStartLocked && startRef.current) {
startRef.current.setLatLng(e.target.getCenter());
} else if (isStartLocked && !isEndLocked && endRef.current) {
endRef.current.setLatLng(e.target.getCenter());
}
},
zoom(e) {
if (!isStartLocked && startRef.current) {
startRef.current.setLatLng(e.target.getCenter());
} else if (isStartLocked && !isEndLocked && endRef.current) {
endRef.current.setLatLng(e.target.getCenter());
}
},
});
useEffect(() => {
if (startLat && endLng) {
map.fitBounds(
[
{ lat: startLat, lng: startLng },
{ lat: endLat, lng: endLng },
],
{ paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] }
);
}
}, [startLat, map, endLng]);
const handleUnlockStart = () => {
setIsStartLocked(false);
setStartPosition(null);
setValue("start_point", null);
};
const handleUnlockEnd = () => {
setIsEndLocked(false);
setIsStartLocked(false);
setEndPosition(null);
setStartPosition(null);
setValue("end_point", "");
setValue("start_point", null);
};
return (
<>
<Marker
position={startPosition || map.getCenter()}
ref={startRef}
icon={createCustomIcon([35, 35], StartIcon.src, "نقطه شروع")}
eventHandlers={{
click: () => {
if (!isStartLocked) {
const center = map.getCenter();
setValue("start_point", { lat: center.lat.toString(), lng: center.lng.toString() });
setStartPosition({ lat: center.lat, lng: center.lng });
setIsStartLocked(true);
}
},
}}
/>
{isStartLocked && !isEndLocked && (
<Box
sx={{
position: "absolute",
bottom: 10,
left: 10,
zIndex: 1000,
}}
>
<Button variant={"contained"} onClick={handleUnlockStart}>
ویرایش نقطه شروع
</Button>
</Box>
)}
{isStartLocked && (
<Marker
position={endPosition || map.getCenter()}
ref={endRef}
icon={createCustomIcon([35, 35], EndIcon.src, "نقطه پایان")}
eventHandlers={{
click: () => {
if (!isEndLocked) {
const center = map.getCenter();
setValue("end_point", { lat: center.lat.toString(), lng: center.lng.toString() });
setEndPosition({ lat: center.lat, lng: center.lng });
setIsEndLocked(true);
}
},
}}
/>
)}
{isEndLocked && (
<Box
sx={{
position: "absolute",
bottom: 10,
left: 10,
zIndex: 1000,
}}
>
<Button variant={"contained"} onClick={handleUnlockEnd}>
ویرایش نقاط
</Button>
</Box>
)}
</>
);
};
const MapInfoTwoMarker = ({ setValue, errors, StartPoint = null, EndPoint = null }) => {
return (
<Box>
<Box sx={{ width: "100%", height: "400px", marginBottom: "16px" }}>
<MapLayer style={{ border: "1px solid #0009", borderRadius: "4px" }}>
<MapInteraction
setValue={setValue}
startLat={StartPoint ? StartPoint.lat : null}
startLng={StartPoint ? StartPoint.lng : null}
endLat={EndPoint ? EndPoint.lat : null}
endLng={EndPoint ? EndPoint.lng : null}
/>
</MapLayer>
</Box>
<Box display="flex" gap={2}>
{errors.start_point && (
<Typography color="error" variant="body2">
{errors.start_point.message}
</Typography>
)}
{errors.end_point && (
<Typography color="error" variant="body2">
{errors.end_point.message}
</Typography>
)}
</Box>
</Box>
);
};
export default MapInfoTwoMarker;

View File

@@ -1,53 +1,70 @@
import { LocalizationProvider, MobileDateTimePicker } from "@mui/x-date-pickers";
import React from "react";
import { LocalizationProvider, MobileDatePicker } from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import moment from "jalali-moment";
import { faIR } from "@mui/x-date-pickers/locales";
import { Box, IconButton, FormControl, FormHelperText, InputAdornment, Stack } from "@mui/material";
import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear";
import moment from "jalali-moment";
export default function PickerWithButtonField({ formik, name, value, error, touched, disabled }) {
function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error }) {
return (
<FormControl variant="outlined" fullWidth size="small" error={!!error}>
<Stack sx={{ width: "100%" }} direction={"row"}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
>
<MobileDateTimePicker
ampm={false}
disableFuture
value={value ? new Date(value) : null}
onChange={(newValue) => {
const formattedDate = moment(newValue).locale("en").format("YYYY-MM-DD HH:mm");
formik.setFieldValue(name, formattedDate);
}}
slotProps={{
textField: {
placeholder:
name === "start_date" ? "تاریخ شروع را وارد کنید" : "تاریخ اتمام را وارد کنید",
variant: "outlined",
disabled,
fullWidth: true,
error: !!error,
helperText: touched && error,
size: "small", // Ensure that the TextField is small
InputProps: {
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small" // Set size="small" for IconButton
onClick={() => formik.setFieldValue(name, null)}
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
>
<MobileDatePicker
value={value ? new Date(value) : null}
sx={{ width: "100%" }}
id={name}
name={name}
closeOnSelect
disableFuture
label={helperText}
aria-describedby="component-helper-text"
onChange={(value) => {
const date = new Date(value);
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD");
setFieldValue(name, formattedDate);
}}
minDate={minDate ? new Date(minDate) : null}
maxDate={maxDate ? new Date(maxDate) : null}
slotProps={{
textField: {
error: error,
size: "small",
placeholder: placeholder,
InputProps: {
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
setFieldValue(name, "");
}}
sx={{
color: "#bfbfbf",
"&:hover": {
backgroundColor: "rgba(189, 189, 189, 0.1)",
color: "#363434",
},
}}
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
}}
/>
</LocalizationProvider>
</Stack>
</FormControl>
},
}}
/>
<FormHelperText id="component-helper-text" sx={{ color: error ? "#d32f2f" : "unset" }}>
{helperText ? helperText : ""}
</FormHelperText>
</LocalizationProvider>
</Box>
);
}
export default MuiDatePicker;

View File

@@ -0,0 +1,75 @@
import React from "react";
import { LocalizationProvider, MobileTimePicker } from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import { faIR } from "@mui/x-date-pickers/locales";
import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear";
import { parseISO, format } from "date-fns";
function MuiTimePicker({ value, setFieldValue, name, minTime, maxTime, helperText, placeholder, error }) {
// Ensure value, minTime, maxTime are Date objects
const parsedValue = value ? (typeof value === "string" ? parseISO(value) : value) : null;
const parsedMinTime = minTime ? (typeof minTime === "string" ? parseISO(minTime) : minTime) : null;
const parsedMaxTime = maxTime && (typeof maxTime === "string" ? parseISO(maxTime) : null);
return (
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
>
<MobileTimePicker
value={parsedValue}
sx={{ width: "100%" }}
id={name}
closeOnSelect
label={helperText ? helperText : placeholder}
ampm={false}
timeSteps={{ hours: 1, minutes: 5 }}
views={["hours", "minutes"]}
name={name}
onChange={(newValue) => {
const formattedTime = newValue ? format(new Date(newValue), "HH:mm") : "";
setFieldValue(name, formattedTime);
}}
minTime={parsedMinTime}
maxTime={parsedMaxTime}
slotProps={{
textField: {
placeholder: placeholder,
size: "small",
error: error,
InputProps: {
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
setFieldValue(name, "");
}}
sx={{
color: "#bfbfbf",
"&:hover": {
backgroundColor: "rgba(189, 189, 189, 0.1)",
color: "#363434",
},
}}
>
<ClearIcon />
</IconButton>
</InputAdornment>
),
},
},
}}
/>
<FormHelperText id="component-helper-text" sx={{ color: error ? "#d32f2f" : "unset" }}>
{helperText ? helperText : ""}
</FormHelperText>
</LocalizationProvider>
</Box>
);
}
export default MuiTimePicker;

View File

@@ -0,0 +1,90 @@
"use client";
import { Autocomplete, CircularProgress, TextField } from "@mui/material";
import { useEffect, useState } from "react";
import useRequest from "@/lib/hooks/useRequest";
import { debounce } from "@mui/material/utils";
import { GET_RAHDARANS_LIST_SEARCH } from "@/core/utils/routes";
const RahdarCode = ({ rahdarsCode, setRahdarsCode, error }) => {
const [inputValue, setInputValue] = useState(""); // مدیریت مقدار تایپ‌شده
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
const requestServer = useRequest();
const fetchRahdarCodes = (inputValue, controller) => {
const debouncer = debounce((query) => {
if ((query || "").length < 3) {
setOptions([]);
return;
}
setLoading(true);
requestServer(`${GET_RAHDARANS_LIST_SEARCH}?code=${query}`, "get", {
requestOptions: { signal: controller.signal },
})
.then((response) => {
setOptions(response.data.data || []);
})
.catch(() => {
setOptions([]);
})
.finally(() => {
setLoading(false);
});
}, 500);
debouncer(inputValue);
};
useEffect(() => {
const controller = new AbortController();
fetchRahdarCodes(inputValue, controller);
return () => controller.abort();
}, [inputValue]);
return (
<Autocomplete
multiple // قابلیت انتخاب چندگانه
value={rahdarsCode} // آرایه موارد انتخاب‌شده
size="small"
onChange={(event, newValue) => {
setRahdarsCode(newValue || []); // به‌روزرسانی موارد انتخاب‌شده
}}
inputValue={inputValue} // مقدار تایپ‌شده
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue || ""); // به‌روزرسانی مقدار تایپ‌شده
}}
options={options} // گزینه‌های موجود
getOptionLabel={(option) => (typeof option === "string" ? option : `${option.code} - ${option.name}`)}
isOptionEqualToValue={(option, value) => {
if (!value) return false;
if (value === "") return option.code === "";
return option.code === (value?.code || value);
}}
loading={loading}
loadingText="درحال جستجوی کد راهدار"
noOptionsText={
(inputValue || "").length < 3 ? "حداقل سه رقم از کد راهدار را وارد کنید" : "کد راهداری یافت نشد"
}
fullWidth
renderInput={(params) => (
<TextField
{...params}
label="کد راهداران"
fullWidth
error={!!error}
helperText={error?.message}
InputProps={{
...params.InputProps,
endAdornment: (
<>
{loading ? <CircularProgress size="25px" color="inherit" /> : null}
{params.InputProps.endAdornment}
</>
),
}}
/>
)}
/>
);
};
export default RahdarCode;

View File

@@ -0,0 +1,181 @@
"use client";
import { Marker, useMap } from "react-leaflet";
import { useEffect } from "react";
import L from "leaflet";
import AzmayeshActiveIcon from "@/assets/images/examine_marker_active.png";
import { Box, FormControl, InputAdornment, InputLabel, OutlinedInput, Stack, useMediaQuery } from "@mui/material";
import VerifiedIcon from "@mui/icons-material/Verified";
import { useTheme } from "@emotion/react";
const ShowLocationMarker = ({ start_lat, start_lng, end_lat, end_lng }) => {
const map = useMap();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const defaultIconSize = [35, 35];
const createCustomIcon = (size, iconUrl) => {
return L.icon({
iconUrl: iconUrl,
iconSize: size,
iconAnchor: [size[0] / 2, size[1]],
popupAnchor: [0, -size[1]],
});
};
useEffect(() => {
if (start_lat && end_lng) {
map.fitBounds(
[
{ lat: start_lat, lng: start_lng },
{ lat: end_lat, lng: end_lng },
],
{ paddingTopLeft: [16, 16], paddingBottomRight: [16, 130] }
);
} else if (start_lat) {
map.setView({ lat: start_lat, lng: start_lng }, 15);
}
}, [start_lat, start_lng, map, end_lng, end_lat]);
return (
<>
{start_lat && start_lng && (
<Marker
position={{ lat: start_lat, lng: start_lng }}
icon={createCustomIcon(defaultIconSize, AzmayeshActiveIcon.src)}
/>
)}
{end_lat && end_lng && (
<Marker
position={{ lat: end_lat, lng: end_lng }}
icon={createCustomIcon(defaultIconSize, AzmayeshActiveIcon.src)}
/>
)}
<Box
sx={{
zIndex: "400",
position: "absolute",
background: "#ffffff94",
p: 2,
borderRadius: "10px",
boxShadow: "rgba(0, 0, 0, 0.24) 0px 3px 8px",
bottom: "5px",
left: "5px",
right: "5px",
}}
>
<Stack direction={end_lat && end_lng ? "row" : "column"} spacing={end_lat && end_lng ? 0.5 : 0}>
{start_lat && start_lng && (
<Stack spacing={1}>
<FormControl size="small" variant="outlined">
<InputLabel sx={{ color: "success.main" }} htmlFor="start_lat">
{end_lat && end_lng ? "طول جغرافیایی شروع" : "طول جغرافیایی"}
</InputLabel>
<OutlinedInput
id="start_lat"
type="text"
size="small"
readOnly
fullWidth
sx={{
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "success.main",
},
color: "success.main",
}}
value={start_lat}
endAdornment={
<InputAdornment position="end">
<VerifiedIcon color="success" />
</InputAdornment>
}
label={end_lat && end_lng ? "طول جغرافیایی شروع" : "طول جغرافیایی"}
/>
</FormControl>
<FormControl size="small" variant="outlined">
<InputLabel sx={{ color: "success.main" }} htmlFor="start_lng">
{end_lat && end_lng ? "عرض جغرافیایی شروع" : "عرض جغرافیایی"}
</InputLabel>
<OutlinedInput
id="start_lng"
type="text"
size="small"
readOnly
fullWidth
sx={{
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "success.main",
},
color: "success.main",
}}
value={start_lng}
endAdornment={
<InputAdornment position="end">
<VerifiedIcon color="success" />
</InputAdornment>
}
label={end_lat && end_lng ? "عرض جغرافیایی شروع" : "عرض جغرافیایی"}
/>
</FormControl>
</Stack>
)}
{end_lat && end_lng && (
<Stack spacing={1}>
<FormControl size="small" variant="outlined">
<InputLabel sx={{ color: "success.main" }} htmlFor="end_lat">
طول جغرافیایی پایان
</InputLabel>
<OutlinedInput
id="end_lat"
type="text"
size="small"
readOnly
sx={{
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "success.main",
},
color: "success.main",
}}
value={end_lat}
endAdornment={
<InputAdornment position="end">
<VerifiedIcon color="success" />
</InputAdornment>
}
label="طول جغرافیایی پایان"
/>
</FormControl>
<FormControl size="small" variant="outlined">
<InputLabel sx={{ color: "success.main" }} htmlFor="end_lng">
عرض جغرافیایی پایان
</InputLabel>
<OutlinedInput
id="end_lng"
type="text"
size="small"
readOnly
sx={{
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "success.main",
},
color: "success.main",
}}
value={end_lng}
endAdornment={
<InputAdornment position="end">
<VerifiedIcon color="success" />
</InputAdornment>
}
label="عرض جغرافیایی پایان"
/>
</FormControl>
</Stack>
)}
</Stack>
</Box>
</>
);
};
export default ShowLocationMarker;

View File

@@ -61,6 +61,35 @@ export const errorUnauthorizedToast = (toastContainer) =>
draggable: true,
}
);
export const errorAccessDeniedToast = (message, toastContainer) =>
toast.error(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Dangerous color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">{message || "Access Denied"}</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,
closeOnClick: false,
draggable: true,
}
);
export const errorLogicToast = (message, toastContainer) =>
toast.error(

View File

@@ -0,0 +1,94 @@
import { Box, Button, Paper, Typography } from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
import { useRef } from "react";
const UploadSystem = ({ selectedImage, handleUploadChange, imageSize, fileType, showAddIcon }) => {
const fileInputRef = useRef(null);
const handleClick = () => {
fileInputRef.current.click();
};
return (
<Box sx={{ width: "100%", my: 1 }}>
{showAddIcon ? (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
border: "1px solid #b3b3b3",
borderRadius: "10px",
cursor: "pointer",
padding: "5px",
height: imageSize[1],
}}
onClick={handleClick}
>
<AddIcon sx={{ fontSize: "2rem", color: "#a19d9d" }} />
<Typography
variant="subtitle2"
sx={{
fontWeight: 600,
fontSize: "0.8rem",
color: "#a19d9d",
mt: 1,
}}
textAlign="center"
>
فرمت قابل قبول : png, jpg
<br />
حداکثر 3Mb
</Typography>
</Box>
) : (
<>
{fileType && fileType.startsWith("image/") && (
<Box
width="100%"
height={imageSize[1]}
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
objectFit: "contain",
border: "1px solid #b3b3b3",
borderRadius: "10px",
padding: "5px",
overflow: "hidden",
}}
onClick={handleClick}
>
<Paper
elevation={0}
sx={{
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundSize: "contain",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundImage: `url(${selectedImage})`,
}}
/>
</Box>
)}
</>
)}
<input
type="file"
accept="image/*"
style={{ display: "none" }}
onChange={handleUploadChange}
ref={fileInputRef}
/>
</Box>
);
};
export default UploadSystem;