feat: Select article list in sight

This commit is contained in:
2025-05-31 06:35:05 +03:00
parent 5ef61bcef4
commit 2e6917406e
21 changed files with 899 additions and 498 deletions

View File

@ -2,133 +2,50 @@ import {
Button,
TextField,
Box,
Autocomplete,
Typography,
IconButton,
Paper,
Tooltip,
} from "@mui/material";
import { BackButton, Sight, sightsStore, TabPanel, Language } from "@shared";
import {
BackButton,
sightsStore,
TabPanel,
languageStore,
CreateSight,
Language,
cityStore,
CoordinatesInput,
} from "@shared";
import { LanguageSwitcher } from "@widgets";
import { ImagePlus, Info } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useState, useEffect } from "react";
import { useLocation } from "react-router-dom";
// Мокап данных для отображения, потом это будет приходить из store/props
// Keeping this mock for demonstration, but in a real app,
// this would come from the MobX store's 'sight' object.
const mockSightData = {
name: "Эрмитаж",
address: "Дворцовая площадь, 2",
city: "Санкт-Петербург", // или city_id, если будет Select
coordinates: "59.9398, 30.3146",
logo: null, // null или URL/ID медиа
watermark_lu: null,
watermark_rd: null,
};
import { observer } from "mobx-react-lite";
import { useState } from "react";
// Мокап для всплывающей подсказки
const watermarkTooltipText = "При наведении открывается просмотр в поп-апе";
const logoTooltipText = "При наведении открывается просмотр логотипа в поп-апе";
export const InformationTab = observer(
({ value, index }: { value: number; index: number }) => {
const {
sight,
cachedMultilingualContent,
updateCachedLanguageContent,
clearCachedMultilingualContent,
// Assuming you'll have an action to update the main sight object
updateSight,
} = sightsStore;
const { cities } = cityStore;
const { createSight, updateCreateSight, createSightAction } = sightsStore;
const [city, setCity] = useState<number>(0);
const [coordinates, setCoordinates] = useState({
latitude: 0,
longitude: 0,
});
const { language } = languageStore;
// Initialize local states with data from the MobX store's 'sight'
const [address, setAddress] = useState(sight?.address ?? "");
const [city, setCity] = useState(sight?.city ?? "");
const [coordinates, setCoordinates] = useState(
sight?.latitude && sight?.longitude
? `${sight.latitude}, ${sight.longitude}`
: ""
);
const [currentLanguage, setCurrentLanguage] = useState<Language>("ru");
const pathname = useLocation().pathname;
// Effect to initialize local states when `sight` data becomes available or changes
useEffect(() => {
if (sight) {
setAddress(sight.address ?? "");
setCity(sight.city ?? "");
setCoordinates(
sight.latitude && sight.longitude
? `${sight.latitude}, ${sight.longitude}`
: ""
);
// Initialize cached content if not already set
if (!cachedMultilingualContent) {
sightsStore.setCachedMultilingualContent({
ru: { name: sight.name, description: "", address: sight.address },
en: { name: "", description: "", address: "" },
zh: { name: "", description: "", address: "" },
});
}
}
}, [sight, cachedMultilingualContent]); // Add cachedMultilingualContent to dependencies
// Effect to clear cached content when the route changes
useEffect(() => {
clearCachedMultilingualContent();
}, [pathname, clearCachedMultilingualContent]);
const handleLanguageChange = (lang: Language) => {
setCurrentLanguage(lang);
};
const handleSelectMedia = (
type: "logo" | "watermark_lu" | "watermark_rd"
const handleChange = (
language: Language,
content: Partial<CreateSight[Language]>
) => {
// Here will be logic for opening modal window for media selection
console.log("Select media for:", type);
// In a real application, you might open a dialog here
// and update the sight object with the selected media URL/ID.
updateCreateSight(language, content);
};
const handleSave = () => {
// Parse coordinates back to latitude and longitude
let latitude: number | undefined;
let longitude: number | undefined;
const coordsArray = coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
if (
coordsArray.length === 2 &&
!isNaN(coordsArray[0]) &&
!isNaN(coordsArray[1])
) {
latitude = coordsArray[0];
longitude = coordsArray[1];
const handleSave = async () => {
try {
await createSightAction(createSight[language], city, coordinates);
} catch (error) {
console.error(error);
}
// Prepare the updated sight data
const updatedSightData = {
...sight, // Keep existing sight data
address: address,
city: city,
latitude: latitude,
longitude: longitude,
// Assuming logo and watermark updates would happen via handleSelectMedia
// and then be reflected in the sight object in the store.
};
// Here we would save both the sight data and the multilingual content
console.log("Saving general information and multilingual content...", {
updatedSightData,
multilingualContent: cachedMultilingualContent,
});
// Call an action from your store to save the data
// For example:
// sightsStore.saveSight({ ...updatedSightData, multilingualContent: cachedMultilingualContent });
// You might have a specific action in your store for saving all this data.
};
return (
@ -164,10 +81,10 @@ export const InformationTab = observer(
}}
>
<TextField
label={`Название (${currentLanguage.toUpperCase()})`}
value={cachedMultilingualContent?.[currentLanguage]?.name ?? ""}
label={`Название (${language.toUpperCase()})`}
value={createSight[language]?.name ?? ""}
onChange={(e) => {
updateCachedLanguageContent(currentLanguage, {
handleChange(language as Language, {
name: e.target.value,
});
}}
@ -175,13 +92,10 @@ export const InformationTab = observer(
variant="outlined"
/>
<TextField
label={`Описание (${currentLanguage.toUpperCase()})`}
value={
cachedMultilingualContent?.[currentLanguage]?.description ??
""
}
label={`Описание (${language.toUpperCase()})`}
value={createSight?.[language]?.description ?? ""}
onChange={(e) => {
updateCachedLanguageContent(currentLanguage, {
handleChange(language as Language, {
description: e.target.value,
});
}}
@ -192,37 +106,30 @@ export const InformationTab = observer(
/>
<TextField
label="Адрес"
value={address}
value={createSight?.[language]?.address ?? ""}
onChange={(e) => {
setAddress(e.target.value);
handleChange(language as Language, {
address: e.target.value,
});
}}
fullWidth
variant="outlined"
/>
<TextField
label="Город"
value={city}
onChange={(e) => {
setCity(e.target.value);
<Autocomplete
options={cities}
getOptionLabel={(option) => option.name}
onChange={(_, value) => {
setCity(value?.id ?? 0);
}}
fullWidth
variant="outlined"
renderInput={(params) => (
<TextField {...params} label="Город" />
)}
/>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<TextField
label="Координаты"
value={coordinates}
onChange={(e) => {
setCoordinates(e.target.value);
}}
fullWidth
variant="outlined"
helperText="Формат: широта, долгота (например, 59.9398, 30.3146)"
/>
</Box>
<CoordinatesInput setValue={setCoordinates} />
</Box>
{/* Правая колонка для логотипа и водяных знаков */}
{/* Правая колонка для логотипа и водяных знаков
<Box
sx={{
display: "flex",
@ -230,7 +137,7 @@ export const InformationTab = observer(
gap: 4,
}}
>
{/* Водяные знаки */}
<Box
sx={{
display: "flex",
@ -453,29 +360,29 @@ export const InformationTab = observer(
</Paper>
</Box>
</Box>
</Box>
</Box> */}
{/* LanguageSwitcher positioned at the top right */}
<Box sx={{ position: "absolute", top: 0, right: 0, zIndex: 1 }}>
<LanguageSwitcher onLanguageChange={handleLanguageChange} />
</Box>
{/* LanguageSwitcher positioned at the top right */}
{/* Save Button fixed at the bottom right */}
<Box
sx={{
position: "absolute",
bottom: 0,
right: 0,
padding: 2,
backgroundColor: "background.paper", // To ensure it stands out over content
width: "100%", // Take full width to cover content below it
display: "flex",
justifyContent: "flex-end", // Align to the right
}}
>
<Button variant="contained" color="success" onClick={handleSave}>
Сохранить
</Button>
<LanguageSwitcher />
{/* Save Button fixed at the bottom right */}
<Box
sx={{
position: "absolute",
bottom: 0,
right: 0,
padding: 2,
backgroundColor: "background.paper", // To ensure it stands out over content
width: "100%", // Take full width to cover content below it
display: "flex",
justifyContent: "flex-end", // Align to the right
}}
>
<Button variant="contained" color="success" onClick={handleSave}>
Сохранить
</Button>
</Box>
</Box>
</Box>
</TabPanel>