Compare commits

...

2 Commits

Author SHA1 Message Date
a357994025 feat: Update pop-up logic 2025-10-02 04:45:43 +03:00
7382a85082 feat: Update city logic in map page with scrollbar 2025-10-02 04:38:15 +03:00
7 changed files with 521 additions and 415 deletions

View File

@@ -1,62 +0,0 @@
# Селектор городов
## Описание функциональности
Добавлена функциональность выбора города в админ-панели "Белые ночи":
### Основные возможности:
1. **Селектор городов в шапке приложения**
- Расположен рядом с именем пользователя в верхней части приложения
- Показывает список всех доступных городов
- Имеет иконку MapPin для лучшего UX
2. **Сохранение в localStorage**
- Выбранный город автоматически сохраняется в localStorage
- При перезагрузке страницы выбранный город восстанавливается
3. **Автоматическое использование в формах**
- При создании новой станции выбранный город автоматически подставляется
- При создании нового перевозчика выбранный город автоматически подставляется
- Пользователь может изменить город в форме при необходимости
### Технические детали:
#### Новые компоненты и сторы:
- `SelectedCityStore` - стор для управления выбранным городом
- `CitySelector` - компонент селектора городов
- `useSelectedCity` - хук для удобного доступа к выбранному городу
#### Интеграция:
- Селектор добавлен в `Layout` компонент
- Интегрирован в `StationCreatePage` и `CarrierCreatePage`
- Использует существующий `CityStore` для получения списка городов
#### Файлы, которые были изменены:
- `src/widgets/Layout/index.tsx` - добавлен CitySelector
- `src/pages/Station/StationCreatePage/index.tsx` - автоматическая подстановка города
- `src/pages/Carrier/CarrierCreatePage/index.tsx` - автоматическая подстановка города
- `src/shared/store/index.ts` - добавлен экспорт SelectedCityStore
- `src/widgets/index.ts` - добавлен экспорт CitySelector
- `src/shared/index.tsx` - добавлен экспорт hooks
#### Новые файлы:
- `src/shared/store/SelectedCityStore/index.ts`
- `src/widgets/CitySelector/index.tsx`
- `src/shared/hooks/useSelectedCity.ts`
- `src/shared/hooks/index.ts`
### Использование:
1. Пользователь выбирает город в селекторе в шапке приложения
2. Выбранный город сохраняется в localStorage
3. При создании новой станции или перевозчика выбранный город автоматически подставляется в форму
4. Пользователь может изменить город в форме если нужно
Функциональность полностью интегрирована и готова к использованию.

View File

