feat: Add edit pages with cache

This commit is contained in:
2025-06-08 08:33:43 +03:00
parent e37f9e14bc
commit b09c1b3214
37 changed files with 2223 additions and 772 deletions

View File

@@ -0,0 +1,207 @@
import {
Button,
Paper,
TextField,
Select,
MenuItem,
FormControl,
InputLabel,
Box,
} from "@mui/material";
import { observer } from "mobx-react-lite";
import { ArrowLeft, Save, ImagePlus } from "lucide-react";
import { Loader2 } from "lucide-react";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import {
cityStore,
countryStore,
languageStore,
mediaStore,
CashedCities,
} from "@shared";
import { useEffect, useState } from "react";
import { LanguageSwitcher, MediaViewer } from "@widgets";
import { SelectMediaDialog } from "@shared";
export const CityEditPage = observer(() => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
const [isSelectMediaOpen, setIsSelectMediaOpen] = useState(false);
const { language } = languageStore;
const { id } = useParams();
const { editCityData, editCity, getCity, setEditCityData } = cityStore;
const { getCountries } = countryStore;
const { getMedia, getOneMedia, oneMedia } = mediaStore;
const handleEdit = async () => {
try {
setIsLoading(true);
await editCity(id as string);
toast.success("Город успешно обновлен");
} catch (error) {
toast.error("Ошибка при обновлении города");
} finally {
setIsLoading(false);
}
};
useEffect(() => {
(async () => {
if (id) {
const data = await getCity(id as string, language);
setEditCityData(
data.name,
data.country,
data.country_code,
data.arms,
language
);
await getOneMedia(data.arms as string);
await getCountries(language);
await getMedia();
}
})();
}, [id, language]);
const handleMediaSelect = (media: {
id: string;
filename: string;
media_name?: string;
media_type: number;
}) => {
setEditCityData(
editCityData[language].name,
editCityData.country,
editCityData.country_code,
media.id,
language
);
};
const selectedMedia = editCityData.arms
? mediaStore.media.find((m) => m.id === editCityData.arms)
: null;
return (
<Paper className="w-full h-full p-3 flex flex-col gap-10">
<LanguageSwitcher />
<div className="flex items-center gap-4">
<button
className="flex items-center gap-2"
onClick={() => navigate("/city")}
>
<ArrowLeft size={20} />
Назад
</button>
</div>
<div className="flex flex-col gap-10 w-full items-end">
<TextField
fullWidth
label="Название"
value={editCityData[language].name}
required
onChange={(e) =>
setEditCityData(
e.target.value,
editCityData.country,
editCityData.country_code,
editCityData.arms,
language
)
}
/>
<FormControl fullWidth>
<InputLabel>Страна</InputLabel>
<Select
value={editCityData.country_code || ""}
label="Страна"
required
onChange={(e) => {
const selectedCountry = countryStore.countries[language]?.find(
(country) => country.code === e.target.value
);
setEditCityData(
editCityData[language as keyof CashedCities]?.name || "",
selectedCountry?.name || "",
e.target.value,
editCityData.arms,
language
);
}}
>
{countryStore.countries[language].map((country) => (
<MenuItem key={country.code} value={country.code}>
{country.name}
</MenuItem>
))}
</Select>
</FormControl>
<div className="w-full flex flex-col gap-4">
<label className="text-sm text-gray-600">Герб города</label>
<div className="flex items-center gap-4">
<Button
variant="outlined"
onClick={() => setIsSelectMediaOpen(true)}
startIcon={<ImagePlus size={20} />}
>
Выбрать герб
</Button>
{selectedMedia && (
<span className="text-sm text-gray-600">
{selectedMedia.media_name || selectedMedia.filename}
</span>
)}
</div>
{selectedMedia && (
<Box
sx={{
width: "200px",
height: "200px",
border: "1px solid #e0e0e0",
borderRadius: "8px",
overflow: "hidden",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<MediaViewer
media={{
id: selectedMedia.id,
media_type: selectedMedia.media_type,
filename: selectedMedia.filename,
}}
/>
</Box>
)}
</div>
<Button
variant="contained"
className="w-min flex gap-2 items-center"
startIcon={<Save size={20} />}
onClick={handleEdit}
disabled={
isLoading || !editCityData[language as keyof CashedCities]?.name
}
>
{isLoading ? (
<Loader2 size={20} className="animate-spin" />
) : (
"Обновить"
)}
</Button>
</div>
<SelectMediaDialog
open={isSelectMediaOpen}
onClose={() => setIsSelectMediaOpen(false)}
onSelectMedia={handleMediaSelect}
/>
</Paper>
);
});