feat: update map, admin to and cache
This commit is contained in:
@@ -31,7 +31,7 @@ export const ArticleListPage = observer(() => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
fetchArticles();
|
||||
}, [language]);
|
||||
}, [language, selectedCityStore.cityVersion]);
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
|
||||
import { ruRU } from "@mui/x-data-grid/locales";
|
||||
import { authStore, carrierStore, cityStore, languageStore, SearchInput } from "@shared";
|
||||
import { authStore, carrierStore, cityStore, languageStore, selectedCityStore, SearchInput } from "@shared";
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Pencil, Trash2, Minus } from "lucide-react";
|
||||
@@ -39,7 +39,7 @@ export const CarrierListPage = observer(() => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
fetchData();
|
||||
}, [language]);
|
||||
}, [language, selectedCityStore.cityVersion]);
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
@@ -169,6 +169,11 @@ export const CarrierListPage = observer(() => {
|
||||
checkboxSelection={canWriteCarriers}
|
||||
disableRowSelectionExcludeModel
|
||||
disableRowSelectionOnClick
|
||||
onRowDoubleClick={(params) => {
|
||||
if (canWriteCarriers) {
|
||||
navigate(`/carrier/${params.id}/edit`);
|
||||
}
|
||||
}}
|
||||
loading={isLoading}
|
||||
paginationModel={paginationModel}
|
||||
onPaginationModelChange={setPaginationModel}
|
||||
|
||||
@@ -172,6 +172,11 @@ export const CityListPage = observer(() => {
|
||||
checkboxSelection={canWriteCities}
|
||||
disableRowSelectionExcludeModel
|
||||
disableRowSelectionOnClick
|
||||
onRowDoubleClick={(params) => {
|
||||
if (canWriteCities) {
|
||||
navigate(`/city/${params.id}/edit`);
|
||||
}
|
||||
}}
|
||||
loading={isLoading}
|
||||
paginationModel={paginationModel}
|
||||
onPaginationModelChange={setPaginationModel}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "@widgets";
|
||||
import { useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { runInAction } from "mobx";
|
||||
|
||||
function a11yProps(index: number) {
|
||||
return {
|
||||
@@ -36,6 +37,16 @@ export const CreateSightPage = observer(() => {
|
||||
return () => selectedCityStore.setIsLocked(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const { selectedCityId, selectedCity } = selectedCityStore;
|
||||
if (selectedCityId && selectedCity && !createSightStore.sight.city_id) {
|
||||
runInAction(() => {
|
||||
createSightStore.sight.city_id = selectedCityId;
|
||||
createSightStore.sight.city = selectedCity.name;
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleChange = (_: React.SyntheticEvent, newValue: number) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ export const RouteListPage = observer(() => {
|
||||
loadCounts(routeIds);
|
||||
};
|
||||
fetchData();
|
||||
}, [language]);
|
||||
}, [language, selectedCityStore.cityVersion]);
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
|
||||
@@ -448,6 +448,22 @@ const StationLabel = observer(
|
||||
anchor={dynamicAnchor}
|
||||
zIndex={isHovered || isControlHovered ? 1000 : 0}
|
||||
>
|
||||
{ruLabelWidth > 0 && (
|
||||
<pixiGraphics
|
||||
draw={(g: Graphics) => {
|
||||
g.clear();
|
||||
const hasSecondLabel = !!(station.name && language !== "ru" && ruLabel);
|
||||
const pad = 10 / scale;
|
||||
const w = ruLabelWidth + pad * 2;
|
||||
const top = -compensatedRuFontSize / 2 - pad;
|
||||
const bottom = hasSecondLabel
|
||||
? compensatedRuFontSize * 1.1 + compensatedNameFontSize / 2 + pad
|
||||
: compensatedRuFontSize / 2 + pad;
|
||||
g.rect(-w / 2, top, w, bottom - top);
|
||||
g.fill({ color: 0x000000, alpha: 0.001 });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{ruLabel && (
|
||||
<pixiText
|
||||
ref={ruLabelRef}
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
height: 96px;
|
||||
background-color: #fcd500;
|
||||
color: black;
|
||||
border-radius: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
border-bottom-left-radius: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@@ -2405,12 +2405,6 @@ export const WebGLRouteMapPrototype = observer(() => {
|
||||
resizingStationIconId === station.id;
|
||||
|
||||
const secondaryLineHeight = 1.2;
|
||||
const secondaryHeight = showSecondary
|
||||
? secondaryFontSize * secondaryLineHeight
|
||||
: 0;
|
||||
const menuPaddingTop = showSecondary
|
||||
? Math.max(0, secondaryHeight - secondaryMarginTop) + 3
|
||||
: 3;
|
||||
|
||||
return (
|
||||
<div key={station.id}>
|
||||
@@ -2440,22 +2434,25 @@ export const WebGLRouteMapPrototype = observer(() => {
|
||||
color: "#fff",
|
||||
fontFamily: "Roboto, sans-serif",
|
||||
textAlign: "left",
|
||||
pointerEvents: "auto",
|
||||
pointerEvents: "none",
|
||||
cursor: "grab",
|
||||
userSelect: "none",
|
||||
touchAction: "none",
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: "auto",
|
||||
display: "inline-block",
|
||||
pointerEvents: "none",
|
||||
transformOrigin: "left center",
|
||||
transform: `rotate(${rotationCss})`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: "auto",
|
||||
display: "inline-block",
|
||||
pointerEvents: "none",
|
||||
transformOrigin: "left center",
|
||||
transform: `rotate(${counterRotationCss})`,
|
||||
}}
|
||||
@@ -2549,34 +2546,36 @@ export const WebGLRouteMapPrototype = observer(() => {
|
||||
) : null}
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
fontWeight: 700,
|
||||
fontSize: primaryFontSize,
|
||||
lineHeight: 1,
|
||||
textShadow: "0 0 4px rgba(0,0,0,0.6)",
|
||||
pointerEvents: "none",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{station.name}
|
||||
{showSecondary ? (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "100%",
|
||||
marginTop: -1 * secondaryMarginTop,
|
||||
fontWeight: 400,
|
||||
fontSize: secondaryFontSize,
|
||||
lineHeight: secondaryLineHeight,
|
||||
color: "#CBCBCB",
|
||||
textShadow: "0 0 3px rgba(0,0,0,0.4)",
|
||||
whiteSpace: "nowrap",
|
||||
...secondaryPositionStyle,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
{secondaryStation?.name}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{showSecondary ? (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "100%",
|
||||
marginTop: -1 * secondaryMarginTop,
|
||||
fontWeight: 400,
|
||||
fontSize: secondaryFontSize,
|
||||
lineHeight: secondaryLineHeight,
|
||||
color: "#CBCBCB",
|
||||
textShadow: "0 0 3px rgba(0,0,0,0.4)",
|
||||
whiteSpace: "nowrap",
|
||||
...secondaryPositionStyle,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
{secondaryStation?.name}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2587,9 +2586,9 @@ export const WebGLRouteMapPrototype = observer(() => {
|
||||
top: "100%",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
paddingTop: menuPaddingTop,
|
||||
paddingTop: "8px",
|
||||
pointerEvents: "auto",
|
||||
zIndex: 10,
|
||||
zIndex: 1000000,
|
||||
cursor: "default",
|
||||
}}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
|
||||
@@ -46,7 +46,7 @@ export const SightListPage = observer(() => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
fetchSights();
|
||||
}, [language]);
|
||||
}, [language, selectedCityStore.cityVersion]);
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ export const StationListPage = observer(() => {
|
||||
loadSightCounts(stationIds);
|
||||
};
|
||||
fetchStations();
|
||||
}, [language]);
|
||||
}, [language, selectedCityStore.cityVersion]);
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
VEHICLE_TYPES,
|
||||
carrierStore,
|
||||
languageStore,
|
||||
cityStore,
|
||||
selectedCityStore,
|
||||
} from "@shared";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ArrowLeft, Save } from "lucide-react";
|
||||
@@ -26,11 +28,13 @@ export const VehicleCreatePage = observer(() => {
|
||||
const [type, setType] = useState("");
|
||||
const [carrierId, setCarrierId] = useState<number | null>(null);
|
||||
const [model, setModel] = useState("");
|
||||
const [cityId, setCityId] = useState<number | null>(selectedCityStore.selectedCityId);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { language } = languageStore;
|
||||
|
||||
useEffect(() => {
|
||||
carrierStore.getCarriers(language);
|
||||
cityStore.getCities("ru");
|
||||
}, [language]);
|
||||
|
||||
const handleCreate = async () => {
|
||||
@@ -43,6 +47,7 @@ export const VehicleCreatePage = observer(() => {
|
||||
?.full_name as string,
|
||||
carrierId!,
|
||||
model || undefined,
|
||||
cityId ?? undefined,
|
||||
);
|
||||
toast.success("Транспорт успешно создан");
|
||||
} catch (error) {
|
||||
@@ -73,12 +78,11 @@ export const VehicleCreatePage = observer(() => {
|
||||
onChange={(e) => setTailNumber(e.target.value)}
|
||||
/>
|
||||
|
||||
<FormControl fullWidth>
|
||||
<FormControl fullWidth required>
|
||||
<InputLabel>Тип</InputLabel>
|
||||
<Select
|
||||
value={type}
|
||||
label="Тип"
|
||||
required
|
||||
onChange={(e) => setType(e.target.value)}
|
||||
>
|
||||
{VEHICLE_TYPES.map((type) => (
|
||||
@@ -89,12 +93,11 @@ export const VehicleCreatePage = observer(() => {
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth>
|
||||
<FormControl fullWidth required>
|
||||
<InputLabel>Перевозчик</InputLabel>
|
||||
<Select
|
||||
value={carrierId || ""}
|
||||
label="Перевозчик"
|
||||
required
|
||||
onChange={(e) => setCarrierId(e.target.value as number)}
|
||||
>
|
||||
{carrierStore.carriers[language].data?.map((carrier) => (
|
||||
@@ -113,12 +116,27 @@ export const VehicleCreatePage = observer(() => {
|
||||
placeholder="Произвольное название модели"
|
||||
/>
|
||||
|
||||
<FormControl fullWidth required>
|
||||
<InputLabel>Город</InputLabel>
|
||||
<Select
|
||||
value={cityId ?? ""}
|
||||
label="Город"
|
||||
onChange={(e) => setCityId(e.target.value ? Number(e.target.value) : null)}
|
||||
>
|
||||
{cityStore.cities.ru.data.map((city) => (
|
||||
<MenuItem key={city.id} value={city.id}>
|
||||
{city.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
className="w-min flex gap-2 items-center"
|
||||
startIcon={<Save size={20} />}
|
||||
onClick={handleCreate}
|
||||
disabled={isLoading || !tailNumber || !type || !carrierId}
|
||||
disabled={isLoading || !tailNumber || !type || !carrierId || !cityId}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2 size={20} className="animate-spin" />
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
VEHICLE_TYPES,
|
||||
vehicleStore,
|
||||
LoadingSpinner,
|
||||
cityStore,
|
||||
selectedCityStore,
|
||||
} from "@shared";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
@@ -54,6 +56,7 @@ export const VehicleEditPage = observer(() => {
|
||||
try {
|
||||
await getVehicle(Number(id));
|
||||
await getCarriers(language);
|
||||
await cityStore.getCities("ru");
|
||||
|
||||
setEditVehicleData({
|
||||
tail_number: vehicle[Number(id)]?.vehicle.tail_number ?? "",
|
||||
@@ -63,6 +66,7 @@ export const VehicleEditPage = observer(() => {
|
||||
model: vehicle[Number(id)]?.vehicle.model ?? "",
|
||||
snapshot_update_blocked:
|
||||
vehicle[Number(id)]?.vehicle.snapshot_update_blocked ?? false,
|
||||
city_id: vehicle[Number(id)]?.vehicle.city_id ?? selectedCityStore.selectedCityId ?? undefined,
|
||||
});
|
||||
} finally {
|
||||
setIsLoadingData(false);
|
||||
@@ -125,12 +129,11 @@ export const VehicleEditPage = observer(() => {
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControl fullWidth>
|
||||
<FormControl fullWidth required>
|
||||
<InputLabel>Тип</InputLabel>
|
||||
<Select
|
||||
value={editVehicleData.type}
|
||||
label="Тип"
|
||||
required
|
||||
onChange={(e) =>
|
||||
setEditVehicleData({ ...editVehicleData, type: e.target.value })
|
||||
}
|
||||
@@ -143,12 +146,11 @@ export const VehicleEditPage = observer(() => {
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl fullWidth>
|
||||
<FormControl fullWidth required>
|
||||
<InputLabel>Перевозчик</InputLabel>
|
||||
<Select
|
||||
value={editVehicleData.carrier_id}
|
||||
label="Перевозчик"
|
||||
required
|
||||
onChange={(e) =>
|
||||
setEditVehicleData({
|
||||
...editVehicleData,
|
||||
@@ -177,6 +179,26 @@ export const VehicleEditPage = observer(() => {
|
||||
placeholder="Произвольное название модели"
|
||||
/>
|
||||
|
||||
<FormControl fullWidth required>
|
||||
<InputLabel>Город</InputLabel>
|
||||
<Select
|
||||
value={editVehicleData.city_id ?? ""}
|
||||
label="Город"
|
||||
onChange={(e) =>
|
||||
setEditVehicleData({
|
||||
...editVehicleData,
|
||||
city_id: e.target.value ? Number(e.target.value) : undefined,
|
||||
})
|
||||
}
|
||||
>
|
||||
{cityStore.cities.ru.data.map((city) => (
|
||||
<MenuItem key={city.id} value={city.id}>
|
||||
{city.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
@@ -202,7 +224,8 @@ export const VehicleEditPage = observer(() => {
|
||||
isLoading ||
|
||||
!editVehicleData.tail_number ||
|
||||
!editVehicleData.type ||
|
||||
!editVehicleData.carrier_id
|
||||
!editVehicleData.carrier_id ||
|
||||
!editVehicleData.city_id
|
||||
}
|
||||
>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
|
||||
import { ruRU } from "@mui/x-data-grid/locales";
|
||||
import { authStore, carrierStore, languageStore, vehicleStore, SearchInput } from "@shared";
|
||||
import { authStore, carrierStore, languageStore, vehicleStore, SearchInput, selectedCityStore, cityStore } from "@shared";
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Eye, Pencil, Trash2, Minus } from "lucide-react";
|
||||
@@ -11,7 +11,7 @@ import { Box, CircularProgress } from "@mui/material";
|
||||
|
||||
export const VehicleListPage = observer(() => {
|
||||
const { vehicles, getVehicles, deleteVehicle } = vehicleStore;
|
||||
const { carriers, getCarriers } = carrierStore;
|
||||
const { getCarriers } = carrierStore;
|
||||
const navigate = useNavigate();
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [isBulkDeleteModalOpen, setIsBulkDeleteModalOpen] = useState(false);
|
||||
@@ -31,10 +31,11 @@ export const VehicleListPage = observer(() => {
|
||||
setIsLoading(true);
|
||||
await getVehicles();
|
||||
await getCarriers(language);
|
||||
await cityStore.getCities("ru");
|
||||
setIsLoading(false);
|
||||
};
|
||||
fetchData();
|
||||
}, [language]);
|
||||
}, [language, selectedCityStore.cityVersion]);
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{
|
||||
@@ -137,9 +138,13 @@ export const VehicleListPage = observer(() => {
|
||||
},
|
||||
];
|
||||
|
||||
const { selectedCityId } = selectedCityStore;
|
||||
|
||||
const rows = useMemo(() => {
|
||||
if (!selectedCityId) return [];
|
||||
const query = searchQuery.trim().toLowerCase();
|
||||
return (vehicles.data ?? [])
|
||||
.filter((vehicle) => vehicle.vehicle.city_id === selectedCityId)
|
||||
.filter(
|
||||
(vehicle) =>
|
||||
!query ||
|
||||
@@ -151,11 +156,9 @@ export const VehicleListPage = observer(() => {
|
||||
tail_number: vehicle.vehicle.tail_number,
|
||||
type: vehicle.vehicle.type,
|
||||
carrier: vehicle.vehicle.carrier,
|
||||
city: carriers[language].data?.find(
|
||||
(carrier) => carrier.id === vehicle.vehicle.carrier_id
|
||||
)?.city,
|
||||
city: cityStore.cities.ru.data.find((c) => c.id === vehicle.vehicle.city_id)?.name,
|
||||
}));
|
||||
}, [vehicles.data, carriers[language].data, searchQuery]);
|
||||
}, [vehicles.data, selectedCityId, searchQuery]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -166,6 +169,7 @@ export const VehicleListPage = observer(() => {
|
||||
<CreateButton
|
||||
label="Создать транспортное средство"
|
||||
path="/vehicle/create"
|
||||
disabled={!selectedCityId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -223,6 +227,8 @@ export const VehicleListPage = observer(() => {
|
||||
<Box sx={{ mt: 5, textAlign: "center", color: "text.secondary" }}>
|
||||
{isLoading ? (
|
||||
<CircularProgress size={20} />
|
||||
) : !selectedCityId ? (
|
||||
"Выберите город"
|
||||
) : (
|
||||
"Нет транспортных средств"
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user