feat: Add translation on 3 languages for sight page
This commit is contained in:
@ -6,46 +6,45 @@ import {
|
||||
Typography,
|
||||
Paper,
|
||||
Tooltip,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
MenuItem,
|
||||
Menu as MuiMenu,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
BackButton,
|
||||
sightsStore,
|
||||
TabPanel,
|
||||
languageStore,
|
||||
CreateSight,
|
||||
Language,
|
||||
cityStore,
|
||||
CoordinatesInput,
|
||||
editSightStore,
|
||||
SelectMediaDialog,
|
||||
PreviewMediaDialog,
|
||||
SightLanguageInfo,
|
||||
SightCommonInfo,
|
||||
} from "@shared";
|
||||
import { LanguageSwitcher } from "@widgets";
|
||||
import { Info, ImagePlus } from "lucide-react";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRef, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
// Мокап для всплывающей подсказки
|
||||
|
||||
export const InformationTab = observer(
|
||||
({ value, index }: { value: number; index: number }) => {
|
||||
const { cities } = cityStore;
|
||||
const [isMediaModalOpen, setIsMediaModalOpen] = useState(false);
|
||||
const [, setIsMediaModalOpen] = useState(false);
|
||||
const [mediaId, setMediaId] = useState<string>("");
|
||||
const [isPreviewMediaOpen, setIsPreviewMediaOpen] = useState(false);
|
||||
const { createSight, updateCreateSight, createSightAction } = sightsStore;
|
||||
const { sightInfo } = editSightStore;
|
||||
const [city, setCity] = useState<number>(sightInfo.city_id ?? 0);
|
||||
const [coordinates, setCoordinates] = useState({
|
||||
latitude: sightInfo.latitude ?? 0,
|
||||
longitude: sightInfo.longitude ?? 0,
|
||||
});
|
||||
|
||||
const { language } = languageStore;
|
||||
const { sight, updateSightInfo } = editSightStore;
|
||||
|
||||
const data = sight[language];
|
||||
const common = sight.common;
|
||||
|
||||
const [, setCity] = useState<number>(common.city_id ?? 0);
|
||||
const [coordinates, setCoordinates] = useState<string>(`0 0`);
|
||||
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
// Menu state for each media button
|
||||
@ -63,6 +62,14 @@ export const InformationTab = observer(
|
||||
setActiveMenuType(type);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Показывать только при инициализации (не менять при ошибках пользователя)
|
||||
if (common.latitude !== 0 || common.longitude !== 0) {
|
||||
setCoordinates(`${common.latitude} ${common.longitude}`);
|
||||
}
|
||||
// если координаты обнулились — оставить поле как есть
|
||||
}, [common.latitude, common.longitude]);
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setMenuAnchorEl(null);
|
||||
setActiveMenuType(null);
|
||||
@ -77,7 +84,7 @@ export const InformationTab = observer(
|
||||
handleMenuClose();
|
||||
};
|
||||
|
||||
const handleMediaSelect = (selectedMediaId: string) => {
|
||||
const handleMediaSelect = () => {
|
||||
if (!activeMenuType) return;
|
||||
|
||||
// Close the dialog
|
||||
@ -87,17 +94,10 @@ export const InformationTab = observer(
|
||||
|
||||
const handleChange = (
|
||||
language: Language,
|
||||
content: Partial<CreateSight[Language]>
|
||||
content: Partial<SightLanguageInfo | SightCommonInfo>,
|
||||
common: boolean = false
|
||||
) => {
|
||||
updateCreateSight(language, content);
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
await createSightAction(city, coordinates);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
updateSightInfo(language, content, common);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -135,7 +135,7 @@ export const InformationTab = observer(
|
||||
>
|
||||
<TextField
|
||||
label={`Название (${language.toUpperCase()})`}
|
||||
value={sightInfo[language]?.info?.name ?? ""}
|
||||
value={data.name}
|
||||
onChange={(e) => {
|
||||
handleChange(language as Language, {
|
||||
name: e.target.value,
|
||||
@ -147,7 +147,7 @@ export const InformationTab = observer(
|
||||
|
||||
<TextField
|
||||
label="Адрес"
|
||||
value={sightInfo[language]?.info?.address ?? ""}
|
||||
value={data.address}
|
||||
onChange={(e) => {
|
||||
handleChange(language as Language, {
|
||||
address: e.target.value,
|
||||
@ -158,20 +158,57 @@ export const InformationTab = observer(
|
||||
/>
|
||||
|
||||
<Autocomplete
|
||||
options={cities}
|
||||
value={cities.find((city) => city.id === sightInfo.city_id)}
|
||||
options={cities ?? []}
|
||||
value={
|
||||
cities.find((city) => city.id === common.city_id) ?? null
|
||||
}
|
||||
getOptionLabel={(option) => option.name}
|
||||
onChange={(_, value) => {
|
||||
setCity(value?.id ?? 0);
|
||||
handleChange(
|
||||
language as Language,
|
||||
{
|
||||
city_id: value?.id ?? 0,
|
||||
},
|
||||
true
|
||||
);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label="Город" />
|
||||
)}
|
||||
/>
|
||||
|
||||
<CoordinatesInput
|
||||
initialValue={coordinates}
|
||||
setValue={setCoordinates}
|
||||
<TextField
|
||||
label="Координаты"
|
||||
value={coordinates}
|
||||
onChange={(e) => {
|
||||
const input = e.target.value;
|
||||
setCoordinates(input); // показываем как есть
|
||||
|
||||
const [latStr, lonStr] = input.split(/\s+/); // учитываем любые пробелы
|
||||
|
||||
const lat = parseFloat(latStr);
|
||||
const lon = parseFloat(lonStr);
|
||||
|
||||
// Проверка, что обе координаты валидные числа
|
||||
const isValidLat = !isNaN(lat);
|
||||
const isValidLon = !isNaN(lon);
|
||||
|
||||
if (isValidLat && isValidLon) {
|
||||
handleChange(language as Language, {
|
||||
latitude: lat,
|
||||
longitude: lon,
|
||||
});
|
||||
} else {
|
||||
handleChange(language as Language, {
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
});
|
||||
}
|
||||
}}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Введите координаты в формате: широта долгота"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@ -222,11 +259,9 @@ export const InformationTab = observer(
|
||||
justifyContent: "center",
|
||||
borderRadius: 1,
|
||||
mb: 1,
|
||||
cursor: editSightStore.sightInfo?.thumbnail
|
||||
? "pointer"
|
||||
: "default", // Only clickable if there's an image
|
||||
cursor: common.thumbnail ? "pointer" : "default",
|
||||
"&:hover": {
|
||||
backgroundColor: editSightStore.sightInfo?.thumbnail
|
||||
backgroundColor: common.thumbnail
|
||||
? "red.300"
|
||||
: "grey.200",
|
||||
},
|
||||
@ -235,16 +270,16 @@ export const InformationTab = observer(
|
||||
setIsMediaModalOpen(true);
|
||||
}}
|
||||
>
|
||||
{editSightStore.sightInfo?.thumbnail ? (
|
||||
{common.thumbnail ? (
|
||||
<img
|
||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||
editSightStore.sightInfo?.thumbnail
|
||||
common.thumbnail
|
||||
}/download?token=${token}`}
|
||||
alt="Логотип"
|
||||
style={{ maxWidth: "100%", maxHeight: "100%" }}
|
||||
onClick={() => {
|
||||
setIsPreviewMediaOpen(true);
|
||||
setMediaId(editSightStore.sightInfo?.thumbnail);
|
||||
setMediaId(common.thumbnail);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@ -297,31 +332,28 @@ export const InformationTab = observer(
|
||||
justifyContent: "center",
|
||||
borderRadius: 1,
|
||||
mb: 1,
|
||||
cursor: editSightStore.sightInfo?.watermark_lu
|
||||
? "pointer"
|
||||
: "default", // Only clickable if there's an image
|
||||
cursor: common.watermark_lu ? "pointer" : "default",
|
||||
"&:hover": {
|
||||
backgroundColor: editSightStore.sightInfo
|
||||
?.watermark_lu
|
||||
backgroundColor: common.watermark_lu
|
||||
? "grey.300"
|
||||
: "grey.200",
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
setIsPreviewMediaOpen(true);
|
||||
setMediaId(editSightStore.sightInfo?.watermark_lu);
|
||||
setMediaId(common.watermark_lu);
|
||||
}}
|
||||
>
|
||||
{editSightStore.sightInfo?.watermark_lu ? (
|
||||
{common.watermark_lu ? (
|
||||
<img
|
||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||
editSightStore.sightInfo?.watermark_lu
|
||||
common.watermark_lu
|
||||
}/download?token=${token}`}
|
||||
alt="Знак л.в"
|
||||
style={{ maxWidth: "100%", maxHeight: "100%" }}
|
||||
onClick={() => {
|
||||
setIsMediaModalOpen(true);
|
||||
setMediaId(editSightStore.sightInfo?.watermark_lu);
|
||||
setMediaId(common.watermark_lu);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@ -375,28 +407,28 @@ export const InformationTab = observer(
|
||||
justifyContent: "center",
|
||||
borderRadius: 1,
|
||||
mb: 1,
|
||||
cursor: editSightStore.sightInfo?.watermark_rd
|
||||
? "pointer"
|
||||
: "default", // Only clickable if there's an image
|
||||
cursor: common.watermark_rd ? "pointer" : "default",
|
||||
"&:hover": {
|
||||
backgroundColor: editSightStore.sightInfo
|
||||
?.watermark_rd
|
||||
backgroundColor: common.watermark_rd
|
||||
? "grey.300"
|
||||
: "grey.200",
|
||||
},
|
||||
}}
|
||||
onClick={() => editSightStore.sightInfo?.watermark_rd}
|
||||
onClick={() => {
|
||||
setIsMediaModalOpen(true);
|
||||
setMediaId(common.watermark_rd);
|
||||
}}
|
||||
>
|
||||
{editSightStore.sightInfo?.watermark_rd ? (
|
||||
{common.watermark_rd ? (
|
||||
<img
|
||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||
editSightStore.sightInfo?.watermark_rd
|
||||
common.watermark_rd
|
||||
}/download?token=${token}`}
|
||||
alt="Знак п.в"
|
||||
style={{ maxWidth: "100%", maxHeight: "100%" }}
|
||||
onClick={() => {
|
||||
setIsPreviewMediaOpen(true);
|
||||
setMediaId(editSightStore.sightInfo?.watermark_rd);
|
||||
setMediaId(common.watermark_rd);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@ -432,7 +464,13 @@ export const InformationTab = observer(
|
||||
justifyContent: "flex-end", // Align to the right
|
||||
}}
|
||||
>
|
||||
<Button variant="contained" color="success" onClick={handleSave}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="success"
|
||||
onClick={() => {
|
||||
console.log(sight);
|
||||
}}
|
||||
>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Box>
|
||||
@ -463,7 +501,7 @@ export const InformationTab = observer(
|
||||
setIsAddMediaOpen(false);
|
||||
setActiveMenuType(null);
|
||||
}}
|
||||
onSelectArticle={handleMediaSelect}
|
||||
onSelectMedia={handleMediaSelect}
|
||||
/>
|
||||
|
||||
<PreviewMediaDialog
|
||||
|
Reference in New Issue
Block a user