feat: update transfers
This commit is contained in:
7
.env
7
.env
@@ -1,3 +1,4 @@
|
|||||||
VITE_API_URL='https://wn.krbl.ru'
|
VITE_API_URL='https://content.wn.polygon.unprism.ru'
|
||||||
VITE_REACT_APP ='https://wn.krbl.ru'
|
VITE_REACT_APP ='https://content.wn.polygon.unprism.ru'
|
||||||
VITE_KRBL_MEDIA='https://wn.krbl.ru/media/'
|
VITE_KRBL_MEDIA='https://content.wn.polygon.unprism.ru//media/'
|
||||||
|
VITE_NEED_AUTH='false'
|
||||||
@@ -51,7 +51,9 @@ import {
|
|||||||
|
|
||||||
const PublicRoute = ({ children }: { children: React.ReactNode }) => {
|
const PublicRoute = ({ children }: { children: React.ReactNode }) => {
|
||||||
const { isAuthenticated } = authStore;
|
const { isAuthenticated } = authStore;
|
||||||
if (isAuthenticated) {
|
const need_auth = import.meta.env.VITE_NEED_AUTH == "true";
|
||||||
|
|
||||||
|
if (isAuthenticated || !need_auth) {
|
||||||
return <Navigate to="/map" replace />;
|
return <Navigate to="/map" replace />;
|
||||||
}
|
}
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
@@ -59,13 +61,18 @@ const PublicRoute = ({ children }: { children: React.ReactNode }) => {
|
|||||||
|
|
||||||
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
||||||
const { isAuthenticated } = authStore;
|
const { isAuthenticated } = authStore;
|
||||||
|
const need_auth = import.meta.env.VITE_NEED_AUTH == "true";
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
if (!isAuthenticated) {
|
|
||||||
|
if (!isAuthenticated && need_auth) {
|
||||||
return <Navigate to="/login" replace />;
|
return <Navigate to="/login" replace />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.pathname === "/") {
|
if (location.pathname === "/") {
|
||||||
return <Navigate to="/map" replace />;
|
return <Navigate to="/map" replace />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ export const NavigationItemComponent: React.FC<NavigationItemProps> = ({
|
|||||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||||
const { payload } = authStore;
|
const { payload } = authStore;
|
||||||
|
|
||||||
|
const need_auth = import.meta.env.VITE_NEED_AUTH == "true";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const isAdmin = payload?.is_admin || false;
|
const isAdmin = payload?.is_admin || false || !need_auth;
|
||||||
|
|
||||||
const isActive = item.path ? location.pathname.startsWith(item.path) : false;
|
const isActive = item.path ? location.pathname.startsWith(item.path) : false;
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
languageStore,
|
languageStore,
|
||||||
routeStore,
|
routeStore,
|
||||||
selectedCityStore,
|
selectedCityStore,
|
||||||
|
stationsStore,
|
||||||
} from "@shared";
|
} from "@shared";
|
||||||
import { EditStationModal } from "../../widgets/modals/EditStationModal";
|
import { EditStationModal } from "../../widgets/modals/EditStationModal";
|
||||||
|
|
||||||
@@ -185,6 +186,19 @@ const LinkedItemsContentsInner = <
|
|||||||
setPosition(linkedItems.length + 1);
|
setPosition(linkedItems.length + 1);
|
||||||
}, [linkedItems.length]);
|
}, [linkedItems.length]);
|
||||||
|
|
||||||
|
const getStationTransfers = (stationId: number, fallbackTransfers?: any) => {
|
||||||
|
const { stationLists } = stationsStore;
|
||||||
|
for (const lang of ["ru", "en", "zh"] as const) {
|
||||||
|
const station = stationLists[lang].data.find(
|
||||||
|
(s: any) => s.id === stationId
|
||||||
|
);
|
||||||
|
if (station?.transfers) {
|
||||||
|
return station.transfers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallbackTransfers;
|
||||||
|
};
|
||||||
|
|
||||||
const onDragEnd = (result: DropResult) => {
|
const onDragEnd = (result: DropResult) => {
|
||||||
if (!result.destination) return;
|
if (!result.destination) return;
|
||||||
|
|
||||||
@@ -198,7 +212,14 @@ const LinkedItemsContentsInner = <
|
|||||||
|
|
||||||
authInstance
|
authInstance
|
||||||
.post(`/${parentResource}/${parentId}/${childResource}`, {
|
.post(`/${parentResource}/${parentId}/${childResource}`, {
|
||||||
stations: reorderedItems.map((item) => ({ id: item.id })),
|
stations: reorderedItems.map((item) => {
|
||||||
|
const stationData: any = { id: item.id };
|
||||||
|
const transfers = getStationTransfers(item.id, item.transfers);
|
||||||
|
if (transfers) {
|
||||||
|
stationData.transfers = transfers;
|
||||||
|
}
|
||||||
|
return stationData;
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Error updating station order:", error);
|
console.error("Error updating station order:", error);
|
||||||
@@ -245,11 +266,29 @@ const LinkedItemsContentsInner = <
|
|||||||
const linkItem = () => {
|
const linkItem = () => {
|
||||||
if (selectedItemId !== null) {
|
if (selectedItemId !== null) {
|
||||||
setError(null);
|
setError(null);
|
||||||
|
const selectedItem = allItems.find((item) => item.id === selectedItemId);
|
||||||
const requestData = {
|
const requestData = {
|
||||||
stations: insertAtPosition(
|
stations: insertAtPosition(
|
||||||
linkedItems.map((item) => ({ id: item.id })),
|
linkedItems.map((item) => {
|
||||||
|
const stationData: any = { id: item.id };
|
||||||
|
const transfers = getStationTransfers(item.id, item.transfers);
|
||||||
|
if (transfers) {
|
||||||
|
stationData.transfers = transfers;
|
||||||
|
}
|
||||||
|
return stationData;
|
||||||
|
}),
|
||||||
position,
|
position,
|
||||||
{ id: selectedItemId }
|
(() => {
|
||||||
|
const newStationData: any = { id: selectedItemId };
|
||||||
|
const transfers = getStationTransfers(
|
||||||
|
selectedItemId,
|
||||||
|
selectedItem?.transfers
|
||||||
|
);
|
||||||
|
if (transfers) {
|
||||||
|
newStationData.transfers = transfers;
|
||||||
|
}
|
||||||
|
return newStationData;
|
||||||
|
})()
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -331,10 +370,25 @@ const LinkedItemsContentsInner = <
|
|||||||
|
|
||||||
setError(null);
|
setError(null);
|
||||||
setIsLinkingBulk(true);
|
setIsLinkingBulk(true);
|
||||||
const selectedStations = Array.from(selectedItems).map((id) => ({ id }));
|
const selectedStations = Array.from(selectedItems).map((id) => {
|
||||||
|
const item = allItems.find((item) => item.id === id);
|
||||||
|
const stationData: any = { id };
|
||||||
|
const transfers = getStationTransfers(id, item?.transfers);
|
||||||
|
if (transfers) {
|
||||||
|
stationData.transfers = transfers;
|
||||||
|
}
|
||||||
|
return stationData;
|
||||||
|
});
|
||||||
const requestData = {
|
const requestData = {
|
||||||
stations: [
|
stations: [
|
||||||
...linkedItems.map((item) => ({ id: item.id })),
|
...linkedItems.map((item) => {
|
||||||
|
const stationData: any = { id: item.id };
|
||||||
|
const transfers = getStationTransfers(item.id, item.transfers);
|
||||||
|
if (transfers) {
|
||||||
|
stationData.transfers = transfers;
|
||||||
|
}
|
||||||
|
return stationData;
|
||||||
|
}),
|
||||||
...selectedStations,
|
...selectedStations,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,9 +8,14 @@ import {
|
|||||||
} from "@shared";
|
} from "@shared";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Eye, Pencil, Trash2, Minus } from "lucide-react";
|
import { Eye, Pencil, Trash2, Minus, Route } from "lucide-react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { CreateButton, DeleteModal, LanguageSwitcher } from "@widgets";
|
import {
|
||||||
|
CreateButton,
|
||||||
|
DeleteModal,
|
||||||
|
LanguageSwitcher,
|
||||||
|
EditStationTransfersModal,
|
||||||
|
} from "@widgets";
|
||||||
import { Box, CircularProgress } from "@mui/material";
|
import { Box, CircularProgress } from "@mui/material";
|
||||||
|
|
||||||
export const StationListPage = observer(() => {
|
export const StationListPage = observer(() => {
|
||||||
@@ -18,7 +23,11 @@ export const StationListPage = observer(() => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
const [isBulkDeleteModalOpen, setIsBulkDeleteModalOpen] = useState(false);
|
const [isBulkDeleteModalOpen, setIsBulkDeleteModalOpen] = useState(false);
|
||||||
|
const [isTransfersModalOpen, setIsTransfersModalOpen] = useState(false);
|
||||||
const [rowId, setRowId] = useState<number | null>(null);
|
const [rowId, setRowId] = useState<number | null>(null);
|
||||||
|
const [selectedStationId, setSelectedStationId] = useState<number | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
const [ids, setIds] = useState<number[]>([]);
|
const [ids, setIds] = useState<number[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
@@ -88,7 +97,7 @@ export const StationListPage = observer(() => {
|
|||||||
{
|
{
|
||||||
field: "actions",
|
field: "actions",
|
||||||
headerName: "Действия",
|
headerName: "Действия",
|
||||||
width: 140,
|
width: 200,
|
||||||
align: "center",
|
align: "center",
|
||||||
headerAlign: "center",
|
headerAlign: "center",
|
||||||
sortable: false,
|
sortable: false,
|
||||||
@@ -102,6 +111,15 @@ export const StationListPage = observer(() => {
|
|||||||
<button onClick={() => navigate(`/station/${params.row.id}`)}>
|
<button onClick={() => navigate(`/station/${params.row.id}`)}>
|
||||||
<Eye size={20} className="text-green-500" />
|
<Eye size={20} className="text-green-500" />
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedStationId(params.row.id);
|
||||||
|
setIsTransfersModalOpen(true);
|
||||||
|
}}
|
||||||
|
title="Редактировать пересадки"
|
||||||
|
>
|
||||||
|
<Route size={20} className="text-purple-500" />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsDeleteModalOpen(true);
|
setIsDeleteModalOpen(true);
|
||||||
@@ -205,6 +223,15 @@ export const StationListPage = observer(() => {
|
|||||||
setIsBulkDeleteModalOpen(false);
|
setIsBulkDeleteModalOpen(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<EditStationTransfersModal
|
||||||
|
open={isTransfersModalOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsTransfersModalOpen(false);
|
||||||
|
setSelectedStationId(null);
|
||||||
|
}}
|
||||||
|
stationId={selectedStationId}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { makeAutoObservable, runInAction } from "mobx";
|
import { makeAutoObservable, runInAction } from "mobx";
|
||||||
import { authInstance } from "@shared";
|
import { authInstance, languageInstance, languageStore } from "@shared";
|
||||||
|
|
||||||
export type Route = {
|
export type Route = {
|
||||||
route_name: string;
|
route_name: string;
|
||||||
@@ -89,11 +89,43 @@ class RouteStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
saveRouteStations = async (routeId: number, stationId: number) => {
|
saveRouteStations = async (routeId: number, stationId: number) => {
|
||||||
await authInstance.patch(`/route/${routeId}/station`, {
|
const { language } = languageStore;
|
||||||
...this.routeStations[routeId]?.find(
|
|
||||||
(station) => station.id === stationId
|
// Получаем актуальные данные станции с сервера
|
||||||
),
|
const stationResponse = await languageInstance(language).get(
|
||||||
|
`/station/${stationId}`
|
||||||
|
);
|
||||||
|
const fullStationData = stationResponse.data;
|
||||||
|
|
||||||
|
// Получаем отредактированные данные из локального кеша
|
||||||
|
const editedStationData = this.routeStations[routeId]?.find(
|
||||||
|
(station) => station.id === stationId
|
||||||
|
);
|
||||||
|
|
||||||
|
// Формируем данные для отправки: все поля станции + отредактированные offset
|
||||||
|
const dataToSend: any = {
|
||||||
station_id: stationId,
|
station_id: stationId,
|
||||||
|
offset_x: editedStationData?.offset_x ?? fullStationData.offset_x ?? 0,
|
||||||
|
offset_y: editedStationData?.offset_y ?? fullStationData.offset_y ?? 0,
|
||||||
|
align: editedStationData?.align ?? fullStationData.align ?? 0,
|
||||||
|
transfers: fullStationData.transfers || {},
|
||||||
|
};
|
||||||
|
|
||||||
|
await authInstance.patch(`/route/${routeId}/station`, dataToSend);
|
||||||
|
|
||||||
|
// Обновляем локальный кеш после успешного сохранения
|
||||||
|
runInAction(() => {
|
||||||
|
if (this.routeStations[routeId]) {
|
||||||
|
this.routeStations[routeId] = this.routeStations[routeId].map(
|
||||||
|
(station) =>
|
||||||
|
station.id === stationId
|
||||||
|
? {
|
||||||
|
...station,
|
||||||
|
...dataToSend,
|
||||||
|
}
|
||||||
|
: station
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { authInstance, languageInstance, languageStore } from "@shared";
|
import { authInstance, languageInstance, languageStore } from "@shared";
|
||||||
import { makeAutoObservable, runInAction } from "mobx";
|
import { makeAutoObservable, runInAction } from "mobx";
|
||||||
|
import { routeStore } from "../RouteStore";
|
||||||
|
|
||||||
type Language = "ru" | "en" | "zh";
|
type Language = "ru" | "en" | "zh";
|
||||||
|
|
||||||
@@ -546,6 +547,99 @@ class StationsStore {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updateStationTransfers = async (
|
||||||
|
id: number,
|
||||||
|
transfers: {
|
||||||
|
bus: string;
|
||||||
|
metro_blue: string;
|
||||||
|
metro_green: string;
|
||||||
|
metro_orange: string;
|
||||||
|
metro_purple: string;
|
||||||
|
metro_red: string;
|
||||||
|
train: string;
|
||||||
|
tram: string;
|
||||||
|
trolleybus: string;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const { language } = languageStore;
|
||||||
|
|
||||||
|
// Получаем данные станции для текущего языка
|
||||||
|
const response = await languageInstance(language).get(`/station/${id}`);
|
||||||
|
const stationData = response.data as Station;
|
||||||
|
|
||||||
|
if (!stationData) {
|
||||||
|
throw new Error("Station not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Формируем commonDataPayload как в editStation, с обновленными transfers
|
||||||
|
const commonDataPayload = {
|
||||||
|
city_id: stationData.city_id,
|
||||||
|
direction: stationData.direction,
|
||||||
|
latitude: stationData.latitude,
|
||||||
|
longitude: stationData.longitude,
|
||||||
|
offset_x: stationData.offset_x,
|
||||||
|
offset_y: stationData.offset_y,
|
||||||
|
transfers: transfers,
|
||||||
|
city: stationData.city || "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Отправляем один PATCH запрос, так как пересадки общие для всех языков
|
||||||
|
const patchResponse = await languageInstance(language).patch(
|
||||||
|
`/station/${id}`,
|
||||||
|
{
|
||||||
|
name: stationData.name || "",
|
||||||
|
system_name: stationData.system_name || "",
|
||||||
|
description: stationData.description || "",
|
||||||
|
address: stationData.address || "",
|
||||||
|
...commonDataPayload,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Обновляем данные для всех языков в локальном состоянии
|
||||||
|
runInAction(() => {
|
||||||
|
const updatedTransfers = patchResponse.data.transfers;
|
||||||
|
|
||||||
|
for (const lang of ["ru", "en", "zh"] as const) {
|
||||||
|
if (this.stationPreview[id]) {
|
||||||
|
this.stationPreview[id][lang] = {
|
||||||
|
...this.stationPreview[id][lang],
|
||||||
|
data: {
|
||||||
|
...this.stationPreview[id][lang].data,
|
||||||
|
transfers: updatedTransfers,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (this.stationLists[lang].data) {
|
||||||
|
this.stationLists[lang].data = this.stationLists[lang].data.map(
|
||||||
|
(station: Station) =>
|
||||||
|
station.id === id
|
||||||
|
? {
|
||||||
|
...station,
|
||||||
|
transfers: updatedTransfers,
|
||||||
|
}
|
||||||
|
: station
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем пересадки в RouteStore.routeStations для всех маршрутов
|
||||||
|
if (routeStore?.routeStations) {
|
||||||
|
for (const routeId in routeStore.routeStations) {
|
||||||
|
routeStore.routeStations[routeId] = routeStore.routeStations[
|
||||||
|
routeId
|
||||||
|
].map((station: any) =>
|
||||||
|
station.id === id
|
||||||
|
? {
|
||||||
|
...station,
|
||||||
|
transfers: updatedTransfers,
|
||||||
|
}
|
||||||
|
: station
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stationsStore = new StationsStore();
|
export const stationsStore = new StationsStore();
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ export function MediaViewer({
|
|||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
alt={media?.filename}
|
alt={media?.filename}
|
||||||
style={{
|
style={{
|
||||||
|
width: "100%",
|
||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -20,18 +20,6 @@ interface EditStationModalProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const transferFields = [
|
|
||||||
{ key: "bus", label: "Автобус" },
|
|
||||||
{ key: "metro_blue", label: "Метро (синяя)" },
|
|
||||||
{ key: "metro_green", label: "Метро (зеленая)" },
|
|
||||||
{ key: "metro_orange", label: "Метро (оранжевая)" },
|
|
||||||
{ key: "metro_purple", label: "Метро (фиолетовая)" },
|
|
||||||
{ key: "metro_red", label: "Метро (красная)" },
|
|
||||||
{ key: "train", label: "Электричка" },
|
|
||||||
{ key: "tram", label: "Трамвай" },
|
|
||||||
{ key: "trolleybus", label: "Троллейбус" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const EditStationModal = observer(
|
export const EditStationModal = observer(
|
||||||
({ open, onClose }: EditStationModalProps) => {
|
({ open, onClose }: EditStationModalProps) => {
|
||||||
const { id: routeId } = useParams<{ id: string }>();
|
const { id: routeId } = useParams<{ id: string }>();
|
||||||
@@ -95,37 +83,6 @@ export const EditStationModal = observer(
|
|||||||
defaultValue={station?.offset_y}
|
defaultValue={station?.offset_y}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ mt: 4 }}>
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
Пересадки
|
|
||||||
</Typography>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "repeat(3, 1fr)",
|
|
||||||
gap: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{transferFields.map(({ key, label }) => (
|
|
||||||
<TextField
|
|
||||||
key={key}
|
|
||||||
label={label}
|
|
||||||
name={key}
|
|
||||||
fullWidth
|
|
||||||
defaultValue={station?.transfers?.[key]}
|
|
||||||
onChange={(e) => {
|
|
||||||
setRouteStations(Number(routeId), selectedStationId, {
|
|
||||||
...station,
|
|
||||||
transfers: {
|
|
||||||
...station?.transfers,
|
|
||||||
[key]: e.target.value,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions sx={{ p: 3, justifyContent: "flex-end" }}>
|
<DialogActions sx={{ p: 3, justifyContent: "flex-end" }}>
|
||||||
<Button onClick={handleSave} variant="contained" color="primary">
|
<Button onClick={handleSave} variant="contained" color="primary">
|
||||||
|
|||||||
168
src/widgets/modals/EditStationTransfersModal/index.tsx
Normal file
168
src/widgets/modals/EditStationTransfersModal/index.tsx
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
|
IconButton,
|
||||||
|
Box,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { stationsStore, languageStore } from "@shared";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { ArrowLeft } from "lucide-react";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
interface EditStationTransfersModalProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
stationId: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transferFields = [
|
||||||
|
{ key: "bus", label: "Автобус" },
|
||||||
|
{ key: "metro_blue", label: "Метро (синяя)" },
|
||||||
|
{ key: "metro_green", label: "Метро (зеленая)" },
|
||||||
|
{ key: "metro_orange", label: "Метро (оранжевая)" },
|
||||||
|
{ key: "metro_purple", label: "Метро (фиолетовая)" },
|
||||||
|
{ key: "metro_red", label: "Метро (красная)" },
|
||||||
|
{ key: "train", label: "Электричка" },
|
||||||
|
{ key: "tram", label: "Трамвай" },
|
||||||
|
{ key: "trolleybus", label: "Троллейбус" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const EditStationTransfersModal = observer(
|
||||||
|
({ open, onClose, stationId }: EditStationTransfersModalProps) => {
|
||||||
|
const { stationLists, updateStationTransfers } = stationsStore;
|
||||||
|
const { language } = languageStore;
|
||||||
|
const [transfers, setTransfers] = useState<{
|
||||||
|
bus: string;
|
||||||
|
metro_blue: string;
|
||||||
|
metro_green: string;
|
||||||
|
metro_orange: string;
|
||||||
|
metro_purple: string;
|
||||||
|
metro_red: string;
|
||||||
|
train: string;
|
||||||
|
tram: string;
|
||||||
|
trolleybus: string;
|
||||||
|
}>({
|
||||||
|
bus: "",
|
||||||
|
metro_blue: "",
|
||||||
|
metro_green: "",
|
||||||
|
metro_orange: "",
|
||||||
|
metro_purple: "",
|
||||||
|
metro_red: "",
|
||||||
|
train: "",
|
||||||
|
tram: "",
|
||||||
|
trolleybus: "",
|
||||||
|
});
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && stationId) {
|
||||||
|
let station = stationLists[language].data.find(
|
||||||
|
(s: any) => s.id === stationId
|
||||||
|
);
|
||||||
|
if (!station?.transfers) {
|
||||||
|
for (const lang of ["ru", "en", "zh"] as const) {
|
||||||
|
const foundStation = stationLists[lang].data.find(
|
||||||
|
(s: any) => s.id === stationId
|
||||||
|
);
|
||||||
|
if (foundStation?.transfers) {
|
||||||
|
station = foundStation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (station?.transfers) {
|
||||||
|
setTransfers(station.transfers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [open, stationId, stationLists]);
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
if (!stationId) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
await updateStationTransfers(stationId, transfers);
|
||||||
|
toast.success("Пересадки успешно обновлены");
|
||||||
|
const { getStationList } = stationsStore;
|
||||||
|
await getStationList();
|
||||||
|
onClose();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating transfers:", error);
|
||||||
|
toast.error("Ошибка при обновлении пересадок");
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTransferChange = (key: string, value: string) => {
|
||||||
|
setTransfers((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[key]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
||||||
|
<DialogTitle>
|
||||||
|
<Box display="flex" alignItems="center" gap={2}>
|
||||||
|
<IconButton onClick={onClose}>
|
||||||
|
<ArrowLeft />
|
||||||
|
</IconButton>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
Станции / Редактировать пересадки
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h6">Редактирование пересадок</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Box sx={{ mt: 2 }}>
|
||||||
|
<Typography variant="h6" gutterBottom>
|
||||||
|
Пересадки
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(3, 1fr)",
|
||||||
|
gap: 2,
|
||||||
|
mt: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{transferFields.map(({ key, label }) => (
|
||||||
|
<TextField
|
||||||
|
key={key}
|
||||||
|
label={label}
|
||||||
|
name={key}
|
||||||
|
fullWidth
|
||||||
|
value={transfers[key as keyof typeof transfers] || ""}
|
||||||
|
onChange={(e) => handleTransferChange(key, e.target.value)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={{ p: 3, justifyContent: "flex-end" }}>
|
||||||
|
<Button onClick={onClose} variant="outlined">
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleSave}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading ? "Сохранение..." : "Сохранить"}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./SelectArticleDialog";
|
export * from "./SelectArticleDialog";
|
||||||
export * from "./EditStationModal";
|
export * from "./EditStationModal";
|
||||||
|
export * from "./EditStationTransfersModal";
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user