@@ -52,7 +52,7 @@ import { FeatureLike } from "ol/Feature";
import { createEmpty, extend, getCenter } from "ol/extent"; import { createEmpty, extend, getCenter } from "ol/extent";
// --- CUSTOM SCROLLBAR STYLES --- // --- CUSTOM SCROLLBAR STYLES ---
const scrollbarHideStyles = ` const scrollbarStyles = `
.scrollbar-hide { .scrollbar-hide {
-ms-overflow-style: none; -ms-overflow-style: none;
scrollbar-width: none; scrollbar-width: none;
@@ -60,11 +60,34 @@ const scrollbarHideStyles = `
.scrollbar-hide::-webkit-scrollbar { .scrollbar-hide::-webkit-scrollbar {
display: none; display: none;
} }
.scrollbar-visible {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 #f1f5f9;
}
.scrollbar-visible::-webkit-scrollbar {
width: 8px;
}
.scrollbar-visible::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 4px;
}
.scrollbar-visible::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
.scrollbar-visible::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
`; `;
if (typeof document !== "undefined") { if (typeof document !== "undefined") {
const styleElement = document.createElement("style"); const styleElement = document.createElement("style");
styleElement.textContent = scrollbarHideStyles; styleElement.textContent = scrollbarStyles;
document.head.appendChild(styleElement); document.head.appendChild(styleElement);
} }
@@ -73,7 +96,13 @@ if (typeof document !== "undefined") {
import { languageInstance } from "@shared"; import { languageInstance } from "@shared";
import { makeAutoObservable } from "mobx"; import { makeAutoObservable } from "mobx";
import { stationsStore, routeStore, sightsStore, menuStore } from "@shared"; import {
stationsStore,
routeStore,
sightsStore,
menuStore,
selectedCityStore,
} from "@shared";
// Функция для сброса кешей карты // Функция для сброса кешей карты
export const clearMapCaches = () => { export const clearMapCaches = () => {
@@ -101,6 +130,7 @@ interface ApiStation {
name: string; name: string;
latitude: number; latitude: number;
longitude: number; longitude: number;
city_id: number;
created_at?: string; created_at?: string;
} }
@@ -110,12 +140,12 @@ interface ApiSight {
description: string; description: string;
latitude: number; latitude: number;
longitude: number; longitude: number;
city_id: number;
created_at?: string; created_at?: string;
} }
export type SortType = "name_asc" | "name_desc" | "date_asc" | "date_desc"; export type SortType = "name_asc" | "name_desc" | "date_asc" | "date_desc";
class MapStore { class MapStore {
constructor() { constructor() {
makeAutoObservable(this); makeAutoObservable(this);
@@ -151,16 +181,32 @@ class MapStore {
return sorted.sort((a, b) => b.name.localeCompare(a.name)); return sorted.sort((a, b) => b.name.localeCompare(a.name));
case "date_asc": case "date_asc":
return sorted.sort((a, b) => { return sorted.sort((a, b) => {
if ('created_at' in a && 'created_at' in b && a.created_at && b.created_at) { if (
return new Date(a.created_at).getTime() - new Date(b.created_at).getTime(); "created_at" in a &&
"created_at" in b &&
a.created_at &&
b.created_at
) {
return (
new Date(a.created_at).getTime() -
new Date(b.created_at).getTime()
);
} }
// Фоллбэк: сортировка по ID, если дата недоступна // Фоллбэк: сортировка по ID, если дата недоступна
return a.id - b.id; return a.id - b.id;
}); });
case "date_desc": case "date_desc":
return sorted.sort((a, b) => { return sorted.sort((a, b) => {
if ('created_at' in a && 'created_at' in b && a.created_at && b.created_at) { if (
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); "created_at" in a &&
"created_at" in b &&
a.created_at &&
b.created_at
) {
return (
new Date(b.created_at).getTime() -
new Date(a.created_at).getTime()
);
} }
// Фоллбэк: сортировка по ID, если дата недоступна // Фоллбэк: сортировка по ID, если дата недоступна
return b.id - a.id; return b.id - a.id;
@@ -179,6 +225,26 @@ class MapStore {
return this.sortFeatures(this.sights, this.sightSort); return this.sortFeatures(this.sights, this.sightSort);
} }
// ГЕТТЕРЫ ДЛЯ ФИЛЬТРАЦИИ ПО ГОРОДУ
get filteredStations(): ApiStation[] {
const selectedCityId = selectedCityStore.selectedCityId;
if (!selectedCityId) {
return this.sortedStations;
}
return this.sortedStations.filter(
(station) => station.city_id === selectedCityId
);
}
get filteredSights(): ApiSight[] {
const selectedCityId = selectedCityStore.selectedCityId;
if (!selectedCityId) {
return this.sortedSights;
}
return this.sortedSights.filter(
(sight) => sight.city_id === selectedCityId
);
}
getRoutes = async () => { getRoutes = async () => {
const response = await languageInstance("ru").get("/route"); const response = await languageInstance("ru").get("/route");
@@ -243,7 +309,15 @@ class MapStore {
address: "", address: "",
system_name: name, system_name: name,
}); });
stationsStore.setCreateCommonData({ latitude, longitude, city_id: 1 }); const selectedCityId = selectedCityStore.selectedCityId || 1;
const selectedCityName =
selectedCityStore.selectedCityName || "Неизвестный город";
stationsStore.setCreateCommonData({
latitude,
longitude,
city_id: selectedCityId,
city: selectedCityName,
});
await stationsStore.createStation(); await stationsStore.createStation();
createdItem = createdItem =
@@ -290,7 +364,11 @@ class MapStore {
sightsStore.updateCreateSight("en", { name, address: "" }); sightsStore.updateCreateSight("en", { name, address: "" });
sightsStore.updateCreateSight("zh", { name, address: "" }); sightsStore.updateCreateSight("zh", { name, address: "" });
await sightsStore.createSightAction(1, { latitude, longitude }); const selectedCityId = selectedCityStore.selectedCityId || 1;
await sightsStore.createSightAction(selectedCityId, {
latitude,
longitude,
});
createdItem = sightsStore.sights[sightsStore.sights.length - 1]; createdItem = sightsStore.sights[sightsStore.sights.length - 1];
} else { } else {
throw new Error(`Unknown feature type for creation: ${featureType}`); throw new Error(`Unknown feature type for creation: ${featureType}`);
@@ -1181,9 +1259,9 @@ class MapService {
} }
public loadFeaturesFromApi( public loadFeaturesFromApi(
apiStations: typeof mapStore.stations, _apiStations: typeof mapStore.stations,
apiRoutes: typeof mapStore.routes, apiRoutes: typeof mapStore.routes,
apiSights: typeof mapStore.sights _apiSights: typeof mapStore.sights
): void { ): void {
if (!this.map) return; if (!this.map) return;
@@ -1191,7 +1269,11 @@ class MapService {
const pointFeatures: Feature<Point>[] = []; const pointFeatures: Feature<Point>[] = [];
const lineFeatures: Feature<LineString>[] = []; const lineFeatures: Feature<LineString>[] = [];
apiStations.forEach((station) => { // Используем фильтрованные данные из mapStore
const filteredStations = mapStore.filteredStations;
const filteredSights = mapStore.filteredSights;
filteredStations.forEach((station) => {
if (station.longitude == null || station.latitude == null) return; if (station.longitude == null || station.latitude == null) return;
const point = new Point( const point = new Point(
transform( transform(
@@ -1206,7 +1288,7 @@ class MapService {
pointFeatures.push(feature); pointFeatures.push(feature);
}); });
apiSights.forEach((sight) => { filteredSights.forEach((sight) => {
if (sight.longitude == null || sight.latitude == null) return; if (sight.longitude == null || sight.latitude == null) return;
const point = new Point( const point = new Point(
transform([sight.longitude, sight.latitude], "EPSG:4326", projection) transform([sight.longitude, sight.latitude], "EPSG:4326", projection)
@@ -1329,6 +1411,7 @@ class MapService {
name: properties.name, name: properties.name,
latitude: lat, latitude: lat,
longitude: lon, longitude: lon,
city_id: properties.city_id || 1, // Default city_id if not available
}); });
} else if (featureType === "sight") { } else if (featureType === "sight") {
const coords = (geometry as Point).getCoordinates(); const coords = (geometry as Point).getCoordinates();
@@ -1339,6 +1422,7 @@ class MapService {
description: properties.description, description: properties.description,
latitude: lat, latitude: lat,
longitude: lon, longitude: lon,
city_id: properties.city_id || 1, // Default city_id if not available
}); });
} else if (featureType === "route") { } else if (featureType === "route") {
const coords = (geometry as LineString).getCoordinates(); const coords = (geometry as LineString).getCoordinates();
@@ -2188,7 +2272,8 @@ interface MapSightbarProps {
activeSection: string | null; activeSection: string | null;
setActiveSection: (section: string | null) => void; setActiveSection: (section: string | null) => void;
} }
const MapSightbar: React.FC<MapSightbarProps> = observer(({ const MapSightbar: React.FC<MapSightbarProps> = observer(
({
mapService, mapService,
mapFeatures, mapFeatures,
selectedFeature, selectedFeature,
@@ -2203,21 +2288,71 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
const [sightSort, setSightSort] = useState<SortType>("name_asc"); const [sightSort, setSightSort] = useState<SortType>("name_asc");
const { isOpen } = menuStore; const { isOpen } = menuStore;
const { selectedCityId } = selectedCityStore;
const actualFeatures = useMemo( const actualFeatures = useMemo(
() => mapFeatures.filter((f) => !f.get("isProxy")), () => mapFeatures.filter((f) => !f.get("isProxy")),
[mapFeatures] [mapFeatures]
); );
// Создаем объединенный список всех объектов для поиска
const allFeatures = useMemo(() => {
const stations = mapStore.filteredStations.map((station) => {
const feature = new Feature({
geometry: new Point(
transform(
[station.longitude, station.latitude],
"EPSG:4326",
"EPSG:3857"
)
),
name: station.name,
});
feature.setId(`station-${station.id}`);
feature.set("featureType", "station");
feature.set("created_at", station.created_at);
return feature;
});
const sights = mapStore.filteredSights.map((sight) => {
const feature = new Feature({
geometry: new Point(
transform(
[sight.longitude, sight.latitude],
"EPSG:4326",
"EPSG:3857"
)
),
name: sight.name,
description: sight.description,
});
feature.setId(`sight-${sight.id}`);
feature.set("featureType", "sight");
feature.set("created_at", sight.created_at);
return feature;
});
const lines = actualFeatures.filter(
(f) => f.get("featureType") === "route"
);
return [...stations, ...sights, ...lines];
}, [
mapStore.filteredStations,
mapStore.filteredSights,
actualFeatures,
selectedCityId,
mapStore,
]);
const filteredFeatures = useMemo(() => { const filteredFeatures = useMemo(() => {
if (!searchQuery.trim()) return actualFeatures; if (!searchQuery.trim()) return allFeatures;
return actualFeatures.filter((f) => return allFeatures.filter((f) =>
((f.get("name") as string) || "") ((f.get("name") as string) || "")
.toLowerCase() .toLowerCase()
.includes(searchQuery.toLowerCase()) .includes(searchQuery.toLowerCase())
); );
}, [actualFeatures, searchQuery]); }, [allFeatures, searchQuery]);
const handleFeatureClick = useCallback( const handleFeatureClick = useCallback(
(id: string | number) => { (id: string | number) => {
@@ -2279,13 +2414,13 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
case "name_asc": case "name_asc":
return sorted.sort((a, b) => return sorted.sort((a, b) =>
((a.get("name") as string) || "").localeCompare( ((a.get("name") as string) || "").localeCompare(
((b.get("name") as string) || "") (b.get("name") as string) || ""
) )
); );
case "name_desc": case "name_desc":
return sorted.sort((a, b) => return sorted.sort((a, b) =>
((b.get("name") as string) || "").localeCompare( ((b.get("name") as string) || "").localeCompare(
((a.get("name") as string) || "") (a.get("name") as string) || ""
) )
); );
case "date_asc": case "date_asc":
@@ -2316,7 +2451,9 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
const stations = filteredFeatures.filter( const stations = filteredFeatures.filter(
(f) => f.get("featureType") === "station" (f) => f.get("featureType") === "station"
); );
const lines = filteredFeatures.filter((f) => f.get("featureType") === "route"); const lines = filteredFeatures.filter(
(f) => f.get("featureType") === "route"
);
const sights = filteredFeatures.filter( const sights = filteredFeatures.filter(
(f) => f.get("featureType") === "sight" (f) => f.get("featureType") === "sight"
); );
@@ -2379,7 +2516,7 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
/> />
<span <span
className={`font-medium whitespace-nowrap overflow-x-auto block className={`font-medium whitespace-nowrap overflow-x-auto block
scrollbar-thin scrollbar-thumb-gray-200 scrollbar-track-transparent`} scrollbar-visible`}
title={fName} title={fName}
> >
{fName} {fName}
@@ -2391,7 +2528,8 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
const featureTypeVal = feature.get("featureType"); const featureTypeVal = feature.get("featureType");
if (featureTypeVal) handleEditFeature(featureTypeVal, fId); if (featureTypeVal)
handleEditFeature(featureTypeVal, fId);
}} }}
className="p-1 rounded-full text-gray-400 hover:text-blue-600 hover:bg-blue-100 transition-colors" className="p-1 rounded-full text-gray-400 hover:text-blue-600 hover:bg-blue-100 transition-colors"
title="Редактировать детали" title="Редактировать детали"
@@ -2427,10 +2565,9 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
title: `Остановки (${sortedStations.length})`, title: `Остановки (${sortedStations.length})`,
icon: <Bus size={20} />, icon: <Bus size={20} />,
count: sortedStations.length, count: sortedStations.length,
content: ( sortControl: (
<> <div className="flex items-center space-x-2 p-3 bg-white border-b border-gray-200">
<div className="flex items-center space-x-2 mb-2"> <label className="text-sm text-gray-700">Сортировка:</label>
<label>Сортировка:</label>
<select <select
value={stationSort} value={stationSort}
onChange={(e) => setStationSort(e.target.value as SortType)} onChange={(e) => setStationSort(e.target.value as SortType)}
@@ -2440,15 +2577,15 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
<option value="name_desc">Имя </option> <option value="name_desc">Имя </option>
</select> </select>
</div> </div>
{renderFeatureList(sortedStations, "station", MapPin)}
</>
), ),
content: renderFeatureList(sortedStations, "station", MapPin),
}, },
{ {
id: "lines", id: "lines",
title: `Маршруты (${lines.length})`, title: `Маршруты (${lines.length})`,
icon: <RouteIcon size={20} />, icon: <RouteIcon size={20} />,
count: lines.length, count: lines.length,
sortControl: null,
content: renderFeatureList(lines, "route", ArrowRightLeft), content: renderFeatureList(lines, "route", ArrowRightLeft),
}, },
{ {
@@ -2456,10 +2593,9 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
title: `Достопримечательности (${sortedSights.length})`, title: `Достопримечательности (${sortedSights.length})`,
icon: <Landmark size={20} />, icon: <Landmark size={20} />,
count: sortedSights.length, count: sortedSights.length,
content: ( sortControl: (
<> <div className="flex items-center space-x-2 p-3 bg-white border-b border-gray-200">
<div className="flex items-center space-x-2 mb-2"> <label className="text-sm text-gray-700">Сортировка:</label>
<label>Сортировка:</label>
<select <select
value={sightSort} value={sightSort}
onChange={(e) => setSightSort(e.target.value as SortType)} onChange={(e) => setSightSort(e.target.value as SortType)}
@@ -2469,9 +2605,8 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
<option value="name_desc">Имя </option> <option value="name_desc">Имя </option>
</select> </select>
</div> </div>
{renderFeatureList(sortedSights, "sight", Landmark)}
</>
), ),
content: renderFeatureList(sortedSights, "sight", Landmark),
}, },
]; ];
@@ -2480,7 +2615,11 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
}, [isOpen]); }, [isOpen]);
return ( return (
<div className={`${isOpen ? "w-[360px]" : "w-[590px]"} transition-all duration-300 ease-in-out relative bg-gray-50 shadow-lg flex flex-col border-l border-gray-200 h-[90vh]`}> <div
className={`${
isOpen ? "w-[360px]" : "w-[590px]"
} transition-all duration-300 ease-in-out relative bg-gray-50 shadow-lg flex flex-col border-l border-gray-200 h-[90vh]`}
>
<div className="p-4 bg-gray-700 text-white"> <div className="p-4 bg-gray-700 text-white">
<h2 className="text-lg font-semibold">Панель управления</h2> <h2 className="text-lg font-semibold">Панель управления</h2>
</div> </div>
@@ -2493,7 +2632,7 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
</div> </div>
<div className="flex-1 flex flex-col min-h-0 overflow-y-auto"> <div className="flex-1 flex flex-col min-h-0 overflow-y-auto scrollbar-visible">
{filteredFeatures.length === 0 && searchQuery ? ( {filteredFeatures.length === 0 && searchQuery ? (
<div className="p-4 text-center text-gray-500"> <div className="p-4 text-center text-gray-500">
Ничего не найдено. Ничего не найдено.
@@ -2536,14 +2675,19 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
</span> </span>
</button> </button>
<div {activeSection === s.id && (
className={`overflow-y-auto scrollbar-hide bg-white ${ <>
activeSection === s.id ? "block" : "hidden" {s.sortControl && (
}`} <div className="flex-shrink-0">{s.sortControl}</div>
> )}
<div className="p-3 text-sm text-gray-600">{s.content}</div> <div className="overflow-y-auto scrollbar-visible bg-white flex-1 min-h-0">
<div className="p-3 text-sm text-gray-600">
{s.content}
</div> </div>
</div> </div>
</>
)}
</div>
) )
) )
)} )}
@@ -2561,9 +2705,10 @@ const MapSightbar: React.FC<MapSightbarProps> = observer(({
)} )}
</div> </div>
); );
}); }
);
// --- MAP PAGE COMPONENT --- // --- MAP PAGE COMPONENT ---
export const MapPage: React.FC = () => { export const MapPage: React.FC = observer(() => {
const mapRef = useRef<HTMLDivElement | null>(null); const mapRef = useRef<HTMLDivElement | null>(null);
const tooltipRef = useRef<HTMLDivElement | null>(null); const tooltipRef = useRef<HTMLDivElement | null>(null);
const [mapServiceInstance, setMapServiceInstance] = const [mapServiceInstance, setMapServiceInstance] =
@@ -2584,6 +2729,8 @@ export const MapPage: React.FC = () => {
string | null string | null
>(() => getStoredActiveSection() || "layers"); >(() => getStoredActiveSection() || "layers");
const { selectedCityId } = selectedCityStore;
const handleFeaturesChange = useCallback( const handleFeaturesChange = useCallback(
(feats: Feature<Geometry>[]) => setMapFeatures([...feats]), (feats: Feature<Geometry>[]) => setMapFeatures([...feats]),
[] []
@@ -2750,6 +2897,22 @@ export const MapPage: React.FC = () => {
saveActiveSection(activeSectionFromParent); saveActiveSection(activeSectionFromParent);
}, [activeSectionFromParent]); }, [activeSectionFromParent]);
// Перезагружаем данные при изменении города
useEffect(() => {
if (mapServiceInstance && !isDataLoading) {
// Очищаем текущие объекты на карте
mapServiceInstance.pointSource.clear();
mapServiceInstance.lineSource.clear();
// Загружаем новые данные с фильтрацией по городу
mapServiceInstance.loadFeaturesFromApi(
mapStore.stations,
mapStore.routes,
mapStore.sights
);
}
}, [selectedCityId, mapServiceInstance, isDataLoading]);
const showLoader = isMapLoading || isDataLoading; const showLoader = isMapLoading || isDataLoading;
const showContent = mapServiceInstance && !showLoader && !error; const showContent = mapServiceInstance && !showLoader && !error;
const isAnythingSelected = const isAnythingSelected =
@@ -2866,4 +3029,4 @@ export const MapPage: React.FC = () => {
)} )}
</div> </div>
); );
}; });

View File

@@ -67,7 +67,8 @@ export const StationCreatePage = observer(() => {
// ОБНОВЛЕННАЯ ФУНКЦИЯ: Проверка и вызов окна или создания // ОБНОВЛЕННАЯ ФУНКЦИЯ: Проверка и вызов окна или создания
const handleCreate = async () => { const handleCreate = async () => {
const isCityMissing = !createStationData.common.city_id; const isCityMissing = !createStationData.common.city_id;
const isNameMissing = !createStationData[language].name; // Проверяем названия на всех языках
const isNameMissing = !createStationData.ru.name || !createStationData.en.name || !createStationData.zh.name;
if (isCityMissing || isNameMissing) { if (isCityMissing || isNameMissing) {
setIsSaveWarningOpen(true); setIsSaveWarningOpen(true);

View File

@@ -67,7 +67,8 @@ export const StationEditPage = observer(() => {
// ОБНОВЛЕННАЯ ФУНКЦИЯ: Проверка и вызов окна или редактирования // ОБНОВЛЕННАЯ ФУНКЦИЯ: Проверка и вызов окна или редактирования
const handleEdit = async () => { const handleEdit = async () => {
const isCityMissing = !editStationData.common.city_id; const isCityMissing = !editStationData.common.city_id;
const isNameMissing = !editStationData[language].name; // Проверяем названия на всех языках
const isNameMissing = !editStationData.ru.name || !editStationData.en.name || !editStationData.zh.name;
if (isCityMissing || isNameMissing) { if (isCityMissing || isNameMissing) {
setIsSaveWarningOpen(true); setIsSaveWarningOpen(true);

View File

@@ -5,9 +5,10 @@ export const SaveWithoutCityAgree = ({ blocker }: { blocker: any }) => {
<div className="fixed top-0 left-0 w-screen h-screen flex justify-center items-center z-10000 bg-black/30"> <div className="fixed top-0 left-0 w-screen h-screen flex justify-center items-center z-10000 bg-black/30">
<div className="bg-white p-4 w-140 rounded-lg flex flex-col gap-4 items-center"> <div className="bg-white p-4 w-140 rounded-lg flex flex-col gap-4 items-center">
<p className="text-black w-140 text-center"> <p className="text-black w-140 text-center">
Вы не указали город и/или не заполнили названия на всех языках. Вы не указали город и/или не заполнили названия на всех языках
(русский, английский, китайский).
<br /> <br />
Сохранить достопримечательность без этой информации? Сохранить без этой информации?
</p> </p>
<div className="flex gap-4 justify-center"> <div className="flex gap-4 justify-center">
<Button variant="contained" onClick={() => blocker.proceed()}> <Button variant="contained" onClick={() => blocker.proceed()}>

View File

@@ -119,7 +119,8 @@ export const CreateInformationTab = observer(
const handleSave = async () => { const handleSave = async () => {
const isCityMissing = !sight.city_id; const isCityMissing = !sight.city_id;
const isNameMissing = !sight[language].name; // Проверяем названия на всех языках
const isNameMissing = !sight.ru.name || !sight.en.name || !sight.zh.name;
if (isCityMissing || isNameMissing) { if (isCityMissing || isNameMissing) {
setIsSaveWarningOpen(true); setIsSaveWarningOpen(true);

View File

@@ -128,7 +128,8 @@ export const InformationTab = observer(
// ОБНОВЛЕННАЯ ФУНКЦИЯ: Проверка и вызов окна или сохранения // ОБНОВЛЕННАЯ ФУНКЦИЯ: Проверка и вызов окна или сохранения
const handleSave = async () => { const handleSave = async () => {
const isCityMissing = !sight.common.city_id; const isCityMissing = !sight.common.city_id;
const isNameMissing = !sight[language].name; // Проверяем названия на всех языках
const isNameMissing = !sight.ru.name || !sight.en.name || !sight.zh.name;
if (isCityMissing || isNameMissing) { if (isCityMissing || isNameMissing) {
setIsSaveWarningOpen(true); setIsSaveWarningOpen(true);