fix: Fix for correct usage

This commit is contained in:
Илья Куприец 2025-05-28 11:08:28 +03:00
parent b837aae711
commit a6a5288262
14 changed files with 230 additions and 144 deletions

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" href="/favicon-ship.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
@ -19,9 +19,7 @@
name="twitter:image"
content="https://refine.dev/img/refine_social.png"
/>
<title>
Белые ночи
</title>
<title>Белые ночи</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

BIN
public/favicon-ship.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -70,6 +70,7 @@ type LinkedItemsProps<T> = {
disableCreation?: boolean;
updatedLinkedItems?: T[];
refresh?: number;
cityId?: number;
};
const reorder = (list: any[], startIndex: number, endIndex: number) => {
@ -131,6 +132,7 @@ export const LinkedItemsContents = <
disableCreation = false,
updatedLinkedItems,
refresh,
cityId,
}: LinkedItemsProps<T>) => {
const { language } = languageStore;
const { setArticleModalOpenAction, setArticleIdAction } = articleStore;
@ -216,7 +218,7 @@ export const LinkedItemsContents = <
useEffect(() => {
if (type === "edit") {
axiosInstance
.get(`${import.meta.env.VITE_KRBL_API}/${childResource}/`)
.get(`${import.meta.env.VITE_KRBL_API}/${childResource}/`, {})
.then((response) => {
setItems(response?.data || []);
setIsLoading(false);
@ -445,7 +447,7 @@ export const LinkedItemsContents = <
availableItems?.find((item) => item.id === selectedItemId) || null
}
onChange={(_, newValue) => setSelectedItemId(newValue?.id || null)}
options={availableItems}
options={availableItems.filter((item) => item.city_id == cityId)}
getOptionLabel={(item) => String(item[fields[0].data])}
renderInput={(params) => (
<TextField {...params} label={`Выберите ${title}`} fullWidth />
@ -456,6 +458,7 @@ export const LinkedItemsContents = <
.toLowerCase()
.split(" ")
.filter((word) => word.length > 0);
return options.filter((option) => {
const optionWords = String(option[fields[0].data])
.toLowerCase()

View File

@ -143,8 +143,17 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = observer(
justifyContent="flex-end"
alignItems="center"
spacing={2}
color="white"
sx={{
"& .MuiSelect-select": {
color: "white",
},
}}
>
<FormControl variant="standard" sx={{ width: "min-content" }}>
<FormControl
variant="standard"
sx={{ width: "min-content", color: "white" }}
>
{city_id && cities && (
<Select
defaultValue={city_id}

View File

@ -1,11 +1,16 @@
import {ProjectIcon} from './Icons'
import { Ship } from "lucide-react";
import { ProjectIcon } from "./Icons";
export default function SidebarTitle({collapsed}: {collapsed: boolean}) {
export default function SidebarTitle({ collapsed }: { collapsed: boolean }) {
return (
<div style={{display: 'flex', alignItems: 'center', whiteSpace: 'nowrap'}}>
<ProjectIcon style={{color: '#7f6b58'}} />
<div
style={{ display: "flex", alignItems: "center", whiteSpace: "nowrap" }}
>
<Ship size={40} style={{ color: "#7f6b58" }} />
{!collapsed && <span style={{marginLeft: 8, fontWeight: 'bold'}}>Белые ночи</span>}
{!collapsed && (
<span style={{ marginLeft: 8, fontWeight: "bold" }}>Белые ночи</span>
)}
</div>
)
);
}

View File

@ -4,7 +4,6 @@ import { useTransform } from "./TransformContext";
import { useMapData } from "./MapDataContext";
import { SCALE_FACTOR } from "./Constants";
import { useApplication } from "@pixi/react";
import { languageStore } from "@/store/LanguageStore";
class ErrorBoundary extends Component<
{ children: ReactNode },

View File

@ -25,7 +25,7 @@ const MapDataContext = createContext<{
originalStationData?: StationData[];
originalSightData?: SightData[];
routeData?: RouteData;
stationData?: StationData[];
stationData?: StationDataWithLanguage;
sightData?: SightData[];
isRouteLoading: boolean;
@ -60,6 +60,9 @@ const MapDataContext = createContext<{
saveChanges: () => {},
});
type StationDataWithLanguage = {
[key: string]: StationData[];
};
export const MapDataProvider = observer(
({ children }: Readonly<{ children: ReactNode }>) => {
const { id: routeId } = useParams<{ id: string }>();
@ -71,7 +74,11 @@ export const MapDataProvider = observer(
const [originalSightData, setOriginalSightData] = useState<SightData[]>();
const [routeData, setRouteData] = useState<RouteData>();
const [stationData, setStationData] = useState<StationData[]>();
const [stationData, setStationData] = useState<StationDataWithLanguage>({
RU: [],
EN: [],
ZH: [],
});
const [sightData, setSightData] = useState<SightData[]>();
const [routeChanges, setRouteChanges] = useState<Partial<RouteData>>({});
@ -92,15 +99,27 @@ export const MapDataProvider = observer(
setIsStationLoading(true);
setIsSightLoading(true);
const [routeResponse, stationResponse, sightResponse] =
await Promise.all([
axiosInstanceForGet.get(`/route/${routeId}`),
axiosInstanceForGet.get(`/route/${routeId}/station`),
axiosInstanceForGet.get(`/route/${routeId}/sight`),
]);
const [
routeResponse,
ruStationResponse,
enStationResponse,
zhStationResponse,
sightResponse,
] = await Promise.all([
axiosInstanceForGet(language).get(`/route/${routeId}`),
axiosInstanceForGet("ru").get(`/route/${routeId}/station`),
axiosInstanceForGet("en").get(`/route/${routeId}/station`),
axiosInstanceForGet("zh").get(`/route/${routeId}/station`),
axiosInstanceForGet(language).get(`/route/${routeId}/sight`),
]);
setOriginalRouteData(routeResponse.data as RouteData);
setOriginalStationData(stationResponse.data as StationData[]);
setOriginalStationData(ruStationResponse.data as StationData[]);
setStationData({
ru: ruStationResponse.data as StationData[],
en: enStationResponse.data as StationData[],
zh: zhStationResponse.data as StationData[],
});
setOriginalSightData(sightResponse.data as SightData[]);
setIsRouteLoading(false);
@ -115,17 +134,15 @@ export const MapDataProvider = observer(
};
fetchData();
}, [routeId, language]);
}, [routeId]);
useEffect(() => {
// combine changes with original data
if (originalRouteData)
setRouteData({ ...originalRouteData, ...routeChanges });
if (originalStationData) setStationData(originalStationData);
if (originalSightData) setSightData(originalSightData);
}, [
originalRouteData,
originalStationData,
originalSightData,
routeChanges,
stationChanges,
@ -189,7 +206,7 @@ export const MapDataProvider = observer(
return station;
});
} else {
const foundStation = stationData?.find(
const foundStation = stationData.ru?.find(
(station) => station.id === stationId
);
if (foundStation) {
@ -248,12 +265,12 @@ export const MapDataProvider = observer(
const value = useMemo(
() => ({
originalRouteData: originalRouteData,
originalStationData: originalStationData,
originalSightData: originalSightData,
routeData: routeData,
stationData: stationData,
sightData: sightData,
originalRouteData,
originalStationData,
originalSightData,
routeData,
stationData,
sightData,
isRouteLoading,
isStationLoading,
isSightLoading,

View File

@ -1,109 +1,148 @@
import { FederatedMouseEvent, Graphics } from "pixi.js";
import { BACKGROUND_COLOR, PATH_COLOR, STATION_RADIUS, STATION_OUTLINE_WIDTH, UP_SCALE } from "./Constants";
import {
BACKGROUND_COLOR,
PATH_COLOR,
STATION_RADIUS,
STATION_OUTLINE_WIDTH,
UP_SCALE,
} from "./Constants";
import { useTransform } from "./TransformContext";
import { useCallback, useEffect, useRef, useState } from "react";
import { StationData } from "./types";
import { useMapData } from "./MapDataContext";
import { coordinatesToLocal } from "./utils";
import { observer } from "mobx-react-lite";
import { languageStore } from "@stores";
interface StationProps {
station: StationData;
station: StationData;
ruLabel: string | null;
}
export function Station({
station
}: Readonly<StationProps>) {
const draw = useCallback((g: Graphics) => {
g.clear();
const coordinates = coordinatesToLocal(station.latitude, station.longitude);
g.circle(coordinates.x * UP_SCALE, coordinates.y * UP_SCALE, STATION_RADIUS);
g.fill({color: PATH_COLOR});
g.stroke({color: BACKGROUND_COLOR, width: STATION_OUTLINE_WIDTH});
}, []);
export const Station = observer(
({ station, ruLabel }: Readonly<StationProps>) => {
const draw = useCallback((g: Graphics) => {
g.clear();
const coordinates = coordinatesToLocal(
station.latitude,
station.longitude
);
g.circle(
coordinates.x * UP_SCALE,
coordinates.y * UP_SCALE,
STATION_RADIUS
);
g.fill({ color: PATH_COLOR });
g.stroke({ color: BACKGROUND_COLOR, width: STATION_OUTLINE_WIDTH });
}, []);
return (
<pixiContainer>
<pixiGraphics draw={draw}/>
<StationLabel station={station}/>
</pixiContainer>
);
}
return (
<pixiContainer>
<pixiGraphics draw={draw} />
<StationLabel station={station} ruLabel={ruLabel} />
</pixiContainer>
);
}
);
export function StationLabel({
station
}: Readonly<StationProps>) {
const { rotation, scale } = useTransform();
const { setStationOffset } = useMapData();
export const StationLabel = observer(
({ station, ruLabel }: Readonly<StationProps>) => {
const { language } = languageStore;
const { rotation, scale } = useTransform();
const { setStationOffset } = useMapData();
const [position, setPosition] = useState({ x: station.offset_x, y: station.offset_y });
const [isDragging, setIsDragging] = useState(false);
const [position, setPosition] = useState({
x: station.offset_x,
y: station.offset_y,
});
const [isDragging, setIsDragging] = useState(false);
const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });
const [startMousePosition, setStartMousePosition] = useState({ x: 0, y: 0 });
const [startMousePosition, setStartMousePosition] = useState({
x: 0,
y: 0,
});
if(!station) {
console.error("station is null");
return null;
}
if (!station) {
console.error("station is null");
return null;
}
const handlePointerDown = (e: FederatedMouseEvent) => {
setIsDragging(true);
setStartPosition({
x: position.x,
y: position.y
});
setStartMousePosition({
x: e.globalX,
y: e.globalY
});
e.stopPropagation();
setIsDragging(true);
setStartPosition({
x: position.x,
y: position.y,
});
setStartMousePosition({
x: e.globalX,
y: e.globalY,
});
e.stopPropagation();
};
const handlePointerMove = (e: FederatedMouseEvent) => {
if (!isDragging) return;
const dx = (e.globalX - startMousePosition.x);
const dy = (e.globalY - startMousePosition.y);
const newPosition = {
x: startPosition.x + dx,
y: startPosition.y + dy
};
setPosition(newPosition);
setStationOffset(station.id, newPosition.x, newPosition.y);
e.stopPropagation();
if (!isDragging) return;
const dx = e.globalX - startMousePosition.x;
const dy = e.globalY - startMousePosition.y;
const newPosition = {
x: startPosition.x + dx,
y: startPosition.y + dy,
};
setPosition(newPosition);
setStationOffset(station.id, newPosition.x, newPosition.y);
e.stopPropagation();
};
const handlePointerUp = (e: FederatedMouseEvent) => {
setIsDragging(false);
e.stopPropagation();
setIsDragging(false);
e.stopPropagation();
};
const coordinates = coordinatesToLocal(station.latitude, station.longitude);
const coordinates = coordinatesToLocal(station.latitude, station.longitude);
return (
<pixiContainer
eventMode='static'
interactive
onPointerDown={handlePointerDown}
onGlobalPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
onPointerUpOutside={handlePointerUp}
width={48}
height={48}
x={coordinates.x * UP_SCALE}
y={coordinates.y * UP_SCALE}
rotation={-rotation}
>
<pixiText
anchor={{x: 0.5, y: 0.5}}
text={station.name}
position={{
x: position.x/scale,
y: position.y/scale
}}
style={{
fontSize: 48,
fontWeight: 'bold',
fill: "#ffffff"
}}
/>
</pixiContainer>
);
}
return (
<pixiContainer
eventMode="static"
interactive
onPointerDown={handlePointerDown}
onGlobalPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
onPointerUpOutside={handlePointerUp}
width={48}
height={48}
x={coordinates.x * UP_SCALE}
y={coordinates.y * UP_SCALE}
rotation={-rotation}
>
<pixiText
anchor={{ x: 0.5, y: 0.5 }}
text={station.name}
position={{
x: position.x / scale,
y: position.y / scale,
}}
style={{
fontSize: 26,
fontWeight: "bold",
fill: "#ffffff",
}}
/>
{ruLabel && (
<pixiText
anchor={{ x: 0.5, y: -1 }}
text={ruLabel}
position={{
x: position.x / scale,
y: position.y / scale,
}}
style={{
fontSize: 16,
fontWeight: "bold",
fill: "#CCCCCC",
}}
/>
)}
</pixiContainer>
);
}
);

View File

@ -22,6 +22,8 @@ import { RightSidebar } from "./RightSidebar";
import { Widgets } from "./Widgets";
import { coordinatesToLocal } from "./utils";
import { LanguageSwitch } from "@/components/LanguageSwitch";
import { languageStore } from "@stores";
import { observer } from "mobx-react-lite";
extend({
Container,
@ -60,10 +62,12 @@ export const RoutePreview = () => {
);
};
export function RouteMap() {
export const RouteMap = observer(() => {
const { language } = languageStore;
const { setPosition, screenToLocal, setTransform, screenCenter } =
useTransform();
const { routeData, stationData, sightData, originalRouteData } = useMapData();
console.log(stationData);
const [points, setPoints] = useState<{ x: number; y: number }[]>([]);
const [isSetup, setIsSetup] = useState(false);
@ -145,11 +149,12 @@ export function RouteMap() {
<Application resizeTo={parentRef} background="#fff">
<InfiniteCanvas>
<TravelPath points={points} />
{stationData?.map((obj) => (
<Station station={obj} key={obj.id} />
))}
{sightData?.map((obj, index) => (
<Sight sight={obj} id={index} key={obj.id} />
{stationData[language].map((obj, index) => (
<Station
station={obj}
key={obj.id}
ruLabel={language === "ru" ? null : stationData.ru[index].name}
/>
))}
<pixiGraphics
@ -164,4 +169,4 @@ export function RouteMap() {
</Application>
</div>
);
}
});

View File

@ -9,7 +9,7 @@ import {
} from "@mui/material";
import { Edit, useAutocomplete } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { Controller } from "react-hook-form";
import { Controller, useWatch } from "react-hook-form";
import { useNavigate, useParams } from "react-router";
import { LinkedItems } from "../../components/LinkedItems";
import {
@ -76,6 +76,11 @@ export const RouteEdit = observer(() => {
...META_LANGUAGE(language),
});
const carrierId = useWatch({ control, name: "carrier_id" });
const cityId = carrierAutocompleteProps.options.find(
(option) => option.id === carrierId
)?.city_id;
const { autocompleteProps: governorAppealAutocompleteProps } =
useAutocomplete({
resource: "article",
@ -395,6 +400,7 @@ export const RouteEdit = observer(() => {
fields={stationFields}
title="остановки"
dragAllowed={true}
cityId={cityId}
/>
{/* <LinkedItems<VehicleItem>

View File

@ -433,7 +433,7 @@ export const SightCreate = observer(() => {
renderInput={(params) => (
<TextField
{...params}
label="Выберите водный знак (Левый верх)"
label="Выберите водяной знак (Левый верх)"
margin="normal"
variant="outlined"
error={!!errors.watermark_lu}
@ -475,7 +475,7 @@ export const SightCreate = observer(() => {
renderInput={(params) => (
<TextField
{...params}
label="Выберите водный знак (Правый верх)"
label="Выберите водяной знак (Правый верх)"
margin="normal"
variant="outlined"
error={!!errors.watermark_rd}

View File

@ -606,7 +606,7 @@ export const SightEdit = observer(() => {
renderInput={(params) => (
<TextField
{...params}
label="Выберите водный знак (Левый верх)"
label="Выберите водяной знак (Левый верх)"
margin="normal"
variant="outlined"
error={!!errors.arms}
@ -650,7 +650,7 @@ export const SightEdit = observer(() => {
renderInput={(params) => (
<TextField
{...params}
label="Выберите водный знак (Правый вверх)"
label="Выберите водяной знак (Правый вверх)"
margin="normal"
variant="outlined"
error={!!errors.arms}
@ -1479,7 +1479,7 @@ export const SightEdit = observer(() => {
renderInput={(params) => (
<TextField
{...params}
label="Выберите водный знак (Левый верх)"
label="Выберите водяной знак (Левый верх)"
margin="normal"
variant="outlined"
error={!!errors.arms}
@ -1523,7 +1523,7 @@ export const SightEdit = observer(() => {
renderInput={(params) => (
<TextField
{...params}
label="Выберите водный знак (Правый вверх)"
label="Выберите водяной знак (Правый вверх)"
margin="normal"
variant="outlined"
error={!!errors.arms}

View File

@ -192,6 +192,8 @@ export const StationEdit = observer(() => {
},
});
const cityId = watch("city_id");
return (
<Edit saveButtonProps={saveButtonProps}>
<Box
@ -353,6 +355,7 @@ export const StationEdit = observer(() => {
fields={sightFields}
title="достопримечательности"
dragAllowed={false}
cityId={cityId}
/>
)}
</Edit>

View File

@ -3,7 +3,6 @@ import dataProvider from "@refinedev/simple-rest";
import { TOKEN_KEY } from "@providers";
import axios from "axios";
import { languageStore } from "@stores";
export const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_KRBL_API,
@ -23,19 +22,22 @@ axiosInstance.interceptors.request.use((config) => {
return config;
});
export const axiosInstanceForGet = axios.create({
baseURL: import.meta.env.VITE_KRBL_API,
});
export const axiosInstanceForGet = (language: string) => {
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_KRBL_API,
});
axiosInstance.interceptors.request.use((config) => {
const token = localStorage.getItem(TOKEN_KEY);
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
axiosInstanceForGet.interceptors.request.use((config) => {
const token = localStorage.getItem(TOKEN_KEY);
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
config.headers["X-Language"] = language;
return config;
});
return axiosInstance;
};
config.headers["X-Language"] = languageStore.language;
return config;
});
const apiUrl = import.meta.env.VITE_KRBL_API;
export const customDataProvider = dataProvider(apiUrl, axiosInstance);