feat: Move route-preview

This commit is contained in:
2025-06-09 09:31:26 +03:00
parent f4544c1888
commit 64c15b2622
17 changed files with 2143 additions and 43 deletions

View File

@@ -0,0 +1,307 @@
import { useParams } from "react-router";
import { authInstance, languageInstance } from "@shared";
import {
createContext,
ReactNode,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import {
RouteData,
SightData,
SightPatchData,
StationData,
StationPatchData,
} from "./types";
import { observer } from "mobx-react-lite";
const MapDataContext = createContext<{
originalRouteData?: RouteData;
originalStationData?: StationData[];
originalSightData?: SightData[];
routeData?: RouteData;
stationData?: StationDataWithLanguage;
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;
}>({
originalRouteData: undefined,
originalStationData: undefined,
originalSightData: undefined,
routeData: undefined,
stationData: undefined,
sightData: undefined,
isRouteLoading: true,
isStationLoading: true,
isSightLoading: true,
setScaleRange: () => {},
setMapRotation: () => {},
setMapCenter: () => {},
setStationOffset: () => {},
setSightCoordinates: () => {},
saveChanges: () => {},
});
type StationDataWithLanguage = {
[key: string]: StationData[];
};
export const MapDataProvider = observer(
({ children }: Readonly<{ children: ReactNode }>) => {
const { id: routeId } = useParams<{ id: string }>();
const [originalRouteData, setOriginalRouteData] = useState<RouteData>();
const [originalStationData, setOriginalStationData] =
useState<StationData[]>();
const [originalSightData, setOriginalSightData] = useState<SightData[]>();
const [routeData, setRouteData] = useState<RouteData>();
const [stationData, setStationData] = useState<StationDataWithLanguage>({
RU: [],
EN: [],
ZH: [],
});
const [sightData, setSightData] = useState<SightData[]>();
const [routeChanges, setRouteChanges] = useState<Partial<RouteData>>({});
const [stationChanges, setStationChanges] = useState<StationPatchData[]>(
[]
);
const [sightChanges, setSightChanges] = useState<SightPatchData[]>([]);
const [isRouteLoading, setIsRouteLoading] = useState(true);
const [isStationLoading, setIsStationLoading] = useState(true);
const [isSightLoading, setIsSightLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
setIsRouteLoading(true);
setIsStationLoading(true);
setIsSightLoading(true);
const [
routeResponse,
ruStationResponse,
enStationResponse,
zhStationResponse,
sightResponse,
] = await Promise.all([
authInstance.get(`/route/${routeId}`),
languageInstance("ru").get(`/route/${routeId}/station`),
languageInstance("en").get(`/route/${routeId}/station`),
languageInstance("zh").get(`/route/${routeId}/station`),
authInstance.get(`/route/${routeId}/sight`),
]);
setOriginalRouteData(routeResponse.data as RouteData);
setOriginalStationData(ruStationResponse.data as StationData[]);
setStationData({
ru: ruStationResponse.data as StationData[],
en: enStationResponse.data as StationData[],
zh: zhStationResponse.data as StationData[],
});
setOriginalSightData(sightResponse as unknown as SightData[]);
setIsRouteLoading(false);
setIsStationLoading(false);
setIsSightLoading(false);
} catch (error) {
console.error("Error fetching data:", error);
setIsRouteLoading(false);
setIsStationLoading(false);
setIsSightLoading(false);
}
};
fetchData();
}, [routeId]);
useEffect(() => {
// combine changes with original data
if (originalRouteData)
setRouteData({ ...originalRouteData, ...routeChanges });
if (originalSightData) setSightData(originalSightData);
}, [
originalRouteData,
originalSightData,
routeChanges,
stationChanges,
sightChanges,
]);
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 setMapCenter(x: number, y: number) {
setRouteChanges((prev) => {
return { ...prev, center_latitude: x, center_longitude: y };
});
}
async function saveChanges() {
await authInstance.patch(`/route/${routeId}`, routeData);
await saveStationChanges();
await saveSightChanges();
}
async function saveStationChanges() {
for (const station of stationChanges) {
const response = await authInstance.patch(
`/route/${routeId}/station`,
station
);
}
}
async function saveSightChanges() {
console.log("sightChanges", sightChanges);
for (const sight of sightChanges) {
const response = await authInstance.patch(
`/route/${routeId}/sight`,
sight
);
}
}
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.ru?.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;
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]);
const value = useMemo(
() => ({
originalRouteData,
originalStationData,
originalSightData,
routeData,
stationData,
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 = () => {
const context = useContext(MapDataContext);
if (!context) {
throw new Error("useMapData must be used within a MapDataProvider");
}
return context;
};