Latest version #12
@ -62,7 +62,6 @@ export const ArticleEditModal = observer(() => {
|
|||||||
|
|
||||||
// Load existing media files when editing an article
|
// Load existing media files when editing an article
|
||||||
const loadExistingMedia = async () => {
|
const loadExistingMedia = async () => {
|
||||||
console.log("Called loadExistingMedia");
|
|
||||||
if (selectedArticleId) {
|
if (selectedArticleId) {
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.get(
|
const response = await axiosInstance.get(
|
||||||
|
@ -50,8 +50,12 @@ const style = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const StationEditModal = observer(() => {
|
export const StationEditModal = observer(() => {
|
||||||
const { stationModalOpen, setStationModalOpenAction, selectedStationId, selectedRouteId } =
|
const {
|
||||||
stationStore;
|
stationModalOpen,
|
||||||
|
setStationModalOpenAction,
|
||||||
|
selectedStationId,
|
||||||
|
selectedRouteId,
|
||||||
|
} = stationStore;
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -63,10 +67,9 @@ export const StationEditModal = observer(() => {
|
|||||||
const apiUrl = useApiUrl();
|
const apiUrl = useApiUrl();
|
||||||
|
|
||||||
const { data: stationQuery, isLoading: isStationLoading } = useCustom({
|
const { data: stationQuery, isLoading: isStationLoading } = useCustom({
|
||||||
url: `${apiUrl}/route/${selectedRouteId ?? 1}/station`,
|
url: `${apiUrl}/route/${selectedRouteId ?? 1}/station`,
|
||||||
method: 'get'
|
method: "get",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -84,7 +87,6 @@ export const StationEditModal = observer(() => {
|
|||||||
id: "",
|
id: "",
|
||||||
redirect: false,
|
redirect: false,
|
||||||
onMutationSuccess: (data) => {
|
onMutationSuccess: (data) => {
|
||||||
console.log(data);
|
|
||||||
setStationModalOpenAction(false);
|
setStationModalOpenAction(false);
|
||||||
reset();
|
reset();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
@ -99,14 +101,14 @@ export const StationEditModal = observer(() => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (stationModalOpen) {
|
if (stationModalOpen) {
|
||||||
const station = stationQuery?.data?.find((station: StationItem) => station.id === selectedStationId);
|
const station = stationQuery?.data?.find(
|
||||||
if(!station) return;
|
(station: StationItem) => station.id === selectedStationId
|
||||||
for(const key in station) {
|
);
|
||||||
|
if (!station) return;
|
||||||
|
for (const key in station) {
|
||||||
setValue(key, station[key]);
|
setValue(key, station[key]);
|
||||||
console.log(key, station[key]);
|
|
||||||
}
|
}
|
||||||
setValue("station_id", station.id);
|
setValue("station_id", station.id);
|
||||||
console.log(stationQuery);
|
|
||||||
}
|
}
|
||||||
}, [stationModalOpen, stationQuery]);
|
}, [stationModalOpen, stationQuery]);
|
||||||
|
|
||||||
|
@ -4,94 +4,102 @@ import { ReactPhotoSphereViewer } from "react-photo-sphere-viewer";
|
|||||||
import { ModelViewer } from "./ModelViewer";
|
import { ModelViewer } from "./ModelViewer";
|
||||||
|
|
||||||
export interface MediaData {
|
export interface MediaData {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
media_type: number;
|
media_type: number;
|
||||||
filename?: string;
|
filename?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MediaView({media} : Readonly<{media?: MediaData}>) {
|
export function MediaView({ media }: Readonly<{ media?: MediaData }>) {
|
||||||
const token = localStorage.getItem(TOKEN_KEY);
|
const token = localStorage.getItem(TOKEN_KEY);
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{maxHeight: "300px", width: "100%", display: "flex", flexGrow: 1, justifyContent: "center"}}
|
sx={{
|
||||||
>
|
maxHeight: "300px",
|
||||||
{media?.media_type === 1 && (
|
width: "100%",
|
||||||
<img
|
height: "100%",
|
||||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
maxWidth: "300px",
|
||||||
media?.id
|
display: "flex",
|
||||||
}/download?token=${token}`}
|
flexGrow: 1,
|
||||||
alt={media?.filename}
|
justifyContent: "center",
|
||||||
style={{
|
}}
|
||||||
maxWidth: "100%",
|
>
|
||||||
height: "auto",
|
{media?.media_type === 1 && (
|
||||||
objectFit: "contain",
|
<img
|
||||||
borderRadius: 8,
|
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||||
}}
|
media?.id
|
||||||
/>
|
}/download?token=${token}`}
|
||||||
)}
|
alt={media?.filename}
|
||||||
|
style={{
|
||||||
|
maxWidth: "100%",
|
||||||
|
height: "auto",
|
||||||
|
objectFit: "contain",
|
||||||
|
borderRadius: 8,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{media?.media_type === 2 && (
|
{media?.media_type === 2 && (
|
||||||
<video
|
<video
|
||||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||||
media?.id
|
media?.id
|
||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
style={{
|
style={{
|
||||||
maxWidth: "100%",
|
maxWidth: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
borderRadius: 30,
|
borderRadius: 30,
|
||||||
}}
|
}}
|
||||||
controls
|
controls
|
||||||
autoPlay
|
autoPlay
|
||||||
muted
|
muted
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{media?.media_type === 3 && (
|
{media?.media_type === 3 && (
|
||||||
<img
|
<img
|
||||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||||
media?.id
|
media?.id
|
||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
alt={media?.filename}
|
alt={media?.filename}
|
||||||
style={{
|
style={{
|
||||||
maxWidth: "100%",
|
maxWidth: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{media?.media_type === 4 && (
|
{media?.media_type === 4 && (
|
||||||
<img
|
<img
|
||||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||||
media?.id
|
media?.id
|
||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
alt={media?.filename}
|
alt={media?.filename}
|
||||||
style={{
|
style={{
|
||||||
maxWidth: "100%",
|
maxWidth: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: "contain",
|
objectFit: "contain",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{media?.media_type === 5 && (
|
{media?.media_type === 5 && (
|
||||||
<ReactPhotoSphereViewer
|
<ReactPhotoSphereViewer
|
||||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||||
media?.id
|
media?.id
|
||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
width={"100%"}
|
width={"100%"}
|
||||||
height={"100%"}
|
height={"100%"}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{media?.media_type === 6 && (
|
{media?.media_type === 6 && (
|
||||||
<ModelViewer
|
<ModelViewer
|
||||||
fileUrl={`${import.meta.env.VITE_KRBL_MEDIA}${
|
fileUrl={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||||
media?.id
|
media?.id
|
||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
}
|
@ -1,222 +1,271 @@
|
|||||||
import { useCustom, useApiUrl } from "@refinedev/core";
|
import { useCustom, useApiUrl } from "@refinedev/core";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import { createContext, ReactNode, useContext, useEffect, useMemo, useState } from "react";
|
import {
|
||||||
import { RouteData, SightData, SightPatchData, StationData, StationPatchData } from "./types";
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import {
|
||||||
|
RouteData,
|
||||||
|
SightData,
|
||||||
|
SightPatchData,
|
||||||
|
StationData,
|
||||||
|
StationPatchData,
|
||||||
|
} from "./types";
|
||||||
import { axiosInstance } from "../../providers/data";
|
import { axiosInstance } from "../../providers/data";
|
||||||
|
|
||||||
const MapDataContext = createContext<{
|
const MapDataContext = createContext<{
|
||||||
originalRouteData?: RouteData,
|
originalRouteData?: RouteData;
|
||||||
originalStationData?: StationData[],
|
originalStationData?: StationData[];
|
||||||
originalSightData?: SightData[],
|
originalSightData?: SightData[];
|
||||||
routeData?: RouteData,
|
routeData?: RouteData;
|
||||||
stationData?: StationData[],
|
stationData?: StationData[];
|
||||||
sightData?: SightData[],
|
sightData?: SightData[];
|
||||||
|
|
||||||
isRouteLoading: boolean,
|
isRouteLoading: boolean;
|
||||||
isStationLoading: boolean,
|
isStationLoading: boolean;
|
||||||
isSightLoading: boolean,
|
isSightLoading: boolean;
|
||||||
setScaleRange: (min: number, max: number) => void,
|
setScaleRange: (min: number, max: number) => void;
|
||||||
setMapRotation: (rotation: number) => void,
|
setMapRotation: (rotation: number) => void;
|
||||||
setMapCenter: (x: number, y: number) => void,
|
setMapCenter: (x: number, y: number) => void;
|
||||||
setStationOffset: (stationId: number, x: number, y: number) => void,
|
setStationOffset: (stationId: number, x: number, y: number) => void;
|
||||||
setSightCoordinates: (sightId: number, latitude: number, longitude: number) => void,
|
setSightCoordinates: (
|
||||||
saveChanges: () => void,
|
sightId: number,
|
||||||
|
latitude: number,
|
||||||
|
longitude: number
|
||||||
|
) => void;
|
||||||
|
saveChanges: () => void;
|
||||||
}>({
|
}>({
|
||||||
originalRouteData: undefined,
|
originalRouteData: undefined,
|
||||||
originalStationData: undefined,
|
originalStationData: undefined,
|
||||||
originalSightData: undefined,
|
originalSightData: undefined,
|
||||||
routeData: undefined,
|
routeData: undefined,
|
||||||
stationData: undefined,
|
stationData: undefined,
|
||||||
sightData: undefined,
|
sightData: undefined,
|
||||||
|
|
||||||
isRouteLoading: true,
|
isRouteLoading: true,
|
||||||
isStationLoading: true,
|
isStationLoading: true,
|
||||||
isSightLoading: true,
|
isSightLoading: true,
|
||||||
setScaleRange: () => {},
|
setScaleRange: () => {},
|
||||||
setMapRotation: () => {},
|
setMapRotation: () => {},
|
||||||
setMapCenter: () => {},
|
setMapCenter: () => {},
|
||||||
setStationOffset: () => {},
|
setStationOffset: () => {},
|
||||||
setSightCoordinates: () => {},
|
setSightCoordinates: () => {},
|
||||||
saveChanges: () => {},
|
saveChanges: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function MapDataProvider({ children }: Readonly<{ children: ReactNode }>) {
|
export function MapDataProvider({
|
||||||
const { id: routeId } = useParams<{ id: string }>();
|
children,
|
||||||
const apiUrl = useApiUrl();
|
}: Readonly<{ children: ReactNode }>) {
|
||||||
|
const { id: routeId } = useParams<{ id: string }>();
|
||||||
|
const apiUrl = useApiUrl();
|
||||||
|
|
||||||
const [originalRouteData, setOriginalRouteData] = useState<RouteData>();
|
const [originalRouteData, setOriginalRouteData] = useState<RouteData>();
|
||||||
const [originalStationData, setOriginalStationData] = useState<StationData[]>();
|
const [originalStationData, setOriginalStationData] =
|
||||||
const [originalSightData, setOriginalSightData] = useState<SightData[]>();
|
useState<StationData[]>();
|
||||||
|
const [originalSightData, setOriginalSightData] = useState<SightData[]>();
|
||||||
|
|
||||||
const [routeData, setRouteData] = useState<RouteData>();
|
const [routeData, setRouteData] = useState<RouteData>();
|
||||||
const [stationData, setStationData] = useState<StationData[]>();
|
const [stationData, setStationData] = useState<StationData[]>();
|
||||||
const [sightData, setSightData] = useState<SightData[]>();
|
const [sightData, setSightData] = useState<SightData[]>();
|
||||||
|
|
||||||
const [routeChanges, setRouteChanges] = useState<RouteData>({} as RouteData);
|
const [routeChanges, setRouteChanges] = useState<RouteData>({} as RouteData);
|
||||||
const [stationChanges, setStationChanges] = useState<StationPatchData[]>([]);
|
const [stationChanges, setStationChanges] = useState<StationPatchData[]>([]);
|
||||||
const [sightChanges, setSightChanges] = useState<SightPatchData[]>([]);
|
const [sightChanges, setSightChanges] = useState<SightPatchData[]>([]);
|
||||||
|
|
||||||
|
const { data: routeQuery, isLoading: isRouteLoading } = useCustom({
|
||||||
|
url: `${apiUrl}/route/${routeId}`,
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
const { data: stationQuery, isLoading: isStationLoading } = useCustom({
|
||||||
|
url: `${apiUrl}/route/${routeId}/station`,
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
const { data: sightQuery, isLoading: isSightLoading } = useCustom({
|
||||||
|
url: `${apiUrl}/route/${routeId}/sight`,
|
||||||
|
method: "get",
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
// if not undefined, set original data
|
||||||
|
if (routeQuery?.data) setOriginalRouteData(routeQuery.data as RouteData);
|
||||||
|
if (stationQuery?.data)
|
||||||
|
setOriginalStationData(stationQuery.data as StationData[]);
|
||||||
|
if (sightQuery?.data) setOriginalSightData(sightQuery.data as SightData[]);
|
||||||
|
console.log("queries", routeQuery, stationQuery, sightQuery);
|
||||||
|
}, [routeQuery, stationQuery, sightQuery]);
|
||||||
|
|
||||||
const { data: routeQuery, isLoading: isRouteLoading } = useCustom({
|
useEffect(() => {
|
||||||
url: `${apiUrl}/route/${routeId}`,
|
// combine changes with original data
|
||||||
method: 'get',
|
if (originalRouteData)
|
||||||
});
|
setRouteData({ ...originalRouteData, ...routeChanges });
|
||||||
const { data: stationQuery, isLoading: isStationLoading } = useCustom({
|
if (originalStationData) setStationData(originalStationData);
|
||||||
url: `${apiUrl}/route/${routeId}/station`,
|
if (originalSightData) setSightData(originalSightData);
|
||||||
method: 'get'
|
}, [
|
||||||
});
|
originalRouteData,
|
||||||
const { data: sightQuery, isLoading: isSightLoading } = useCustom({
|
originalStationData,
|
||||||
url: `${apiUrl}/route/${routeId}/sight`,
|
originalSightData,
|
||||||
method: 'get',
|
routeChanges,
|
||||||
});
|
stationChanges,
|
||||||
useEffect(() => {
|
sightChanges,
|
||||||
// if not undefined, set original data
|
]);
|
||||||
if(routeQuery?.data) setOriginalRouteData(routeQuery.data as RouteData);
|
|
||||||
if(stationQuery?.data) setOriginalStationData(stationQuery.data as StationData[]);
|
|
||||||
if(sightQuery?.data) setOriginalSightData(sightQuery.data as SightData[]);
|
|
||||||
console.log("queries", routeQuery, stationQuery, sightQuery);
|
|
||||||
}, [routeQuery, stationQuery, sightQuery]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
function setScaleRange(min: number, max: number) {
|
||||||
// combine changes with original data
|
setRouteChanges((prev) => {
|
||||||
if(originalRouteData) setRouteData({...originalRouteData, ...routeChanges});
|
return { ...prev, scale_min: min, scale_max: max };
|
||||||
if(originalStationData) setStationData(originalStationData);
|
});
|
||||||
if(originalSightData) setSightData(originalSightData);
|
}
|
||||||
}, [
|
|
||||||
originalRouteData, originalStationData, originalSightData,
|
|
||||||
routeChanges, stationChanges, sightChanges
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
function setMapRotation(rotation: number) {
|
||||||
console.log("data", routeData, stationData, sightData);
|
setRouteChanges((prev) => {
|
||||||
}, [routeData, stationData, sightData]);
|
return { ...prev, rotate: rotation };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function setScaleRange(min: number, max: number) {
|
function setMapCenter(x: number, y: number) {
|
||||||
setRouteChanges((prev) => {
|
setRouteChanges((prev) => {
|
||||||
return {...prev, scale_min: min, scale_max: max};
|
return { ...prev, center_latitude: x, center_longitude: y };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMapRotation(rotation: number) {
|
async function saveChanges() {
|
||||||
setRouteChanges((prev) => {
|
await axiosInstance.patch(`/route/${routeId}`, routeData);
|
||||||
return {...prev, rotate: rotation};
|
await saveStationChanges();
|
||||||
});
|
await saveSightChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMapCenter(x: number, y: number) {
|
async function saveStationChanges() {
|
||||||
setRouteChanges((prev) => {
|
for (const station of stationChanges) {
|
||||||
return {...prev, center_latitude: x, center_longitude: y}
|
const response = await axiosInstance.patch(
|
||||||
});
|
`/route/${routeId}/station`,
|
||||||
}
|
station
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function saveChanges() {
|
async function saveSightChanges() {
|
||||||
await axiosInstance.patch(`/route/${routeId}`, routeData);
|
console.log("sightChanges", sightChanges);
|
||||||
await saveStationChanges();
|
for (const sight of sightChanges) {
|
||||||
await saveSightChanges();
|
const response = await axiosInstance.patch(
|
||||||
}
|
`/route/${routeId}/sight`,
|
||||||
|
sight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function saveStationChanges() {
|
function setStationOffset(stationId: number, x: number, y: number) {
|
||||||
for(const station of stationChanges) {
|
setStationChanges((prev) => {
|
||||||
const response = await axiosInstance.patch(`/route/${routeId}/station`, station);
|
let found = prev.find((station) => station.station_id === stationId);
|
||||||
console.log("response", response);
|
if (found) {
|
||||||
}
|
found.offset_x = x;
|
||||||
}
|
found.offset_y = y;
|
||||||
|
|
||||||
async function saveSightChanges() {
|
return prev.map((station) => {
|
||||||
console.log("sightChanges", sightChanges);
|
if (station.station_id === stationId) {
|
||||||
for(const sight of sightChanges) {
|
return found;
|
||||||
const response = await axiosInstance.patch(`/route/${routeId}/sight`, sight);
|
}
|
||||||
console.log("response", response);
|
return station;
|
||||||
}
|
});
|
||||||
}
|
} else {
|
||||||
|
const foundStation = stationData?.find(
|
||||||
|
(station) => station.id === stationId
|
||||||
|
);
|
||||||
|
if (foundStation) {
|
||||||
|
return [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
station_id: stationId,
|
||||||
|
offset_x: x,
|
||||||
|
offset_y: y,
|
||||||
|
transfers: foundStation.transfers,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function setStationOffset(stationId: number, x: number, y: number) {
|
function setSightCoordinates(
|
||||||
setStationChanges((prev) => {
|
sightId: number,
|
||||||
let found = prev.find((station) => station.station_id === stationId);
|
latitude: number,
|
||||||
if(found) {
|
longitude: number
|
||||||
found.offset_x = x;
|
) {
|
||||||
found.offset_y = y;
|
setSightChanges((prev) => {
|
||||||
|
let found = prev.find((sight) => sight.sight_id === sightId);
|
||||||
|
if (found) {
|
||||||
|
found.latitude = latitude;
|
||||||
|
found.longitude = longitude;
|
||||||
|
|
||||||
return prev.map((station) => {
|
return prev.map((sight) => {
|
||||||
if(station.station_id === stationId) {
|
if (sight.sight_id === sightId) {
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
return station;
|
return sight;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const foundStation = stationData?.find((station) => station.id === stationId);
|
const foundSight = sightData?.find((sight) => sight.id === sightId);
|
||||||
if(foundStation) {
|
if (foundSight) {
|
||||||
return [...prev, {
|
return [
|
||||||
station_id: stationId,
|
...prev,
|
||||||
offset_x: x, offset_y: y,
|
{
|
||||||
transfers: foundStation.transfers
|
sight_id: sightId,
|
||||||
}];
|
latitude,
|
||||||
}
|
longitude,
|
||||||
return prev;
|
},
|
||||||
}
|
];
|
||||||
});
|
}
|
||||||
}
|
return prev;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function setSightCoordinates(sightId: number, latitude: number, longitude: number) {
|
useEffect(() => {
|
||||||
setSightChanges((prev) => {
|
console.log("sightChanges", sightChanges);
|
||||||
let found = prev.find((sight) => sight.sight_id === sightId);
|
}, [sightChanges]);
|
||||||
if(found) {
|
|
||||||
found.latitude = latitude;
|
|
||||||
found.longitude = longitude;
|
|
||||||
|
|
||||||
return prev.map((sight) => {
|
const value = useMemo(
|
||||||
if(sight.sight_id === sightId) {
|
() => ({
|
||||||
return found;
|
originalRouteData: originalRouteData,
|
||||||
}
|
originalStationData: originalStationData,
|
||||||
return sight;
|
originalSightData: originalSightData,
|
||||||
});
|
routeData: routeData,
|
||||||
} else {
|
stationData: stationData,
|
||||||
const foundSight = sightData?.find((sight) => sight.id === sightId);
|
sightData: sightData,
|
||||||
if(foundSight) {
|
isRouteLoading,
|
||||||
return [...prev, {
|
isStationLoading,
|
||||||
sight_id: sightId,
|
isSightLoading,
|
||||||
latitude,
|
setScaleRange,
|
||||||
longitude
|
setMapRotation,
|
||||||
}];
|
setMapCenter,
|
||||||
}
|
saveChanges,
|
||||||
return prev;
|
setStationOffset,
|
||||||
}
|
setSightCoordinates,
|
||||||
});
|
}),
|
||||||
}
|
[
|
||||||
|
originalRouteData,
|
||||||
|
originalStationData,
|
||||||
|
originalSightData,
|
||||||
|
routeData,
|
||||||
|
stationData,
|
||||||
|
sightData,
|
||||||
|
isRouteLoading,
|
||||||
|
isStationLoading,
|
||||||
|
isSightLoading,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
console.log("sightChanges", sightChanges);
|
<MapDataContext.Provider value={value}>{children}</MapDataContext.Provider>
|
||||||
}, [sightChanges]);
|
);
|
||||||
|
|
||||||
const value = useMemo(() => ({
|
|
||||||
originalRouteData: originalRouteData,
|
|
||||||
originalStationData: originalStationData,
|
|
||||||
originalSightData: originalSightData,
|
|
||||||
routeData: routeData,
|
|
||||||
stationData: stationData,
|
|
||||||
sightData: sightData,
|
|
||||||
isRouteLoading,
|
|
||||||
isStationLoading,
|
|
||||||
isSightLoading,
|
|
||||||
setScaleRange,
|
|
||||||
setMapRotation,
|
|
||||||
setMapCenter,
|
|
||||||
saveChanges,
|
|
||||||
setStationOffset,
|
|
||||||
setSightCoordinates
|
|
||||||
}), [originalRouteData, originalStationData, originalSightData, routeData, stationData, sightData, isRouteLoading, isStationLoading, isSightLoading]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MapDataContext.Provider value={value}>
|
|
||||||
{children}
|
|
||||||
</MapDataContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useMapData = () => {
|
export const useMapData = () => {
|
||||||
const context = useContext(MapDataContext);
|
const context = useContext(MapDataContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('useMapData must be used within a MapDataProvider');
|
throw new Error("useMapData must be used within a MapDataProvider");
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
};
|
};
|
@ -277,14 +277,6 @@ export const RouteCreate = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Typography
|
|
||||||
variant="caption"
|
|
||||||
color="textSecondary"
|
|
||||||
sx={{ mt: 0, mb: 1 }}
|
|
||||||
>
|
|
||||||
{routeDirection ? "Прямой" : "Обратный"}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
{...register("scale_min", {
|
{...register("scale_min", {
|
||||||
// required: 'Это поле является обязательным',
|
// required: 'Это поле является обязательным',
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { Create, useAutocomplete } from "@refinedev/mui";
|
import { Create, useAutocomplete } from "@refinedev/mui";
|
||||||
import { useForm } from "@refinedev/react-hook-form";
|
import { useForm } from "@refinedev/react-hook-form";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import { Controller } from "react-hook-form";
|
import { Controller } from "react-hook-form";
|
||||||
|
|
||||||
const TRANSFER_FIELDS = [
|
const TRANSFER_FIELDS = [
|
||||||
@ -29,14 +30,64 @@ export const StationCreate = () => {
|
|||||||
saveButtonProps,
|
saveButtonProps,
|
||||||
refineCore: { formLoading },
|
refineCore: { formLoading },
|
||||||
register,
|
register,
|
||||||
|
setValue,
|
||||||
control,
|
control,
|
||||||
|
getValues,
|
||||||
|
watch,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm({
|
} = useForm({
|
||||||
refineCoreProps: {
|
refineCoreProps: {
|
||||||
resource: "station/",
|
resource: "station",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [coordinatesPreview, setCoordinatesPreview] = useState({
|
||||||
|
latitude: "",
|
||||||
|
longitude: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCoordinatesChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const [lat, lon] = e.target.value.split(",").map((s) => s.trim());
|
||||||
|
setCoordinatesPreview({
|
||||||
|
latitude: lat,
|
||||||
|
longitude: lon,
|
||||||
|
});
|
||||||
|
setValue("latitude", lat);
|
||||||
|
setValue("longitude", lon);
|
||||||
|
};
|
||||||
|
const latitudeContent = watch("latitude");
|
||||||
|
const longitudeContent = watch("longitude");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCoordinatesPreview({
|
||||||
|
latitude: latitudeContent || "",
|
||||||
|
longitude: longitudeContent || "",
|
||||||
|
});
|
||||||
|
}, [latitudeContent, longitudeContent]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const latitude = getValues("latitude");
|
||||||
|
const longitude = getValues("longitude");
|
||||||
|
if (latitude && longitude) {
|
||||||
|
setCoordinatesPreview({
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [getValues]);
|
||||||
|
const directions = [
|
||||||
|
{
|
||||||
|
label: "Прямой",
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Обратный",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const [routeDirection, setRouteDirection] = useState(false);
|
||||||
|
|
||||||
const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({
|
const { autocompleteProps: cityAutocompleteProps } = useAutocomplete({
|
||||||
resource: "city",
|
resource: "city",
|
||||||
onSearch: (value) => [
|
onSearch: (value) => [
|
||||||
@ -108,50 +159,68 @@ export const StationCreate = () => {
|
|||||||
label={"Описание"}
|
label={"Описание"}
|
||||||
name="description"
|
name="description"
|
||||||
/>
|
/>
|
||||||
<Controller
|
<input
|
||||||
name="direction" // boolean
|
type="hidden"
|
||||||
control={control}
|
{...register("direction", {
|
||||||
defaultValue={false}
|
value: routeDirection,
|
||||||
render={({ field }: { field: any }) => (
|
})}
|
||||||
<FormControlLabel
|
/>
|
||||||
label="Прямой маршрут?"
|
|
||||||
control={
|
<Autocomplete
|
||||||
<Checkbox
|
options={directions}
|
||||||
{...field}
|
defaultValue={directions.find((el) => el.value == false)}
|
||||||
checked={field.value}
|
onChange={(_, element) => {
|
||||||
onChange={(e) => field.onChange(e.target.checked)}
|
if (element) {
|
||||||
/>
|
setValue("direction", element.value);
|
||||||
}
|
setRouteDirection(element.value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Прямой/обратный маршрут"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
error={!!errors.arms}
|
||||||
|
helperText={(errors as any)?.arms?.message}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
{...register("latitude", {
|
value={`${coordinatesPreview.latitude}, ${coordinatesPreview.longitude}`}
|
||||||
required: "Это поле является обязательным",
|
onChange={handleCoordinatesChange}
|
||||||
valueAsNumber: true,
|
|
||||||
})}
|
|
||||||
error={!!(errors as any)?.latitude}
|
error={!!(errors as any)?.latitude}
|
||||||
helperText={(errors as any)?.latitude?.message}
|
helperText={(errors as any)?.latitude?.message}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth
|
fullWidth
|
||||||
InputLabelProps={{ shrink: true }}
|
InputLabelProps={{ shrink: true }}
|
||||||
type="number"
|
type="text"
|
||||||
label={"Широта *"}
|
label={"Координаты *"}
|
||||||
name="latitude"
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<input
|
||||||
{...register("longitude", {
|
type="hidden"
|
||||||
required: "Это поле является обязательным",
|
{...register("latitude", {
|
||||||
valueAsNumber: true,
|
value: coordinatesPreview.latitude,
|
||||||
|
setValueAs: (value) => {
|
||||||
|
if (value === "") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Number(value);
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
{...register("longitude", {
|
||||||
|
value: coordinatesPreview.longitude,
|
||||||
|
setValueAs: (value) => {
|
||||||
|
if (value === "") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Number(value);
|
||||||
|
},
|
||||||
})}
|
})}
|
||||||
error={!!(errors as any)?.longitude}
|
|
||||||
helperText={(errors as any)?.longitude?.message}
|
|
||||||
margin="normal"
|
|
||||||
fullWidth
|
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
type="number"
|
|
||||||
label={"Долгота *"}
|
|
||||||
name="longitude"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
@ -196,58 +265,70 @@ export const StationCreate = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<Box sx={{ visibility: "hidden" }}>
|
||||||
{...register("offset_x", {
|
<TextField
|
||||||
// required: 'Это поле является обязательным',
|
{...register("offset_x", {
|
||||||
})}
|
// required: 'Это поле является обязательным',
|
||||||
error={!!(errors as any)?.offset_x}
|
setValueAs: (value) => {
|
||||||
helperText={(errors as any)?.offset_x?.message}
|
if (value === "") {
|
||||||
margin="normal"
|
return 0;
|
||||||
fullWidth
|
}
|
||||||
InputLabelProps={{ shrink: true }}
|
},
|
||||||
type="number"
|
})}
|
||||||
label={"Смещение (X)"}
|
error={!!(errors as any)?.offset_x}
|
||||||
name="offset_x"
|
helperText={(errors as any)?.offset_x?.message}
|
||||||
/>
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{ shrink: true }}
|
||||||
|
type="number"
|
||||||
|
label={"Смещение (X)"}
|
||||||
|
name="offset_x"
|
||||||
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
{...register("offset_y", {
|
{...register("offset_y", {
|
||||||
// required: 'Это поле является обязательным',
|
setValueAs: (value) => {
|
||||||
})}
|
if (value === "") {
|
||||||
error={!!(errors as any)?.offset_y}
|
return 0;
|
||||||
helperText={(errors as any)?.offset_y?.message}
|
}
|
||||||
margin="normal"
|
},
|
||||||
fullWidth
|
// required: 'Это поле является обязательным',
|
||||||
InputLabelProps={{ shrink: true }}
|
})}
|
||||||
type="number"
|
error={!!(errors as any)?.offset_y}
|
||||||
label={"Смещение (Y)"}
|
helperText={(errors as any)?.offset_y?.message}
|
||||||
name="offset_y"
|
margin="normal"
|
||||||
/>
|
fullWidth
|
||||||
|
InputLabelProps={{ shrink: true }}
|
||||||
{/* Группа полей пересадок */}
|
type="number"
|
||||||
<Paper sx={{ p: 2, mt: 2 }}>
|
label={"Смещение (Y)"}
|
||||||
<Typography variant="h6" gutterBottom>
|
name="offset_y"
|
||||||
Пересадки
|
/>
|
||||||
</Typography>
|
</Box>
|
||||||
<Grid container spacing={2}>
|
|
||||||
{TRANSFER_FIELDS.map((field) => (
|
|
||||||
<Grid item xs={12} sm={6} md={4} key={field.name}>
|
|
||||||
<TextField
|
|
||||||
{...register(`transfers.${field.name}`)}
|
|
||||||
error={!!(errors as any)?.transfers?.[field.name]}
|
|
||||||
helperText={(errors as any)?.transfers?.[field.name]?.message}
|
|
||||||
margin="normal"
|
|
||||||
fullWidth
|
|
||||||
InputLabelProps={{ shrink: true }}
|
|
||||||
type="text"
|
|
||||||
label={field.label}
|
|
||||||
name={`transfers.${field.name}`}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{/* Группа полей пересадок */}
|
||||||
|
<Paper hidden sx={{ p: 2, mt: 2 }}>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Пересадки
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{TRANSFER_FIELDS.map((field) => (
|
||||||
|
<Grid item xs={12} sm={6} md={4} key={field.name}>
|
||||||
|
<TextField
|
||||||
|
{...register(`transfers.${field.name}`)}
|
||||||
|
error={!!(errors as any)?.transfers?.[field.name]}
|
||||||
|
helperText={(errors as any)?.transfers?.[field.name]?.message}
|
||||||
|
margin="normal"
|
||||||
|
fullWidth
|
||||||
|
InputLabelProps={{ shrink: true }}
|
||||||
|
type="text"
|
||||||
|
label={field.label}
|
||||||
|
name={`transfers.${field.name}`}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
</Create>
|
</Create>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -96,6 +96,26 @@ export const StationEdit = observer(() => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const directions = [
|
||||||
|
{
|
||||||
|
label: "Прямой",
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Обратный",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const directionContent = watch("direction");
|
||||||
|
const [routeDirection, setRouteDirection] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (directionContent) {
|
||||||
|
setRouteDirection(directionContent);
|
||||||
|
}
|
||||||
|
}, [directionContent]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (stationData[language as keyof typeof stationData]?.name) {
|
if (stationData[language as keyof typeof stationData]?.name) {
|
||||||
setValue("name", stationData[language as keyof typeof stationData]?.name);
|
setValue("name", stationData[language as keyof typeof stationData]?.name);
|
||||||
@ -172,17 +192,6 @@ export const StationEdit = observer(() => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const latitude = getValues("latitude");
|
|
||||||
const longitude = getValues("longitude");
|
|
||||||
if (latitude && longitude) {
|
|
||||||
setCoordinatesPreview({
|
|
||||||
latitude: latitude,
|
|
||||||
longitude: longitude,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [getValues]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Edit saveButtonProps={saveButtonProps}>
|
<Edit saveButtonProps={saveButtonProps}>
|
||||||
<Box
|
<Box
|
||||||
@ -217,20 +226,29 @@ export const StationEdit = observer(() => {
|
|||||||
label={"Системное название *"}
|
label={"Системное название *"}
|
||||||
name="system_name"
|
name="system_name"
|
||||||
/>
|
/>
|
||||||
<Controller
|
|
||||||
name="direction" // boolean
|
<input
|
||||||
control={control}
|
type="hidden"
|
||||||
defaultValue={false}
|
{...register("direction", { value: routeDirection })}
|
||||||
render={({ field }: { field: any }) => (
|
/>
|
||||||
<FormControlLabel
|
<Autocomplete
|
||||||
label="Прямой маршрут?"
|
options={directions}
|
||||||
control={
|
value={directions.find((el) => el.value == routeDirection)}
|
||||||
<Checkbox
|
onChange={(_, element) => {
|
||||||
{...field}
|
if (element) {
|
||||||
checked={field.value}
|
setValue("direction", element.value);
|
||||||
onChange={(e) => field.onChange(e.target.checked)}
|
setRouteDirection(element.value);
|
||||||
/>
|
}
|
||||||
}
|
}}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label="Прямой/обратный маршрут"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
error={!!errors.direction}
|
||||||
|
helperText={(errors as any)?.direction?.message}
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -61,6 +61,7 @@ export const VehicleList = observer(() => {
|
|||||||
type: "string",
|
type: "string",
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
flex: 1,
|
||||||
align: "left",
|
align: "left",
|
||||||
headerAlign: "left",
|
headerAlign: "left",
|
||||||
renderCell: (params) => {
|
renderCell: (params) => {
|
||||||
@ -70,14 +71,14 @@ export const VehicleList = observer(() => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
field: "city",
|
// field: "city",
|
||||||
headerName: "Город",
|
// headerName: "Город",
|
||||||
type: "string",
|
// type: "string",
|
||||||
align: "left",
|
// align: "left",
|
||||||
headerAlign: "left",
|
// headerAlign: "left",
|
||||||
flex: 1,
|
// flex: 1,
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
field: "actions",
|
field: "actions",
|
||||||
headerName: "Действия",
|
headerName: "Действия",
|
||||||
@ -110,7 +111,6 @@ export const VehicleList = observer(() => {
|
|||||||
<List>
|
<List>
|
||||||
<CustomDataGrid
|
<CustomDataGrid
|
||||||
{...dataGridProps}
|
{...dataGridProps}
|
||||||
languageEnabled
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
localeText={localeText}
|
localeText={localeText}
|
||||||
getRowId={(row: any) => row.id}
|
getRowId={(row: any) => row.id}
|
||||||
|
Loading…
Reference in New Issue
Block a user