Compare commits
2 Commits
4b02c6e9d3
...
fbf8232ce3
| Author | SHA1 | Date | |
|---|---|---|---|
| fbf8232ce3 | |||
| 8d1de769c5 |
6
.env
6
.env
@@ -1,6 +1,6 @@
|
|||||||
# # VITE_API_URL='https://wn.st.unprism.ru'
|
# VITE_API_URL='https://wn.st.unprism.ru'
|
||||||
# # VITE_REACT_APP ='https://wn.st.unprism.ru/'
|
# VITE_REACT_APP ='https://wn.st.unprism.ru/'
|
||||||
# # VITE_KRBL_MEDIA='https://wn.st.unprism.ru/media/'
|
# VITE_KRBL_MEDIA='https://wn.st.unprism.ru/media/'
|
||||||
# VITE_NEED_AUTH='true'
|
# VITE_NEED_AUTH='true'
|
||||||
VITE_API_URL='https://wn.krbl.ru'
|
VITE_API_URL='https://wn.krbl.ru'
|
||||||
VITE_REACT_APP ='https://wn.krbl.ru/'
|
VITE_REACT_APP ='https://wn.krbl.ru/'
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export const LinkedItems = <
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="subtitle1" fontWeight="bold">
|
<Typography variant="subtitle1" fontWeight="bold">
|
||||||
Привязанные станции
|
Привязанные остановки
|
||||||
</Typography>
|
</Typography>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
|
|
||||||
@@ -499,7 +499,7 @@ const LinkedItemsContentsInner = <
|
|||||||
|
|
||||||
{linkedItems.length === 0 && !isLoading && (
|
{linkedItems.length === 0 && !isLoading && (
|
||||||
<Typography color="textSecondary" textAlign="center" py={2}>
|
<Typography color="textSecondary" textAlign="center" py={2}>
|
||||||
Станции не найдены
|
Остановки не найдены
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function Widgets() {
|
|||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
>
|
>
|
||||||
<Typography variant="h6" sx={{ color: "#fff" }}>
|
<Typography variant="h6" sx={{ color: "#fff" }}>
|
||||||
Станция
|
Остановка
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { toast } from "react-toastify";
|
|||||||
import { runInAction } from "mobx";
|
import { runInAction } from "mobx";
|
||||||
|
|
||||||
export const SnapshotCreatePage = observer(() => {
|
export const SnapshotCreatePage = observer(() => {
|
||||||
const { createSnapshot, getSnapshotStatus, snapshotStatus } = snapshotStore;
|
const { createSnapshot, getSnapshotStatus, getStorageInfo, snapshotStatus } = snapshotStore;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -59,6 +59,7 @@ export const SnapshotCreatePage = observer(() => {
|
|||||||
snapshotStore.snapshotStatus = null;
|
snapshotStore.snapshotStatus = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await getStorageInfo();
|
||||||
navigate(-1);
|
navigate(-1);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -5,13 +5,35 @@ import { useEffect, useState, useMemo } from "react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { DatabaseBackup, Trash2 } from "lucide-react";
|
import { DatabaseBackup, Trash2 } from "lucide-react";
|
||||||
import { CreateButton, DeleteModal, SnapshotRestore } from "@widgets";
|
import { CreateButton, DeleteModal, SnapshotRestore } from "@widgets";
|
||||||
import { Box, CircularProgress } from "@mui/material";
|
import { Alert, Box, CircularProgress } from "@mui/material";
|
||||||
|
|
||||||
|
const LOW_STORAGE_THRESHOLD_GB = 10;
|
||||||
|
|
||||||
|
const SEGMENT_COLORS = [
|
||||||
|
"#FF3B30",
|
||||||
|
"#FF9500",
|
||||||
|
"#FFCC00",
|
||||||
|
"#8E8E93",
|
||||||
|
"#AEAEB2",
|
||||||
|
"#34C759",
|
||||||
|
"#007AFF",
|
||||||
|
"#5856D6",
|
||||||
|
"#AF52DE",
|
||||||
|
"#FF2D55",
|
||||||
|
];
|
||||||
|
|
||||||
export const SnapshotListPage = observer(() => {
|
export const SnapshotListPage = observer(() => {
|
||||||
const { snapshots, getSnapshots, deleteSnapshot, restoreSnapshot } =
|
const {
|
||||||
snapshotStore;
|
snapshots,
|
||||||
|
getSnapshots,
|
||||||
|
deleteSnapshot,
|
||||||
|
restoreSnapshot,
|
||||||
|
storageInfo,
|
||||||
|
getStorageInfo,
|
||||||
|
} = snapshotStore;
|
||||||
const canWriteDevices = authStore.canWrite("devices");
|
const canWriteDevices = authStore.canWrite("devices");
|
||||||
const canCreateSnapshot = authStore.hasRole("snapshot_create") && canWriteDevices;
|
const canCreateSnapshot =
|
||||||
|
authStore.hasRole("snapshot_create") && canWriteDevices;
|
||||||
const canManageSnapshots = authStore.canWrite("snapshot") && canWriteDevices;
|
const canManageSnapshots = authStore.canWrite("snapshot") && canWriteDevices;
|
||||||
|
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
@@ -25,10 +47,17 @@ export const SnapshotListPage = observer(() => {
|
|||||||
pageSize: 50,
|
pageSize: 50,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const availableGB = storageInfo ? storageInfo.available_memory : null;
|
||||||
|
const totalGB = storageInfo ? storageInfo.all_memory : null;
|
||||||
|
const usedGB =
|
||||||
|
totalGB !== null && availableGB !== null ? totalGB - availableGB : null;
|
||||||
|
const isLowStorage =
|
||||||
|
availableGB !== null && availableGB < LOW_STORAGE_THRESHOLD_GB;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchSnapshots = async () => {
|
const fetchSnapshots = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await getSnapshots();
|
await Promise.all([getSnapshots(), getStorageInfo()]);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
fetchSnapshots();
|
fetchSnapshots();
|
||||||
@@ -61,33 +90,49 @@ export const SnapshotListPage = observer(() => {
|
|||||||
return <div>{params.value ? params.value : "-"}</div>;
|
return <div>{params.value ? params.value : "-"}</div>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...(canManageSnapshots ? [{
|
{
|
||||||
field: "actions",
|
field: "occupied_memory",
|
||||||
headerName: "Действия",
|
headerName: "Размер",
|
||||||
width: 300,
|
width: 120,
|
||||||
headerAlign: "center" as const,
|
renderCell: (params: GridRenderCellParams) => {
|
||||||
sortable: false,
|
return (
|
||||||
renderCell: (params: GridRenderCellParams) => (
|
<div>
|
||||||
<div className="flex h-full gap-7 justify-center items-center">
|
{params.value != null ? `${params.value.toFixed(1)} ГБ` : "-"}
|
||||||
<button
|
</div>
|
||||||
onClick={() => {
|
);
|
||||||
setIsRestoreModalOpen(true);
|
},
|
||||||
setRowId(params.row.id);
|
},
|
||||||
}}
|
...(canManageSnapshots
|
||||||
>
|
? [
|
||||||
<DatabaseBackup size={20} className="text-blue-500" />
|
{
|
||||||
</button>
|
field: "actions",
|
||||||
<button
|
headerName: "Действия",
|
||||||
onClick={() => {
|
width: 300,
|
||||||
setIsDeleteModalOpen(true);
|
headerAlign: "center" as const,
|
||||||
setRowId(params.row.id);
|
sortable: false,
|
||||||
}}
|
renderCell: (params: GridRenderCellParams) => (
|
||||||
>
|
<div className="flex h-full gap-7 justify-center items-center">
|
||||||
<Trash2 size={20} className="text-red-500" />
|
<button
|
||||||
</button>
|
onClick={() => {
|
||||||
</div>
|
setIsRestoreModalOpen(true);
|
||||||
),
|
setRowId(params.row.id);
|
||||||
}] : []),
|
}}
|
||||||
|
>
|
||||||
|
<DatabaseBackup size={20} className="text-blue-500" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsDeleteModalOpen(true);
|
||||||
|
setRowId(params.row.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 size={20} className="text-red-500" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
];
|
];
|
||||||
|
|
||||||
const rows = useMemo(() => {
|
const rows = useMemo(() => {
|
||||||
@@ -97,16 +142,25 @@ export const SnapshotListPage = observer(() => {
|
|||||||
(snapshot) =>
|
(snapshot) =>
|
||||||
!query ||
|
!query ||
|
||||||
(snapshot.Name ?? "").toLowerCase().includes(query) ||
|
(snapshot.Name ?? "").toLowerCase().includes(query) ||
|
||||||
(snapshots.find((s) => s.ID === snapshot.ParentID)?.Name ?? "").toLowerCase().includes(query)
|
(snapshots.find((s) => s.ID === snapshot.ParentID)?.Name ?? "")
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(query),
|
||||||
)
|
)
|
||||||
.map((snapshot) => ({
|
.map((snapshot) => ({
|
||||||
id: snapshot.ID,
|
id: snapshot.ID,
|
||||||
name: snapshot.Name,
|
name: snapshot.Name,
|
||||||
parent: snapshots.find((s) => s.ID === snapshot.ParentID)?.Name,
|
parent: snapshots.find((s) => s.ID === snapshot.ParentID)?.Name,
|
||||||
created_at: formatCreationTime(snapshot.CreationTime),
|
created_at: formatCreationTime(snapshot.CreationTime),
|
||||||
|
occupied_memory: snapshot.occupied_memory,
|
||||||
}));
|
}));
|
||||||
}, [snapshots, searchQuery]);
|
}, [snapshots, searchQuery]);
|
||||||
|
|
||||||
|
const snapshotsGB = rows.reduce(
|
||||||
|
(sum, row) => sum + (row.occupied_memory ?? 0),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
const systemGB = usedGB !== null ? Math.max(0, usedGB - snapshotsGB) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div style={{ width: "100%" }}>
|
<div style={{ width: "100%" }}>
|
||||||
@@ -114,9 +168,100 @@ export const SnapshotListPage = observer(() => {
|
|||||||
<h1 className="text-2xl ">Экспорт Медиа</h1>
|
<h1 className="text-2xl ">Экспорт Медиа</h1>
|
||||||
|
|
||||||
{canCreateSnapshot && (
|
{canCreateSnapshot && (
|
||||||
<CreateButton label="Создать экспорт медиа" path="/snapshot/create" />
|
<CreateButton
|
||||||
|
label="Создать экспорт медиа"
|
||||||
|
path="/snapshot/create"
|
||||||
|
disabled={isLowStorage}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{usedGB !== null && totalGB !== null && (
|
||||||
|
<div className="bg-white rounded-2xl p-5 mb-6 shadow-sm border border-gray-100">
|
||||||
|
<div className="flex items-baseline gap-3 mb-3">
|
||||||
|
<span className="text-lg font-semibold">Хранилище</span>
|
||||||
|
<span className="text-sm text-gray-500">
|
||||||
|
Используется: {usedGB.toFixed(2)} ГБ из {totalGB.toFixed(0)} ГБ
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex w-full h-3 rounded-lg overflow-hidden bg-gray-100">
|
||||||
|
{rows.map((row, i) => {
|
||||||
|
const pct =
|
||||||
|
row.occupied_memory != null && totalGB > 0
|
||||||
|
? (row.occupied_memory / totalGB) * 100
|
||||||
|
: 0;
|
||||||
|
if (pct <= 0) return null;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={row.id}
|
||||||
|
style={{
|
||||||
|
width: `${pct}%`,
|
||||||
|
backgroundColor:
|
||||||
|
SEGMENT_COLORS[i % SEGMENT_COLORS.length],
|
||||||
|
}}
|
||||||
|
title={`${row.name}: ${row.occupied_memory?.toFixed(1)} ГБ`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{systemGB !== null && systemGB > 0 && totalGB > 0 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: `${(systemGB / totalGB) * 100}%`,
|
||||||
|
backgroundColor: "#C7C7CC",
|
||||||
|
}}
|
||||||
|
title={`Системные данные: ${systemGB.toFixed(1)} ГБ`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-x-5 gap-y-1 mt-3">
|
||||||
|
{rows.map((row, i) => {
|
||||||
|
if (row.occupied_memory == null || row.occupied_memory <= 0)
|
||||||
|
return null;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={row.id}
|
||||||
|
className="flex items-center gap-1.5 text-xs text-gray-700"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="inline-block w-2.5 h-2.5 rounded-full"
|
||||||
|
style={{
|
||||||
|
backgroundColor:
|
||||||
|
SEGMENT_COLORS[i % SEGMENT_COLORS.length],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{row.name}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{systemGB !== null && systemGB > 0 && (
|
||||||
|
<div className="flex items-center gap-1.5 text-xs text-gray-700">
|
||||||
|
<span
|
||||||
|
className="inline-block w-2.5 h-2.5 rounded-full"
|
||||||
|
style={{ backgroundColor: "#C7C7CC" }}
|
||||||
|
/>
|
||||||
|
Системные данные
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{availableGB !== null && availableGB > 0 && (
|
||||||
|
<div className="flex items-center gap-1.5 text-xs text-gray-700">
|
||||||
|
<span className="inline-block w-2.5 h-2.5 rounded-full bg-gray-100" />
|
||||||
|
Свободно
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isLowStorage && (
|
||||||
|
<Alert severity="warning" className="mb-4">
|
||||||
|
Недостаточно места на диске! Осталось {availableGB?.toFixed(1)} ГБ
|
||||||
|
из {totalGB?.toFixed(0)} ГБ. Создание новых экспортов заблокировано.
|
||||||
|
Удалите ненужные экспорты для освобождения места или обратитесь к
|
||||||
|
администратору сервера.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
<SearchInput value={searchQuery} onChange={setSearchQuery} />
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const StationCreatePage = observer(() => {
|
|||||||
navigate("/station");
|
navigate("/station");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating station:", error);
|
console.error("Error creating station:", error);
|
||||||
toast.error("Ошибка при создании станции");
|
toast.error("Ошибка при создании остановки");
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export const StationEditPage = observer(() => {
|
|||||||
toast.success("Остановка успешно обновлена");
|
toast.success("Остановка успешно обновлена");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating station:", error);
|
console.error("Error updating station:", error);
|
||||||
toast.error("Ошибка при обновлении станции");
|
toast.error("Ошибка при обновлении остановки");
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ export const StationEditPage = observer(() => {
|
|||||||
minHeight: "60vh",
|
minHeight: "60vh",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LoadingSpinner message="Загрузка данных станции..." />
|
<LoadingSpinner message="Загрузка данных остановки..." />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export const StationListPage = observer(() => {
|
|||||||
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex justify-between items-center mb-10">
|
<div className="flex justify-between items-center mb-10">
|
||||||
<h1 className="text-2xl">Станции</h1>
|
<h1 className="text-2xl">Остановки</h1>
|
||||||
{canWriteStations && (
|
{canWriteStations && (
|
||||||
<CreateButton label="Создать остановки" path="/station/create" />
|
<CreateButton label="Создать остановки" path="/station/create" />
|
||||||
)}
|
)}
|
||||||
@@ -222,7 +222,7 @@ export const StationListPage = observer(() => {
|
|||||||
slots={{
|
slots={{
|
||||||
noRowsOverlay: () => (
|
noRowsOverlay: () => (
|
||||||
<Box sx={{ mt: 5, textAlign: "center", color: "text.secondary" }}>
|
<Box sx={{ mt: 5, textAlign: "center", color: "text.secondary" }}>
|
||||||
{isLoading ? <CircularProgress size={20} /> : "Нет станций"}
|
{isLoading ? <CircularProgress size={20} /> : "Нет остановок"}
|
||||||
</Box>
|
</Box>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const StationPreviewPage = observer(() => {
|
|||||||
minHeight: "60vh",
|
minHeight: "60vh",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LoadingSpinner message="Загрузка данных станции..." />
|
<LoadingSpinner message="Загрузка данных остановки..." />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ class RouteStore {
|
|||||||
saveRouteStations = async (routeId: number, stationId: number) => {
|
saveRouteStations = async (routeId: number, stationId: number) => {
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
|
|
||||||
// Получаем актуальные данные станции с сервера
|
|
||||||
const stationResponse = await languageInstance(language).get(
|
const stationResponse = await languageInstance(language).get(
|
||||||
`/station/${stationId}`
|
`/station/${stationId}`
|
||||||
);
|
);
|
||||||
@@ -108,7 +107,6 @@ class RouteStore {
|
|||||||
(station) => station.id === stationId
|
(station) => station.id === stationId
|
||||||
);
|
);
|
||||||
|
|
||||||
// Формируем данные для отправки: все поля станции + отредактированные offset
|
|
||||||
const dataToSend: any = {
|
const dataToSend: any = {
|
||||||
station_id: stationId,
|
station_id: stationId,
|
||||||
offset_x: editedStationData?.offset_x ?? fullStationData.offset_x ?? 0,
|
offset_x: editedStationData?.offset_x ?? fullStationData.offset_x ?? 0,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Snapshot = {
|
|||||||
Name: string;
|
Name: string;
|
||||||
ParentID: string;
|
ParentID: string;
|
||||||
CreationTime: string;
|
CreationTime: string;
|
||||||
|
occupied_memory: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SnapshotStatus = {
|
type SnapshotStatus = {
|
||||||
@@ -32,11 +33,17 @@ type SnapshotStatus = {
|
|||||||
Error: string;
|
Error: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type StorageInfo = {
|
||||||
|
available_memory: number;
|
||||||
|
all_memory: number;
|
||||||
|
};
|
||||||
|
|
||||||
class SnapshotStore {
|
class SnapshotStore {
|
||||||
snapshots: Snapshot[] = [];
|
snapshots: Snapshot[] = [];
|
||||||
snapshot: Snapshot | null = null;
|
snapshot: Snapshot | null = null;
|
||||||
lastRequestId: string | null = null;
|
lastRequestId: string | null = null;
|
||||||
snapshotStatus: SnapshotStatus | null = null;
|
snapshotStatus: SnapshotStatus | null = null;
|
||||||
|
storageInfo: StorageInfo | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
@@ -259,10 +266,17 @@ class SnapshotStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
deleteSnapshot = async (id: string) => {
|
deleteSnapshot = async (id: string) => {
|
||||||
|
const snapshot = this.snapshots.find((s) => s.ID === id);
|
||||||
await authInstance.delete(`/snapshots/${id}`);
|
await authInstance.delete(`/snapshots/${id}`);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.snapshots = this.snapshots.filter((snapshot) => snapshot.ID !== id);
|
this.snapshots = this.snapshots.filter((s) => s.ID !== id);
|
||||||
|
if (this.storageInfo && snapshot?.occupied_memory) {
|
||||||
|
this.storageInfo = {
|
||||||
|
...this.storageInfo,
|
||||||
|
available_memory: this.storageInfo.available_memory + snapshot.occupied_memory,
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -299,6 +313,14 @@ class SnapshotStore {
|
|||||||
this.snapshotStatus = response.data;
|
this.snapshotStatus = response.data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getStorageInfo = async () => {
|
||||||
|
const response = await authInstance.get(`/snapshots/storage`);
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this.storageInfo = response.data;
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const snapshotStore = new SnapshotStore();
|
export const snapshotStore = new SnapshotStore();
|
||||||
|
|||||||
@@ -555,7 +555,6 @@ class StationsStore {
|
|||||||
) => {
|
) => {
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
|
|
||||||
// Получаем данные станции для текущего языка
|
|
||||||
const response = await languageInstance(language).get(`/station/${id}`);
|
const response = await languageInstance(language).get(`/station/${id}`);
|
||||||
const stationData = response.data as Station;
|
const stationData = response.data as Station;
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import { useNavigate } from "react-router-dom";
|
|||||||
interface CreateButtonProps {
|
interface CreateButtonProps {
|
||||||
label: string;
|
label: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreateButton = ({ label, path }: CreateButtonProps) => {
|
export const CreateButton = ({ label, path, disabled }: CreateButtonProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -18,6 +19,7 @@ export const CreateButton = ({ label, path }: CreateButtonProps) => {
|
|||||||
navigate(path);
|
navigate(path);
|
||||||
}}
|
}}
|
||||||
startIcon={<Plus size={20} />}
|
startIcon={<Plus size={20} />}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export const EditStationTransfersModal = observer(
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" color="text.secondary">
|
||||||
Станции / Редактировать пересадки
|
Остановки / Редактировать пересадки
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="h6">Редактирование пересадок</Typography>
|
<Typography variant="h6">Редактирование пересадок</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user