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