#14 Перепись редактирования и создания маршрута #16
@@ -13,7 +13,7 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { MediaViewer } from "@widgets";
|
import { MediaViewer, VideoPreviewCard } from "@widgets";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { ArrowLeft, Loader2, Save, Plus } from "lucide-react";
|
import { ArrowLeft, Loader2, Save, Plus } from "lucide-react";
|
||||||
import { useEffect, useState, useMemo } from "react";
|
import { useEffect, useState, useMemo } from "react";
|
||||||
@@ -24,9 +24,10 @@ import { articlesStore } from "../../../shared/store/ArticlesStore";
|
|||||||
import { Route, routeStore } from "../../../shared/store/RouteStore";
|
import { Route, routeStore } from "../../../shared/store/RouteStore";
|
||||||
import {
|
import {
|
||||||
languageStore,
|
languageStore,
|
||||||
SelectArticleModal,
|
ArticleSelectOrCreateDialog,
|
||||||
SelectMediaDialog,
|
SelectMediaDialog,
|
||||||
selectedCityStore,
|
selectedCityStore,
|
||||||
|
UploadMediaDialog,
|
||||||
} from "@shared";
|
} from "@shared";
|
||||||
|
|
||||||
export const RouteCreatePage = observer(() => {
|
export const RouteCreatePage = observer(() => {
|
||||||
@@ -39,6 +40,7 @@ export const RouteCreatePage = observer(() => {
|
|||||||
const [direction, setDirection] = useState("backward");
|
const [direction, setDirection] = useState("backward");
|
||||||
const [scaleMin, setScaleMin] = useState("");
|
const [scaleMin, setScaleMin] = useState("");
|
||||||
const [scaleMax, setScaleMax] = useState("");
|
const [scaleMax, setScaleMax] = useState("");
|
||||||
|
const [routeName, setRouteName] = useState("");
|
||||||
const [turn, setTurn] = useState("");
|
const [turn, setTurn] = useState("");
|
||||||
const [centerLat, setCenterLat] = useState("");
|
const [centerLat, setCenterLat] = useState("");
|
||||||
const [centerLng, setCenterLng] = useState("");
|
const [centerLng, setCenterLng] = useState("");
|
||||||
@@ -48,6 +50,8 @@ export const RouteCreatePage = observer(() => {
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [isSelectVideoDialogOpen, setIsSelectVideoDialogOpen] = useState(false);
|
const [isSelectVideoDialogOpen, setIsSelectVideoDialogOpen] = useState(false);
|
||||||
const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false);
|
const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false);
|
||||||
|
const [isUploadVideoDialogOpen, setIsUploadVideoDialogOpen] = useState(false);
|
||||||
|
const [fileToUpload, setFileToUpload] = useState<File | null>(null);
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -110,6 +114,8 @@ export const RouteCreatePage = observer(() => {
|
|||||||
const handleArticleSelect = (articleId: number) => {
|
const handleArticleSelect = (articleId: number) => {
|
||||||
setGovernorAppeal(articleId.toString());
|
setGovernorAppeal(articleId.toString());
|
||||||
setIsSelectArticleDialogOpen(false);
|
setIsSelectArticleDialogOpen(false);
|
||||||
|
// Обновляем список статей после создания новой
|
||||||
|
articlesStore.getArticleList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleVideoSelect = (media: {
|
const handleVideoSelect = (media: {
|
||||||
@@ -122,6 +128,26 @@ export const RouteCreatePage = observer(() => {
|
|||||||
setIsSelectVideoDialogOpen(false);
|
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 handleVideoPreviewClick = () => {
|
const handleVideoPreviewClick = () => {
|
||||||
setIsVideoPreviewOpen(true);
|
setIsVideoPreviewOpen(true);
|
||||||
};
|
};
|
||||||
@@ -167,6 +193,7 @@ export const RouteCreatePage = observer(() => {
|
|||||||
route_number: routeNumber,
|
route_number: routeNumber,
|
||||||
route_sys_number: govRouteNumber,
|
route_sys_number: govRouteNumber,
|
||||||
governor_appeal,
|
governor_appeal,
|
||||||
|
route_name: routeName,
|
||||||
route_direction,
|
route_direction,
|
||||||
scale_min,
|
scale_min,
|
||||||
scale_max,
|
scale_max,
|
||||||
@@ -208,6 +235,13 @@ export const RouteCreatePage = observer(() => {
|
|||||||
|
|
||||||
<div className="flex flex-col gap-10 w-full items-end">
|
<div className="flex flex-col gap-10 w-full items-end">
|
||||||
<Box className="flex flex-col gap-6 w-full">
|
<Box className="flex flex-col gap-6 w-full">
|
||||||
|
<TextField
|
||||||
|
className="w-full"
|
||||||
|
label="Название маршрута"
|
||||||
|
required
|
||||||
|
value={routeName}
|
||||||
|
onChange={(e) => setRouteName(e.target.value)}
|
||||||
|
/>
|
||||||
<FormControl fullWidth required>
|
<FormControl fullWidth required>
|
||||||
<InputLabel>Выберите перевозчика</InputLabel>
|
<InputLabel>Выберите перевозчика</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
@@ -279,6 +313,7 @@ export const RouteCreatePage = observer(() => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
className="w-full"
|
className="w-full"
|
||||||
label="Номер маршрута в Говорящем Городе"
|
label="Номер маршрута в Говорящем Городе"
|
||||||
@@ -287,17 +322,16 @@ export const RouteCreatePage = observer(() => {
|
|||||||
onChange={(e) => setGovRouteNumber(e.target.value)}
|
onChange={(e) => setGovRouteNumber(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Заменяем Select на кнопку для выбора статьи */}
|
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||||
<Box className="flex flex-col gap-2">
|
|
||||||
<label className="text-sm font-medium text-gray-700">
|
|
||||||
Обращение к пассажирам
|
Обращение к пассажирам
|
||||||
</label>
|
</Typography>
|
||||||
<Box className="flex gap-2">
|
<Box className="flex gap-2">
|
||||||
<TextField
|
<TextField
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
value={selectedArticle?.heading || "Статья не выбрана"}
|
value={selectedArticle?.heading || "Статья не выбрана"}
|
||||||
placeholder="Выберите статью"
|
placeholder="Выберите статью"
|
||||||
disabled
|
disabled
|
||||||
|
fullWidth
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiInputBase-input": {
|
"& .MuiInputBase-input": {
|
||||||
color: selectedArticle ? "inherit" : "#999",
|
color: selectedArticle ? "inherit" : "#999",
|
||||||
@@ -313,73 +347,18 @@ export const RouteCreatePage = observer(() => {
|
|||||||
Выбрать
|
Выбрать
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Селектор видеозаставки */}
|
{/* Видео-превью как на странице редактирования */}
|
||||||
<Box className="flex flex-col gap-2">
|
<VideoPreviewCard
|
||||||
<label className="text-sm font-medium text-gray-700">
|
title="Видеозаставка"
|
||||||
Видеозаставка
|
videoId={videoPreview}
|
||||||
</label>
|
onVideoClick={handleVideoPreviewClick}
|
||||||
<Box className="flex gap-2">
|
onDeleteVideoClick={() => {
|
||||||
<Box
|
|
||||||
className="flex-1"
|
|
||||||
onClick={handleVideoPreviewClick}
|
|
||||||
sx={{
|
|
||||||
cursor:
|
|
||||||
videoPreview && videoPreview !== "" ? "pointer" : "default",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
className="w-full h-[50px] border border-gray-400 rounded-sm flex items-center justify-between px-4"
|
|
||||||
sx={{
|
|
||||||
"& .MuiInputBase-input": {
|
|
||||||
color:
|
|
||||||
videoPreview && videoPreview !== ""
|
|
||||||
? "inherit"
|
|
||||||
: "#999",
|
|
||||||
cursor:
|
|
||||||
videoPreview && videoPreview !== ""
|
|
||||||
? "pointer"
|
|
||||||
: "default",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body1" className="text-sm">
|
|
||||||
{videoPreview && videoPreview !== ""
|
|
||||||
? "Видео выбрано"
|
|
||||||
: "Видео не выбрано"}
|
|
||||||
</Typography>
|
|
||||||
{videoPreview && videoPreview !== "" && (
|
|
||||||
<Box
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setVideoPreview("");
|
setVideoPreview("");
|
||||||
}}
|
}}
|
||||||
sx={{
|
onSelectVideoClick={handleVideoFileSelect}
|
||||||
cursor: "pointer",
|
className="w-full"
|
||||||
color: "#999",
|
/>
|
||||||
"&:hover": {
|
|
||||||
color: "#666",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body1" className="text-lg font-bold">
|
|
||||||
×
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setIsSelectVideoDialogOpen(true)}
|
|
||||||
startIcon={<Plus size={16} />}
|
|
||||||
sx={{ minWidth: "auto", px: 2 }}
|
|
||||||
>
|
|
||||||
Выбрать
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<FormControl fullWidth required>
|
<FormControl fullWidth required>
|
||||||
<InputLabel>Прямой/обратный маршрут</InputLabel>
|
<InputLabel>Прямой/обратный маршрут</InputLabel>
|
||||||
@@ -404,6 +383,7 @@ export const RouteCreatePage = observer(() => {
|
|||||||
value={scaleMax}
|
value={scaleMax}
|
||||||
onChange={(e) => setScaleMax(e.target.value)}
|
onChange={(e) => setScaleMax(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
className="w-full"
|
className="w-full"
|
||||||
label="Поворот"
|
label="Поворот"
|
||||||
@@ -441,8 +421,8 @@ export const RouteCreatePage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Модальное окно выбора статьи */}
|
{/* Модальное окно выбора или создания статьи */}
|
||||||
<SelectArticleModal
|
<ArticleSelectOrCreateDialog
|
||||||
open={isSelectArticleDialogOpen}
|
open={isSelectArticleDialogOpen}
|
||||||
onClose={() => setIsSelectArticleDialogOpen(false)}
|
onClose={() => setIsSelectArticleDialogOpen(false)}
|
||||||
onSelectArticle={handleArticleSelect}
|
onSelectArticle={handleArticleSelect}
|
||||||
@@ -483,6 +463,20 @@ export const RouteCreatePage = observer(() => {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Модальное окно загрузки видео */}
|
||||||
|
<UploadMediaDialog
|
||||||
|
open={isUploadVideoDialogOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsUploadVideoDialogOpen(false);
|
||||||
|
setFileToUpload(null);
|
||||||
|
}}
|
||||||
|
hardcodeType="video_preview"
|
||||||
|
contextObjectName={routeName || "Маршрут"}
|
||||||
|
contextType="sight"
|
||||||
|
initialFile={fileToUpload || undefined}
|
||||||
|
afterUpload={handleVideoUpload}
|
||||||
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { MediaViewer } from "@widgets";
|
import { MediaViewer, VideoPreviewCard } from "@widgets";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { ArrowLeft, Copy, Save, Plus } from "lucide-react";
|
import { ArrowLeft, Copy, Save, Plus } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -24,8 +24,9 @@ import { articlesStore } from "../../../shared/store/ArticlesStore";
|
|||||||
import {
|
import {
|
||||||
routeStore,
|
routeStore,
|
||||||
languageStore,
|
languageStore,
|
||||||
SelectArticleModal,
|
ArticleSelectOrCreateDialog,
|
||||||
SelectMediaDialog,
|
SelectMediaDialog,
|
||||||
|
UploadMediaDialog,
|
||||||
} from "@shared";
|
} from "@shared";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { stationsStore } from "@shared";
|
import { stationsStore } from "@shared";
|
||||||
@@ -40,6 +41,8 @@ export const RouteEditPage = observer(() => {
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [isSelectVideoDialogOpen, setIsSelectVideoDialogOpen] = useState(false);
|
const [isSelectVideoDialogOpen, setIsSelectVideoDialogOpen] = useState(false);
|
||||||
const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false);
|
const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false);
|
||||||
|
const [isUploadVideoDialogOpen, setIsUploadVideoDialogOpen] = useState(false);
|
||||||
|
const [fileToUpload, setFileToUpload] = useState<File | null>(null);
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
const [coordinates, setCoordinates] = useState<string>("");
|
const [coordinates, setCoordinates] = useState<string>("");
|
||||||
|
|
||||||
@@ -125,6 +128,8 @@ export const RouteEditPage = observer(() => {
|
|||||||
governor_appeal: articleId,
|
governor_appeal: articleId,
|
||||||
});
|
});
|
||||||
setIsSelectArticleDialogOpen(false);
|
setIsSelectArticleDialogOpen(false);
|
||||||
|
// Обновляем список статей после создания новой
|
||||||
|
articlesStore.getArticleList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleVideoSelect = (media: {
|
const handleVideoSelect = (media: {
|
||||||
@@ -139,6 +144,28 @@ export const RouteEditPage = observer(() => {
|
|||||||
setIsSelectVideoDialogOpen(false);
|
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 = () => {
|
const handleVideoPreviewClick = () => {
|
||||||
if (editRouteData.video_preview && editRouteData.video_preview !== "") {
|
if (editRouteData.video_preview && editRouteData.video_preview !== "") {
|
||||||
setIsVideoPreviewOpen(true);
|
setIsVideoPreviewOpen(true);
|
||||||
@@ -164,6 +191,17 @@ export const RouteEditPage = observer(() => {
|
|||||||
|
|
||||||
<div className="flex flex-col gap-10 w-full items-end">
|
<div className="flex flex-col gap-10 w-full items-end">
|
||||||
<Box className="flex flex-col gap-6 w-full">
|
<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>
|
<FormControl fullWidth required>
|
||||||
<InputLabel>Выберите перевозчика</InputLabel>
|
<InputLabel>Выберите перевозчика</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
@@ -279,110 +317,6 @@ export const RouteEditPage = observer(() => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Заменяем Select на кнопку для выбора статьи */}
|
|
||||||
<Box className="flex flex-col gap-2">
|
|
||||||
<label className="text-sm font-medium text-gray-700">
|
|
||||||
Обращение к пассажирам
|
|
||||||
</label>
|
|
||||||
<Box className="flex gap-2">
|
|
||||||
<TextField
|
|
||||||
className="flex-1"
|
|
||||||
value={selectedArticle?.heading || "Статья не выбрана"}
|
|
||||||
placeholder="Выберите статью"
|
|
||||||
disabled
|
|
||||||
sx={{
|
|
||||||
"& .MuiInputBase-input": {
|
|
||||||
color: selectedArticle ? "inherit" : "#999",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setIsSelectArticleDialogOpen(true)}
|
|
||||||
startIcon={<Plus size={16} />}
|
|
||||||
sx={{ minWidth: "auto", px: 2 }}
|
|
||||||
>
|
|
||||||
Выбрать
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Селектор видеозаставки */}
|
|
||||||
<Box className="flex flex-col gap-2">
|
|
||||||
<label className="text-sm font-medium text-gray-700">
|
|
||||||
Видеозаставка
|
|
||||||
</label>
|
|
||||||
<Box className="flex gap-2">
|
|
||||||
<Box
|
|
||||||
className="flex-1"
|
|
||||||
onClick={handleVideoPreviewClick}
|
|
||||||
sx={{
|
|
||||||
cursor:
|
|
||||||
editRouteData.video_preview &&
|
|
||||||
editRouteData.video_preview !== ""
|
|
||||||
? "pointer"
|
|
||||||
: "default",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
className="w-full h-[50px] border border-gray-400 rounded-sm flex items-center justify-between px-4"
|
|
||||||
sx={{
|
|
||||||
"& .MuiInputBase-input": {
|
|
||||||
color:
|
|
||||||
editRouteData.video_preview &&
|
|
||||||
editRouteData.video_preview !== ""
|
|
||||||
? "inherit"
|
|
||||||
: "#999",
|
|
||||||
cursor:
|
|
||||||
editRouteData.video_preview &&
|
|
||||||
editRouteData.video_preview !== ""
|
|
||||||
? "pointer"
|
|
||||||
: "default",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography variant="body1" className="text-sm">
|
|
||||||
{editRouteData.video_preview &&
|
|
||||||
editRouteData.video_preview !== ""
|
|
||||||
? "Видео выбрано"
|
|
||||||
: "Видео не выбрано"}
|
|
||||||
</Typography>
|
|
||||||
{editRouteData.video_preview &&
|
|
||||||
editRouteData.video_preview !== "" && (
|
|
||||||
<Box
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
routeStore.setEditRouteData({ video_preview: "" });
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
color: "#999",
|
|
||||||
"&:hover": {
|
|
||||||
color: "#666",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
variant="body1"
|
|
||||||
className="text-lg font-bold"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setIsSelectVideoDialogOpen(true)}
|
|
||||||
startIcon={<Plus size={16} />}
|
|
||||||
sx={{ minWidth: "auto", px: 2 }}
|
|
||||||
>
|
|
||||||
Выбрать
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<FormControl fullWidth required>
|
<FormControl fullWidth required>
|
||||||
<InputLabel>Прямой/обратный маршрут</InputLabel>
|
<InputLabel>Прямой/обратный маршрут</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
@@ -453,6 +387,45 @@ export const RouteEditPage = observer(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<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",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setIsSelectArticleDialogOpen(true)}
|
||||||
|
startIcon={<Plus size={16} />}
|
||||||
|
sx={{ minWidth: "auto", px: 2 }}
|
||||||
|
>
|
||||||
|
Выбрать
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Правая часть - Видео (30%) */}
|
||||||
|
|
||||||
|
<VideoPreviewCard
|
||||||
|
title="Видеозаставка"
|
||||||
|
videoId={editRouteData.video_preview}
|
||||||
|
onVideoClick={handleVideoPreviewClick}
|
||||||
|
onDeleteVideoClick={() => {
|
||||||
|
routeStore.setEditRouteData({ video_preview: "" });
|
||||||
|
}}
|
||||||
|
onSelectVideoClick={handleVideoFileSelect}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<LinkedItems
|
<LinkedItems
|
||||||
@@ -494,8 +467,8 @@ export const RouteEditPage = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Модальное окно выбора статьи */}
|
{/* Модальное окно выбора или создания статьи */}
|
||||||
<SelectArticleModal
|
<ArticleSelectOrCreateDialog
|
||||||
open={isSelectArticleDialogOpen}
|
open={isSelectArticleDialogOpen}
|
||||||
onClose={() => setIsSelectArticleDialogOpen(false)}
|
onClose={() => setIsSelectArticleDialogOpen(false)}
|
||||||
onSelectArticle={handleArticleSelect}
|
onSelectArticle={handleArticleSelect}
|
||||||
@@ -519,6 +492,7 @@ export const RouteEditPage = observer(() => {
|
|||||||
<DialogTitle>Предпросмотр видео</DialogTitle>
|
<DialogTitle>Предпросмотр видео</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Box className="flex justify-center items-center p-4">
|
<Box className="flex justify-center items-center p-4">
|
||||||
|
{editRouteData.video_preview && (
|
||||||
<MediaViewer
|
<MediaViewer
|
||||||
media={{
|
media={{
|
||||||
id: editRouteData.video_preview,
|
id: editRouteData.video_preview,
|
||||||
@@ -526,12 +500,27 @@ export const RouteEditPage = observer(() => {
|
|||||||
filename: "video_preview",
|
filename: "video_preview",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={() => setIsVideoPreviewOpen(false)}>Закрыть</Button>
|
<Button onClick={() => setIsVideoPreviewOpen(false)}>Закрыть</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{/* Модальное окно загрузки видео */}
|
||||||
|
<UploadMediaDialog
|
||||||
|
open={isUploadVideoDialogOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsUploadVideoDialogOpen(false);
|
||||||
|
setFileToUpload(null);
|
||||||
|
}}
|
||||||
|
hardcodeType="video_preview"
|
||||||
|
contextObjectName={editRouteData.route_name || "Маршрут"}
|
||||||
|
contextType="sight"
|
||||||
|
initialFile={fileToUpload || undefined}
|
||||||
|
afterUpload={handleVideoUpload}
|
||||||
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,6 +50,22 @@ export const RouteListPage = observer(() => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: "route_name",
|
||||||
|
headerName: "Название маршрута",
|
||||||
|
flex: 1,
|
||||||
|
renderCell: (params: GridRenderCellParams) => {
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full flex items-center">
|
||||||
|
{params.value ? (
|
||||||
|
params.value
|
||||||
|
) : (
|
||||||
|
<Minus size={20} className="text-red-500" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: "route_number",
|
field: "route_number",
|
||||||
headerName: "Номер маршрута",
|
headerName: "Номер маршрута",
|
||||||
@@ -122,6 +138,7 @@ export const RouteListPage = observer(() => {
|
|||||||
carrier_id: route.carrier_id,
|
carrier_id: route.carrier_id,
|
||||||
route_number: route.route_number,
|
route_number: route.route_number,
|
||||||
route_direction: route.route_direction ? "Прямой" : "Обратный",
|
route_direction: route.route_direction ? "Прямой" : "Обратный",
|
||||||
|
route_name: route.route_name,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
1083
src/shared/modals/ArticleSelectOrCreateDialog/index.tsx
Normal file
1083
src/shared/modals/ArticleSelectOrCreateDialog/index.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,3 +2,4 @@ export * from "./SelectArticleDialog";
|
|||||||
export * from "./SelectMediaDialog";
|
export * from "./SelectMediaDialog";
|
||||||
export * from "./PreviewMediaDialog";
|
export * from "./PreviewMediaDialog";
|
||||||
export * from "./UploadMediaDialog";
|
export * from "./UploadMediaDialog";
|
||||||
|
export * from "./ArticleSelectOrCreateDialog";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { makeAutoObservable, runInAction } from "mobx";
|
|||||||
import { authInstance } from "@shared";
|
import { authInstance } from "@shared";
|
||||||
|
|
||||||
export type Route = {
|
export type Route = {
|
||||||
|
route_name: string;
|
||||||
carrier: string;
|
carrier: string;
|
||||||
carrier_id: number;
|
carrier_id: number;
|
||||||
center_latitude: number;
|
center_latitude: number;
|
||||||
@@ -97,6 +98,7 @@ class RouteStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
editRouteData = {
|
editRouteData = {
|
||||||
|
route_name: "",
|
||||||
carrier: "",
|
carrier: "",
|
||||||
carrier_id: 0,
|
carrier_id: 0,
|
||||||
center_latitude: "",
|
center_latitude: "",
|
||||||
@@ -110,7 +112,7 @@ class RouteStore {
|
|||||||
route_sys_number: "",
|
route_sys_number: "",
|
||||||
scale_max: 0,
|
scale_max: 0,
|
||||||
scale_min: 0,
|
scale_min: 0,
|
||||||
video_preview: "",
|
video_preview: "" as string | undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
setEditRouteData = (data: any) => {
|
setEditRouteData = (data: any) => {
|
||||||
@@ -118,6 +120,9 @@ class RouteStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
editRoute = async (id: number) => {
|
editRoute = async (id: number) => {
|
||||||
|
if (!this.editRouteData.video_preview) {
|
||||||
|
delete this.editRouteData.video_preview;
|
||||||
|
}
|
||||||
const response = await authInstance.patch(`/route/${id}`, {
|
const response = await authInstance.patch(`/route/${id}`, {
|
||||||
...this.editRouteData,
|
...this.editRouteData,
|
||||||
center_latitude: parseFloat(this.editRouteData.center_latitude),
|
center_latitude: parseFloat(this.editRouteData.center_latitude),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface VideoPreviewCardProps {
|
|||||||
onDeleteVideoClick: () => void;
|
onDeleteVideoClick: () => void;
|
||||||
onSelectVideoClick: (file?: File) => void;
|
onSelectVideoClick: (file?: File) => void;
|
||||||
tooltipText?: string;
|
tooltipText?: string;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VideoPreviewCard: React.FC<VideoPreviewCardProps> = ({
|
export const VideoPreviewCard: React.FC<VideoPreviewCardProps> = ({
|
||||||
@@ -20,6 +21,7 @@ export const VideoPreviewCard: React.FC<VideoPreviewCardProps> = ({
|
|||||||
onDeleteVideoClick,
|
onDeleteVideoClick,
|
||||||
onSelectVideoClick,
|
onSelectVideoClick,
|
||||||
tooltipText,
|
tooltipText,
|
||||||
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const [isDragOver, setIsDragOver] = useState(false);
|
const [isDragOver, setIsDragOver] = useState(false);
|
||||||
@@ -89,7 +91,10 @@ export const VideoPreviewCard: React.FC<VideoPreviewCardProps> = ({
|
|||||||
gap: 1,
|
gap: 1,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
|
width: "min-content",
|
||||||
|
mx: "auto",
|
||||||
}}
|
}}
|
||||||
|
className={className}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
<Typography variant="subtitle2" gutterBottom sx={{ mb: 0, mr: 0.5 }}>
|
<Typography variant="subtitle2" gutterBottom sx={{ mb: 0, mr: 0.5 }}>
|
||||||
@@ -127,7 +132,10 @@ export const VideoPreviewCard: React.FC<VideoPreviewCardProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{videoId ? (
|
{videoId ? (
|
||||||
<Box sx={{ position: "relative", width: "100%", height: "100%" }}>
|
<Box
|
||||||
|
sx={{ position: "relative", width: "100%", height: "100%" }}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
<video
|
<video
|
||||||
src={`${
|
src={`${
|
||||||
import.meta.env.VITE_KRBL_MEDIA
|
import.meta.env.VITE_KRBL_MEDIA
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user