783 lines
25 KiB
TypeScript
783 lines
25 KiB
TypeScript
import {
|
||
Button,
|
||
TextField,
|
||
Select,
|
||
MenuItem,
|
||
FormControl,
|
||
InputLabel,
|
||
Typography,
|
||
Box,
|
||
Dialog,
|
||
DialogTitle,
|
||
DialogContent,
|
||
DialogActions,
|
||
} from "@mui/material";
|
||
import {
|
||
MediaViewer,
|
||
VideoPreviewCard,
|
||
ImageUploadCard,
|
||
DeleteModal,
|
||
} from "@widgets";
|
||
import { observer } from "mobx-react-lite";
|
||
import { ArrowLeft, Copy, Save, Plus, X } from "lucide-react";
|
||
import { useEffect, useState } from "react";
|
||
import { useNavigate, useParams } from "react-router-dom";
|
||
import { toast } from "react-toastify";
|
||
import {
|
||
carrierStore,
|
||
articlesStore,
|
||
routeStore,
|
||
languageStore,
|
||
mediaStore,
|
||
isMediaIdEmpty,
|
||
stationsStore,
|
||
ArticleSelectOrCreateDialog,
|
||
SelectMediaDialog,
|
||
UploadMediaDialog,
|
||
PreviewMediaDialog,
|
||
LoadingSpinner,
|
||
selectedCityStore,
|
||
} from "@shared";
|
||
import { LinkedItems } from "../LinekedStations";
|
||
|
||
export const RouteEditPage = observer(() => {
|
||
const navigate = useNavigate();
|
||
|
||
useEffect(() => {
|
||
selectedCityStore.setIsLocked(true);
|
||
return () => selectedCityStore.setIsLocked(false);
|
||
}, []);
|
||
|
||
const { id } = useParams();
|
||
const { editRouteData, copyRouteAction } = routeStore;
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
const [isLoadingData, setIsLoadingData] = useState(true);
|
||
const [isSelectArticleDialogOpen, setIsSelectArticleDialogOpen] =
|
||
useState(false);
|
||
const [isSelectVideoDialogOpen, setIsSelectVideoDialogOpen] = useState(false);
|
||
const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false);
|
||
const [isUploadVideoDialogOpen, setIsUploadVideoDialogOpen] = useState(false);
|
||
const [isSelectIconDialogOpen, setIsSelectIconDialogOpen] = useState(false);
|
||
const [isUploadIconDialogOpen, setIsUploadIconDialogOpen] = useState(false);
|
||
const [isPreviewIconOpen, setIsPreviewIconOpen] = useState(false);
|
||
const [isDeleteIconModalOpen, setIsDeleteIconModalOpen] = useState(false);
|
||
const [previewIconId, setPreviewIconId] = useState("");
|
||
const [activeIconMenuType, setActiveIconMenuType] = useState<
|
||
"thumbnail" | "watermark_lu" | "watermark_rd" | "image" | null
|
||
>(null);
|
||
const [fileToUpload, setFileToUpload] = useState<File | null>(null);
|
||
const { language } = languageStore;
|
||
const [coordinates, setCoordinates] = useState<string>("");
|
||
|
||
useEffect(() => {
|
||
const fetchData = async () => {
|
||
if (!id) {
|
||
setIsLoadingData(false);
|
||
return;
|
||
}
|
||
setIsLoadingData(true);
|
||
try {
|
||
const response = await routeStore.getRoute(Number(id));
|
||
routeStore.setEditRouteData(response);
|
||
languageStore.setLanguage("ru");
|
||
} finally {
|
||
setIsLoadingData(false);
|
||
}
|
||
};
|
||
fetchData();
|
||
}, [id]);
|
||
|
||
useEffect(() => {
|
||
const fetchData = async () => {
|
||
await carrierStore.getCarriers(language);
|
||
await stationsStore.getStations();
|
||
await articlesStore.getArticleList();
|
||
await mediaStore.getMedia();
|
||
};
|
||
fetchData();
|
||
}, [id, language]);
|
||
|
||
const handleIconSelect = (media: {
|
||
id: string;
|
||
filename: string;
|
||
media_name?: string;
|
||
media_type: number;
|
||
}) => {
|
||
routeStore.setEditRouteData({ icon: media.id });
|
||
setIsSelectIconDialogOpen(false);
|
||
};
|
||
|
||
const selectedIconMedia =
|
||
editRouteData.icon && !isMediaIdEmpty(editRouteData.icon)
|
||
? mediaStore.media.find((m) => m.id === editRouteData.icon)
|
||
: null;
|
||
const effectiveIconUrl = isMediaIdEmpty(editRouteData.icon)
|
||
? null
|
||
: (selectedIconMedia?.id ?? editRouteData.icon);
|
||
const effectiveVideoId = isMediaIdEmpty(editRouteData.video_preview)
|
||
? null
|
||
: editRouteData.video_preview;
|
||
|
||
useEffect(() => {
|
||
if (editRouteData.path && editRouteData.path.length > 0) {
|
||
const formattedPath = editRouteData.path
|
||
.map((coords) => coords.join(" "))
|
||
.join("\n");
|
||
setCoordinates(formattedPath);
|
||
}
|
||
}, [editRouteData.path]);
|
||
|
||
const handleSave = async () => {
|
||
// Валидация обязательных полей
|
||
if (!editRouteData.route_name?.trim()) {
|
||
toast.error("Заполните название маршрута");
|
||
return;
|
||
}
|
||
if (!editRouteData.carrier_id) {
|
||
toast.error("Выберите перевозчика");
|
||
return;
|
||
}
|
||
if (!editRouteData.route_number?.trim()) {
|
||
toast.error("Заполните номер маршрута");
|
||
return;
|
||
}
|
||
if (!editRouteData.route_sys_number?.trim()) {
|
||
toast.error("Заполните номер маршрута в Говорящем Городе");
|
||
return;
|
||
}
|
||
|
||
const validationResult = validateCoordinates(coordinates);
|
||
if (validationResult !== true) {
|
||
toast.error(validationResult);
|
||
return;
|
||
}
|
||
|
||
// Валидация масштабов
|
||
if (
|
||
editRouteData.scale_min !== null &&
|
||
editRouteData.scale_min !== undefined &&
|
||
editRouteData.scale_max !== null &&
|
||
editRouteData.scale_max !== undefined &&
|
||
editRouteData.scale_min > editRouteData.scale_max
|
||
) {
|
||
toast.error("Максимальный масштаб не может быть меньше минимального");
|
||
return;
|
||
}
|
||
|
||
if (
|
||
editRouteData.scale_min === 0 ||
|
||
editRouteData.scale_max === 0 ||
|
||
editRouteData.scale_min === null ||
|
||
editRouteData.scale_max === null
|
||
) {
|
||
toast.error("Масштабы не могут быть равны 0");
|
||
setIsLoading(false);
|
||
return;
|
||
}
|
||
|
||
setIsLoading(true);
|
||
try {
|
||
await routeStore.editRoute(Number(id));
|
||
toast.success("Маршрут успешно сохранен");
|
||
} catch (error) {
|
||
console.error(error);
|
||
toast.error("Произошла ошибка при сохранении маршрута");
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
const validateCoordinates = (value: string) => {
|
||
try {
|
||
const lines = value.trim().split("\n");
|
||
const coordinates = lines.map((line) => {
|
||
const [lat, lon] = line
|
||
.trim()
|
||
.split(/[\s,]+/)
|
||
.map(Number);
|
||
return [lat, lon];
|
||
});
|
||
|
||
if (coordinates.length === 0) {
|
||
return "Введите хотя бы одну пару координат";
|
||
}
|
||
|
||
if (
|
||
!coordinates.every(
|
||
(point) => Array.isArray(point) && point.length === 2
|
||
)
|
||
) {
|
||
return "Каждая строка должна содержать две координаты";
|
||
}
|
||
|
||
if (
|
||
!coordinates.every((point) =>
|
||
point.every((coord) => !isNaN(coord) && typeof coord === "number")
|
||
)
|
||
) {
|
||
return "Координаты должны быть числами";
|
||
}
|
||
|
||
return true;
|
||
} catch {
|
||
return "Неверный формат координат";
|
||
}
|
||
};
|
||
|
||
const handleCopy = async () => {
|
||
await copyRouteAction(Number(id));
|
||
toast.success("Маршрут успешно скопирован");
|
||
};
|
||
|
||
const handleArticleSelect = (articleId: number) => {
|
||
routeStore.setEditRouteData({
|
||
governor_appeal: articleId,
|
||
});
|
||
setIsSelectArticleDialogOpen(false);
|
||
// Обновляем список статей после создания новой
|
||
articlesStore.getArticleList();
|
||
};
|
||
|
||
const handleVideoSelect = (media: {
|
||
id: string;
|
||
filename: string;
|
||
media_name?: string;
|
||
media_type: number;
|
||
}) => {
|
||
routeStore.setEditRouteData({
|
||
video_preview: media.id,
|
||
});
|
||
setIsSelectVideoDialogOpen(false);
|
||
};
|
||
|
||
const handleVideoFileSelect = (file?: File) => {
|
||
if (file) {
|
||
setFileToUpload(file);
|
||
setIsUploadVideoDialogOpen(true);
|
||
} else {
|
||
setIsSelectVideoDialogOpen(true);
|
||
}
|
||
};
|
||
|
||
const handleVideoUpload = (media: {
|
||
id: string;
|
||
filename: string;
|
||
media_name?: string;
|
||
media_type: number;
|
||
}) => {
|
||
routeStore.setEditRouteData({
|
||
video_preview: media.id,
|
||
});
|
||
setIsUploadVideoDialogOpen(false);
|
||
setFileToUpload(null);
|
||
};
|
||
|
||
const handleVideoPreviewClick = () => {
|
||
if (editRouteData.video_preview && editRouteData.video_preview !== "") {
|
||
setIsVideoPreviewOpen(true);
|
||
}
|
||
};
|
||
|
||
// Получаем название выбранной статьи для отображения
|
||
const selectedArticle = articlesStore.articleList.ru.data.find(
|
||
(article) => article.id === editRouteData.governor_appeal
|
||
);
|
||
|
||
if (isLoadingData) {
|
||
return (
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
minHeight: "60vh",
|
||
}}
|
||
>
|
||
<LoadingSpinner message="Загрузка данных маршрута..." />
|
||
</Box>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="w-full h-full p-3 flex flex-col gap-10">
|
||
<div className="flex items-center gap-4">
|
||
<button
|
||
className="flex items-center gap-2"
|
||
onClick={() => navigate(-1)}
|
||
>
|
||
<ArrowLeft size={20} />
|
||
Назад
|
||
</button>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-10 w-full items-end">
|
||
<Box className="flex flex-col gap-6 w-full">
|
||
<TextField
|
||
className="w-full"
|
||
label="Название маршрута"
|
||
required
|
||
value={editRouteData.route_name || ""}
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
route_name: e.target.value,
|
||
})
|
||
}
|
||
/>
|
||
<FormControl fullWidth required>
|
||
<InputLabel>Выберите перевозчика</InputLabel>
|
||
<Select
|
||
value={editRouteData.carrier_id}
|
||
label="Выберите перевозчика"
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
carrier_id: Number(e.target.value),
|
||
carrier:
|
||
carrierStore.carriers[
|
||
language as keyof typeof carrierStore.carriers
|
||
].data?.find((c) => c.id === Number(e.target.value))
|
||
?.full_name || "",
|
||
})
|
||
}
|
||
disabled={
|
||
carrierStore.carriers[
|
||
language as keyof typeof carrierStore.carriers
|
||
].data?.length === 0
|
||
}
|
||
>
|
||
<MenuItem value="">Не выбрано</MenuItem>
|
||
{carrierStore.carriers[
|
||
language as keyof typeof carrierStore.carriers
|
||
].data?.map((carrier) => (
|
||
<MenuItem key={carrier.id} value={carrier.id}>
|
||
{carrier.full_name}
|
||
</MenuItem>
|
||
))}
|
||
</Select>
|
||
</FormControl>
|
||
<TextField
|
||
className="w-full"
|
||
label="Номер маршрута"
|
||
required
|
||
value={editRouteData.route_number || ""}
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
route_number: e.target.value,
|
||
})
|
||
}
|
||
/>
|
||
<TextField
|
||
className="w-full"
|
||
label="Координаты маршрута"
|
||
multiline
|
||
minRows={2}
|
||
maxRows={10}
|
||
value={coordinates}
|
||
onChange={(e) => {
|
||
const newValue = e.target.value;
|
||
setCoordinates(newValue);
|
||
|
||
const validationResult = validateCoordinates(newValue);
|
||
if (validationResult === true) {
|
||
const lines = newValue.trim().split("\n");
|
||
const path = lines.map((line) => {
|
||
const [lat, lon] = line
|
||
.trim()
|
||
.split(/[\s,]+/)
|
||
.map(Number);
|
||
return [lat, lon];
|
||
});
|
||
routeStore.setEditRouteData({ path });
|
||
}
|
||
}}
|
||
onKeyDown={(e) => {
|
||
if (e.key === "Enter") {
|
||
const lines = coordinates.split("\n");
|
||
const lastLine = lines[lines.length - 1];
|
||
|
||
if (lastLine && lastLine.trim()) {
|
||
e.preventDefault();
|
||
const newValue = coordinates + "\n";
|
||
setCoordinates(newValue);
|
||
}
|
||
}
|
||
}}
|
||
error={validateCoordinates(coordinates) !== true}
|
||
helperText={
|
||
typeof validateCoordinates(coordinates) === "string"
|
||
? validateCoordinates(coordinates)
|
||
: "Формат: широта долгота"
|
||
}
|
||
placeholder="55.7558 37.6173 55.7539 37.6208"
|
||
sx={{
|
||
mt: 1,
|
||
"& .MuiInputBase-root": {
|
||
maxHeight: "500px",
|
||
overflow: "auto",
|
||
},
|
||
"& .MuiInputBase-input": {
|
||
fontFamily: "monospace",
|
||
fontSize: "0.8rem",
|
||
lineHeight: "1.2",
|
||
},
|
||
"& .MuiFormHelperText-root": {
|
||
fontSize: "0.75rem",
|
||
marginTop: "2px",
|
||
},
|
||
}}
|
||
/>
|
||
<TextField
|
||
className="w-full"
|
||
label="Номер маршрута в Говорящем Городе"
|
||
required
|
||
value={editRouteData.route_sys_number || ""}
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
route_sys_number: e.target.value,
|
||
})
|
||
}
|
||
/>
|
||
|
||
<FormControl fullWidth required>
|
||
<InputLabel>Прямой/обратный маршрут</InputLabel>
|
||
<Select
|
||
value={editRouteData.route_direction ? "forward" : "backward"}
|
||
label="Прямой/обратный маршрут"
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
route_direction: e.target.value === "forward",
|
||
})
|
||
}
|
||
>
|
||
<MenuItem value="forward">Прямой</MenuItem>
|
||
<MenuItem value="backward">Обратный</MenuItem>
|
||
</Select>
|
||
</FormControl>
|
||
<TextField
|
||
className="w-full"
|
||
label="Масштаб (мин)"
|
||
type="number"
|
||
value={editRouteData.scale_min ?? ""}
|
||
onChange={(e) => {
|
||
let value = e.target.value === "" ? null : e.target.value;
|
||
|
||
if (value && Number(value) > 297) {
|
||
value = "297";
|
||
}
|
||
|
||
if (value && Number(value) < 10) {
|
||
value = "10";
|
||
}
|
||
|
||
routeStore.setEditRouteData({
|
||
scale_min: value ? Number(value) : null,
|
||
});
|
||
// Если максимальный масштаб стал меньше минимального, обновляем его
|
||
if (
|
||
value !== null &&
|
||
editRouteData.scale_max !== null &&
|
||
editRouteData.scale_max !== undefined &&
|
||
value &&
|
||
Number(value) > (editRouteData.scale_max ?? 0)
|
||
) {
|
||
routeStore.setEditRouteData({
|
||
scale_max: value ? Number(value) : null,
|
||
});
|
||
}
|
||
}}
|
||
required
|
||
/>
|
||
<TextField
|
||
className="w-full"
|
||
required
|
||
label="Масштаб (макс)"
|
||
type="number"
|
||
value={editRouteData.scale_max ?? ""}
|
||
onChange={(e) => {
|
||
let value = e.target.value;
|
||
|
||
if (Number(value) > 300) {
|
||
value = "300";
|
||
}
|
||
|
||
routeStore.setEditRouteData({
|
||
scale_max: value === "" ? null : parseFloat(value),
|
||
});
|
||
}}
|
||
error={
|
||
editRouteData.scale_min !== null &&
|
||
editRouteData.scale_min !== undefined &&
|
||
editRouteData.scale_max !== null &&
|
||
editRouteData.scale_max !== undefined &&
|
||
editRouteData.scale_max < editRouteData.scale_min
|
||
}
|
||
helperText={
|
||
editRouteData.scale_min !== null &&
|
||
editRouteData.scale_min !== undefined &&
|
||
editRouteData.scale_max !== null &&
|
||
editRouteData.scale_max !== undefined &&
|
||
editRouteData.scale_max < editRouteData.scale_min
|
||
? "Максимальный масштаб не может быть меньше минимального"
|
||
: ""
|
||
}
|
||
/>
|
||
<TextField
|
||
className="w-full"
|
||
label="Поворот"
|
||
value={editRouteData.rotate ?? ""}
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
rotate:
|
||
e.target.value === "" ? null : parseFloat(e.target.value),
|
||
})
|
||
}
|
||
/>
|
||
<TextField
|
||
className="w-full"
|
||
label="Центр. широта"
|
||
value={editRouteData.center_latitude ?? ""}
|
||
type="text"
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
center_latitude: e.target.value,
|
||
})
|
||
}
|
||
/>
|
||
<TextField
|
||
className="w-full"
|
||
label="Центр. долгота"
|
||
value={editRouteData.center_longitude ?? ""}
|
||
type="text"
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
center_longitude: e.target.value,
|
||
})
|
||
}
|
||
/>
|
||
<TextField
|
||
className="w-full"
|
||
label="Таймер видео заставки (сек)"
|
||
type="number"
|
||
value={editRouteData.video_timer ?? 420}
|
||
onChange={(e) => {
|
||
const val = Math.max(1, Math.round(Number(e.target.value)));
|
||
if (Number.isFinite(val)) {
|
||
routeStore.setEditRouteData({ video_timer: val });
|
||
}
|
||
}}
|
||
/>
|
||
|
||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||
Текст кнопки обращения
|
||
</Typography>
|
||
<TextField
|
||
value={editRouteData.button_text || ""}
|
||
onChange={(e) =>
|
||
routeStore.setEditRouteData({
|
||
button_text: e.target.value,
|
||
})
|
||
}
|
||
placeholder="Обращение губернатора"
|
||
fullWidth
|
||
size="small"
|
||
helperText="Если пусто, будет использован текст по умолчанию"
|
||
/>
|
||
|
||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||
Обращение к пассажирам
|
||
</Typography>
|
||
<Box className="flex gap-2">
|
||
<TextField
|
||
className="flex-1"
|
||
value={selectedArticle?.heading || "Статья не выбрана"}
|
||
placeholder="Выберите статью"
|
||
disabled
|
||
fullWidth
|
||
sx={{
|
||
"& .MuiInputBase-input": {
|
||
color: selectedArticle ? "inherit" : "#999",
|
||
},
|
||
}}
|
||
/>
|
||
{selectedArticle && (
|
||
<Button
|
||
variant="outlined"
|
||
color="error"
|
||
onClick={() =>
|
||
routeStore.setEditRouteData({
|
||
governor_appeal: 0,
|
||
})
|
||
}
|
||
startIcon={<X size={16} />}
|
||
sx={{ minWidth: "auto", px: 2 }}
|
||
>
|
||
Сбросить
|
||
</Button>
|
||
)}
|
||
<Button
|
||
variant="outlined"
|
||
onClick={() => setIsSelectArticleDialogOpen(true)}
|
||
startIcon={<Plus size={16} />}
|
||
sx={{ minWidth: "auto", px: 2 }}
|
||
>
|
||
Выбрать
|
||
</Button>
|
||
</Box>
|
||
|
||
<Box className="w-full flex justify-center gap-4 flex-wrap">
|
||
<div className="flex flex-col gap-4 max-w-[300px]">
|
||
<ImageUploadCard
|
||
title="Иконка маршрута"
|
||
imageKey="thumbnail"
|
||
imageUrl={effectiveIconUrl}
|
||
onImageClick={() => {
|
||
setIsPreviewIconOpen(true);
|
||
setPreviewIconId(
|
||
selectedIconMedia?.id ?? editRouteData.icon ?? ""
|
||
);
|
||
}}
|
||
onDeleteImageClick={() => setIsDeleteIconModalOpen(true)}
|
||
onSelectFileClick={() => {
|
||
setActiveIconMenuType("image");
|
||
setIsSelectIconDialogOpen(true);
|
||
}}
|
||
setUploadMediaOpen={() => {
|
||
setIsUploadIconDialogOpen(true);
|
||
setActiveIconMenuType("image");
|
||
}}
|
||
/>
|
||
</div>
|
||
<div className="flex flex-col gap-4 max-w-[300px]">
|
||
<VideoPreviewCard
|
||
title="Видеозаставка"
|
||
videoId={effectiveVideoId}
|
||
onVideoClick={handleVideoPreviewClick}
|
||
onDeleteVideoClick={() => {
|
||
routeStore.setEditRouteData({ video_preview: "" });
|
||
}}
|
||
onSelectVideoClick={handleVideoFileSelect}
|
||
className="w-full"
|
||
/>
|
||
</div>
|
||
</Box>
|
||
</Box>
|
||
|
||
<LinkedItems
|
||
parentId={id || ""}
|
||
type="edit"
|
||
dragAllowed={true}
|
||
fields={[
|
||
{ label: "Название", data: "name" },
|
||
{ label: "Описание", data: "description" },
|
||
]}
|
||
onUpdate={() => {
|
||
routeStore.getRoute(Number(id));
|
||
}}
|
||
/>
|
||
|
||
<div className="flex w-full justify-between">
|
||
<Button
|
||
variant="contained"
|
||
color="primary"
|
||
className="w-min flex gap-2 items-center"
|
||
startIcon={<Copy size={20} />}
|
||
onClick={handleCopy}
|
||
disabled={isLoading}
|
||
>
|
||
Скопировать
|
||
</Button>
|
||
|
||
<Button
|
||
variant="contained"
|
||
color="primary"
|
||
className="w-min flex gap-2 items-center"
|
||
startIcon={<Save size={20} />}
|
||
onClick={handleSave}
|
||
disabled={isLoading}
|
||
>
|
||
Сохранить
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
<ArticleSelectOrCreateDialog
|
||
open={isSelectArticleDialogOpen}
|
||
onClose={() => setIsSelectArticleDialogOpen(false)}
|
||
onSelectArticle={handleArticleSelect}
|
||
/>
|
||
<SelectMediaDialog
|
||
open={isSelectVideoDialogOpen}
|
||
onClose={() => setIsSelectVideoDialogOpen(false)}
|
||
onSelectMedia={handleVideoSelect}
|
||
mediaType={2}
|
||
/>
|
||
<Dialog
|
||
open={isVideoPreviewOpen}
|
||
onClose={() => setIsVideoPreviewOpen(false)}
|
||
maxWidth="md"
|
||
fullWidth
|
||
>
|
||
<DialogTitle>Предпросмотр видео</DialogTitle>
|
||
<DialogContent>
|
||
<Box className="flex justify-center items-center p-4">
|
||
{effectiveVideoId && (
|
||
<MediaViewer
|
||
media={{
|
||
id: effectiveVideoId,
|
||
media_type: 2,
|
||
filename: "video_preview",
|
||
}}
|
||
/>
|
||
)}
|
||
</Box>
|
||
</DialogContent>
|
||
<DialogActions>
|
||
<Button onClick={() => setIsVideoPreviewOpen(false)}>Закрыть</Button>
|
||
</DialogActions>
|
||
</Dialog>
|
||
<UploadMediaDialog
|
||
open={isUploadVideoDialogOpen}
|
||
onClose={() => {
|
||
setIsUploadVideoDialogOpen(false);
|
||
setFileToUpload(null);
|
||
}}
|
||
hardcodeType="video_preview"
|
||
contextObjectName={editRouteData.route_name || "Маршрут"}
|
||
contextType="sight"
|
||
initialFile={fileToUpload || undefined}
|
||
afterUpload={handleVideoUpload}
|
||
/>
|
||
|
||
<SelectMediaDialog
|
||
open={isSelectIconDialogOpen}
|
||
onClose={() => setIsSelectIconDialogOpen(false)}
|
||
onSelectMedia={handleIconSelect}
|
||
mediaType={1}
|
||
/>
|
||
|
||
<UploadMediaDialog
|
||
open={isUploadIconDialogOpen}
|
||
onClose={() => setIsUploadIconDialogOpen(false)}
|
||
contextObjectName={editRouteData.route_name || "Маршрут"}
|
||
contextType="route"
|
||
afterUpload={handleIconSelect}
|
||
hardcodeType={activeIconMenuType}
|
||
/>
|
||
|
||
<PreviewMediaDialog
|
||
open={isPreviewIconOpen}
|
||
onClose={() => setIsPreviewIconOpen(false)}
|
||
mediaId={previewIconId}
|
||
/>
|
||
|
||
<DeleteModal
|
||
open={isDeleteIconModalOpen}
|
||
onDelete={() => {
|
||
routeStore.setEditRouteData({ icon: "" });
|
||
setIsDeleteIconModalOpen(false);
|
||
}}
|
||
onCancel={() => setIsDeleteIconModalOpen(false)}
|
||
edit
|
||
/>
|
||
</div>
|
||
);
|
||
});
|