From 42f8019e27d5b110e241f5f55364d5702fdae486 Mon Sep 17 00:00:00 2001 From: itoshi Date: Mon, 28 Sep 2026 15:12:16 +0300 Subject: [PATCH] fix: restore route video preview --- src/client/src/api/ApiStore/types.ts | 1 + src/pages/Route/RouteCreatePage/index.tsx | 97 ++++++++++++++++++++++- src/pages/Route/RouteEditPage/index.tsx | 97 +++++++++++++++++++++++ src/shared/store/RouteStore/index.ts | 13 ++- 4 files changed, 203 insertions(+), 5 deletions(-) diff --git a/src/client/src/api/ApiStore/types.ts b/src/client/src/api/ApiStore/types.ts index 1b331e8..6c96efc 100644 --- a/src/client/src/api/ApiStore/types.ts +++ b/src/client/src/api/ApiStore/types.ts @@ -64,6 +64,7 @@ export type GetRouteResponse = { route_sys_number: string; scale_max: number; scale_min: number; + video_preview: string; video_timer?: number; icon?: string; icon_size?: number; diff --git a/src/pages/Route/RouteCreatePage/index.tsx b/src/pages/Route/RouteCreatePage/index.tsx index 92a666b..97e8b27 100644 --- a/src/pages/Route/RouteCreatePage/index.tsx +++ b/src/pages/Route/RouteCreatePage/index.tsx @@ -7,8 +7,12 @@ import { InputLabel, Typography, Box, + Dialog, + DialogTitle, + DialogContent, + DialogActions, } from "@mui/material"; -import { ImageUploadCard } from "@widgets"; +import { MediaViewer, VideoPreviewCard, ImageUploadCard } from "@widgets"; import { observer } from "mobx-react-lite"; import { ArrowLeft, Loader2, Save, Plus, X } from "lucide-react"; import { useEffect, useState, useMemo } from "react"; @@ -51,10 +55,14 @@ export const RouteCreatePage = observer(() => { const [centerLat, setCenterLat] = useState(""); const [centerLng, setCenterLng] = useState(""); const [videoTimer, setVideoTimer] = useState(420); + const [videoPreview, setVideoPreview] = useState(""); const [icon, setIcon] = useState(""); const [isLoading, setIsLoading] = useState(false); 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); @@ -62,6 +70,7 @@ export const RouteCreatePage = observer(() => { const [activeIconMenuType, setActiveIconMenuType] = useState< "thumbnail" | "watermark_lu" | "watermark_rd" | "image" | null >(null); + const [fileToUpload, setFileToUpload] = useState(null); const { language } = languageStore; useEffect(() => { @@ -137,11 +146,42 @@ export const RouteCreatePage = observer(() => { setIsSelectIconDialogOpen(false); }; + const handleVideoSelect = (media: { + id: string; + filename: string; + media_name?: string; + media_type: number; + }) => { + setVideoPreview(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; + }) => { + setVideoPreview(media.id); + setIsUploadVideoDialogOpen(false); + setFileToUpload(null); + }; + const selectedIconMedia = icon && !isMediaIdEmpty(icon) ? mediaStore.media.find((m) => m.id === icon) : null; const effectiveIconUrl = isMediaIdEmpty(icon) ? null : selectedIconMedia?.id ?? icon; + const effectiveVideoId = isMediaIdEmpty(videoPreview) ? null : videoPreview; const handleCreateRoute = async () => { try { @@ -236,6 +276,7 @@ export const RouteCreatePage = observer(() => { center_latitude, center_longitude, path, + video_preview: !isMediaIdEmpty(videoPreview) ? videoPreview : undefined, icon: !isMediaIdEmpty(icon) ? icon : undefined, video_timer: videoTimer, }; @@ -436,6 +477,16 @@ export const RouteCreatePage = observer(() => { }} /> +
+ setIsVideoPreviewOpen(true)} + onDeleteVideoClick={() => setVideoPreview("")} + onSelectVideoClick={handleVideoFileSelect} + className="w-full" + /> +
@@ -552,6 +603,50 @@ export const RouteCreatePage = observer(() => { onClose={() => setIsSelectArticleDialogOpen(false)} onSelectArticle={handleArticleSelect} /> + setIsSelectVideoDialogOpen(false)} + onSelectMedia={handleVideoSelect} + mediaType={2} + /> + {effectiveVideoId && ( + setIsVideoPreviewOpen(false)} + maxWidth="md" + fullWidth + > + Предпросмотр видео + + + + + + + + + + )} + { + setIsUploadVideoDialogOpen(false); + setFileToUpload(null); + }} + hardcodeType="video_preview" + contextObjectName={routeName || "Маршрут"} + contextType="route" + initialFile={fileToUpload || undefined} + afterUpload={handleVideoUpload} + /> setIsSelectIconDialogOpen(false)} diff --git a/src/pages/Route/RouteEditPage/index.tsx b/src/pages/Route/RouteEditPage/index.tsx index 1eca200..c8e9c10 100644 --- a/src/pages/Route/RouteEditPage/index.tsx +++ b/src/pages/Route/RouteEditPage/index.tsx @@ -7,8 +7,14 @@ import { InputLabel, Typography, Box, + Dialog, + DialogTitle, + DialogContent, + DialogActions, } from "@mui/material"; import { + MediaViewer, + VideoPreviewCard, ImageUploadCard, DeleteModal, } from "@widgets"; @@ -48,6 +54,9 @@ export const RouteEditPage = observer(() => { 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); @@ -56,6 +65,7 @@ export const RouteEditPage = observer(() => { const [activeIconMenuType, setActiveIconMenuType] = useState< "thumbnail" | "watermark_lu" | "watermark_rd" | "image" | null >(null); + const [fileToUpload, setFileToUpload] = useState(null); const { language } = languageStore; const [coordinates, setCoordinates] = useState(""); @@ -104,6 +114,9 @@ export const RouteEditPage = observer(() => { 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) { @@ -225,6 +238,36 @@ export const RouteEditPage = observer(() => { 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 selectedArticle = articlesStore.articleList.ru.data.find( (article) => article.id === editRouteData.governor_appeal @@ -593,6 +636,18 @@ export const RouteEditPage = observer(() => { }} /> +
+ setIsVideoPreviewOpen(true)} + onDeleteVideoClick={() => + routeStore.setEditRouteData({ video_preview: "" }) + } + onSelectVideoClick={handleVideoFileSelect} + className="w-full" + /> +
@@ -638,6 +693,48 @@ export const RouteEditPage = observer(() => { onClose={() => setIsSelectArticleDialogOpen(false)} onSelectArticle={handleArticleSelect} /> + setIsSelectVideoDialogOpen(false)} + onSelectMedia={handleVideoSelect} + mediaType={2} + /> + setIsVideoPreviewOpen(false)} + maxWidth="md" + fullWidth + > + Предпросмотр видео + + + {effectiveVideoId && ( + + )} + + + + + + + { + setIsUploadVideoDialogOpen(false); + setFileToUpload(null); + }} + hardcodeType="video_preview" + contextObjectName={editRouteData.route_name || "Маршрут"} + contextType="route" + initialFile={fileToUpload || undefined} + afterUpload={handleVideoUpload} + /> setIsSelectIconDialogOpen(false)} diff --git a/src/shared/store/RouteStore/index.ts b/src/shared/store/RouteStore/index.ts index dd56536..edb2bbb 100644 --- a/src/shared/store/RouteStore/index.ts +++ b/src/shared/store/RouteStore/index.ts @@ -23,6 +23,7 @@ export type Route = { route_sys_number: string; scale_max: number; scale_min: number; + video_preview: string; video_timer: number; }; @@ -153,16 +154,21 @@ class RouteStore { route_sys_number: "", scale_max: 0, scale_min: 0, + video_preview: "" as string | undefined, video_timer: 420, }; setEditRouteData = (data: any) => { - const routeData = { ...data }; - delete routeData.video_preview; - this.editRouteData = { ...this.editRouteData, ...routeData }; + this.editRouteData = { ...this.editRouteData, ...data }; }; editRoute = async (id: number) => { + if ( + !this.editRouteData.video_preview || + isMediaIdEmpty(this.editRouteData.video_preview) + ) { + delete this.editRouteData.video_preview; + } if (!this.editRouteData.icon || isMediaIdEmpty(this.editRouteData.icon)) { delete (this.editRouteData as any).icon; } @@ -174,7 +180,6 @@ class RouteStore { center_latitude: parseFloat(this.editRouteData.center_latitude), center_longitude: parseFloat(this.editRouteData.center_longitude), }; - delete dataToSend.video_preview; if ( this.editRouteData.governor_appeal === 0 || !this.editRouteData.governor_appeal