feature/webgl-preview-update #21
@@ -23,6 +23,7 @@ const App = observer(() => {
|
||||
loadingStatus,
|
||||
loadingProgress,
|
||||
context,
|
||||
carrier,
|
||||
setIsLoading,
|
||||
setLoadingStatus,
|
||||
setLoadingProgress,
|
||||
@@ -172,6 +173,7 @@ const App = observer(() => {
|
||||
loadingStatus={loadingStatus}
|
||||
loadingProgress={loadingProgress}
|
||||
onToggleDebug={() => setIsDebugVisible((prev) => !prev)}
|
||||
videoId={carrier?.video_preview}
|
||||
/>
|
||||
) : (
|
||||
<div>
|
||||
|
||||
@@ -64,7 +64,6 @@ export type GetRouteResponse = {
|
||||
route_sys_number: string;
|
||||
scale_max: number;
|
||||
scale_min: number;
|
||||
video_preview: string;
|
||||
video_timer?: number;
|
||||
icon?: string;
|
||||
icon_size?: number;
|
||||
@@ -77,6 +76,8 @@ export type GetCarrierResponse = {
|
||||
full_name: string;
|
||||
id: number;
|
||||
logo: string;
|
||||
preview_media?: string;
|
||||
video_preview?: string;
|
||||
short_name: string;
|
||||
slogan: string;
|
||||
main_color?: string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import "../styles/Loader.css";
|
||||
import { getMediaUrl } from "../api/apiConfig";
|
||||
|
||||
const loaderVideoPath = new URL("../assets/video/taganai.mp4", import.meta.url)
|
||||
.href;
|
||||
@@ -9,8 +10,14 @@ const Loader = ({
|
||||
loadingStatus = null,
|
||||
loadingProgress = null,
|
||||
onToggleDebug,
|
||||
videoId = null,
|
||||
}) => {
|
||||
const [showLoadingInfo, setShowLoadingInfo] = useState(false);
|
||||
const [isCustomVideoError, setIsCustomVideoError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsCustomVideoError(false);
|
||||
}, [videoId]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event) => {
|
||||
@@ -55,7 +62,12 @@ const Loader = ({
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
src={loaderVideoPath}
|
||||
src={
|
||||
videoId && !isCustomVideoError
|
||||
? getMediaUrl(videoId)
|
||||
: loaderVideoPath
|
||||
}
|
||||
onError={() => setIsCustomVideoError(true)}
|
||||
alt="Loading"
|
||||
className="error-gif"
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
|
||||
@@ -13,12 +13,10 @@ import StationsList from "./StationsList";
|
||||
import LeftWidget from "./LeftWidget";
|
||||
import { apiStore } from "../../api/ApiStore/store";
|
||||
import { getMediaUrl } from "../../api/apiConfig";
|
||||
import defaultCrest from "../../assets/images/Герб.png";
|
||||
|
||||
const SideMenu = observer(({ onMenuToggle }) => {
|
||||
const {
|
||||
carrier,
|
||||
city,
|
||||
route,
|
||||
getArticle,
|
||||
sightArticles,
|
||||
@@ -33,6 +31,7 @@ const SideMenu = observer(({ onMenuToggle }) => {
|
||||
const [isStationOpen, openStation] = useState(false);
|
||||
const [isWidgetOpen, openWidget] = useState(false);
|
||||
const [isWeatherVisible, setIsWeatherVisible] = useState(true);
|
||||
const [isPreviewMediaError, setIsPreviewMediaError] = useState(false);
|
||||
const [isLeftWidgetVisible, setIsLeftWidgetVisible] = useState(false);
|
||||
const [localSelectedSightId, setLocalSelectedSightId] = useState(null);
|
||||
const [isSightsAnimating, setIsSightsAnimating] = useState(false);
|
||||
@@ -186,27 +185,12 @@ const SideMenu = observer(({ onMenuToggle }) => {
|
||||
}
|
||||
}, [isLeftWidgetVisible]);
|
||||
|
||||
const [designData, setDesignData] = useState(null);
|
||||
const [routeData, setRouteData] = useState(null);
|
||||
const [isLangMenuOpen, setIsLangMenuOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchCarrierAndCrest() {
|
||||
try {
|
||||
const carrierPath = carrier.logo ? getMediaUrl(carrier.logo) : null;
|
||||
const cityPath = city.arms ? getMediaUrl(city.arms) : null;
|
||||
|
||||
const response = {
|
||||
carrierPath: carrierPath,
|
||||
creastPath: cityPath,
|
||||
};
|
||||
setDesignData(response);
|
||||
} catch (err) {
|
||||
console.error("Ошибка в получении данных герба и перевозчика:", err);
|
||||
}
|
||||
}
|
||||
fetchCarrierAndCrest();
|
||||
}, [contextData?.routeId, isGeolocationLoading, selectedLanguage]);
|
||||
setIsPreviewMediaError(false);
|
||||
}, [carrier?.preview_media]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isGeolocationLoading || !contextData?.routeId) {
|
||||
@@ -415,14 +399,6 @@ const SideMenu = observer(({ onMenuToggle }) => {
|
||||
"background 0.3s ease, backdrop-filter 0.3s ease, box-shadow 0.3s ease",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
className="side-menu-crest"
|
||||
src={designData?.creastPath || defaultCrest}
|
||||
alt="Герб"
|
||||
/>
|
||||
{carrier?.slogan && (
|
||||
<div className="side-menu-label">{carrier.slogan}</div>
|
||||
)}
|
||||
{shouldRenderSights && (
|
||||
<SightsList
|
||||
ref={sightsListRef}
|
||||
@@ -606,19 +582,16 @@ const SideMenu = observer(({ onMenuToggle }) => {
|
||||
: "#EveryoneOnTheWay"} */}
|
||||
</div>
|
||||
<div className="side-menu-bottom-section">
|
||||
<div className="side-menu-carrier-block">
|
||||
{designData?.carrierPath && (
|
||||
<img
|
||||
className="carrier-logo"
|
||||
src={designData?.carrierPath}
|
||||
alt="ГЭТ - Электротранспорт Санкт-Петербурга"
|
||||
className="side-menu-bottom-photo"
|
||||
src={
|
||||
carrier?.preview_media && !isPreviewMediaError
|
||||
? getMediaUrl(carrier.preview_media)
|
||||
: sideMenuPhoto
|
||||
}
|
||||
onError={() => setIsPreviewMediaError(true)}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
{carrier?.full_name && (
|
||||
<div className="side-menu-slogan">{carrier.short_name}</div>
|
||||
)}
|
||||
</div>
|
||||
<img className="side-menu-bottom-photo" src={sideMenuPhoto} alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="side-menu-control-panel">
|
||||
|
||||
@@ -16,17 +16,6 @@
|
||||
var(--carrier-left, #006f3a);
|
||||
}
|
||||
|
||||
.side-menu-label {
|
||||
margin-top: 10px;
|
||||
text-align: left;
|
||||
font-size: 15px;
|
||||
padding: 0 20px;
|
||||
align-self: flex-start;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
.appeal-button {
|
||||
color: black;
|
||||
padding: 8px 16px;
|
||||
@@ -66,24 +55,6 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.side-menu-carrier-block {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.carrier-logo {
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
.side-menu-slogan {
|
||||
margin-top: 4px;
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 150%;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.side-menu-bottom-photo {
|
||||
width: 100%;
|
||||
margin-top: 32px;
|
||||
@@ -284,12 +255,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.side-menu-crest {
|
||||
width: 170px;
|
||||
align-self: flex-start;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.side-menu-control-lang-open {
|
||||
gap: 10px;
|
||||
display: inline-flex;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
Button,
|
||||
Paper,
|
||||
TextField,
|
||||
Select,
|
||||
MenuItem,
|
||||
@@ -24,9 +23,10 @@ import {
|
||||
SelectMediaDialog,
|
||||
UploadMediaDialog,
|
||||
PreviewMediaDialog,
|
||||
editSightStore,
|
||||
} from "@shared";
|
||||
import { useState, useEffect } from "react";
|
||||
import { ImageUploadCard, LanguageSwitcher } from "@widgets";
|
||||
import { ImageUploadCard, LanguageSwitcher, VideoPreviewCard } from "@widgets";
|
||||
|
||||
type ColorFields = { main_color: string; left_color: string; right_color: string };
|
||||
|
||||
@@ -80,18 +80,29 @@ const ColorPickerField = ({
|
||||
|
||||
export const CarrierCreatePage = observer(() => {
|
||||
const navigate = useNavigate();
|
||||
const { createCarrierData, setCreateCarrierData } = carrierStore;
|
||||
const {
|
||||
createCarrierData,
|
||||
setCreateCarrierData,
|
||||
setCreateCarrierMedia,
|
||||
} = carrierStore;
|
||||
const { language } = languageStore;
|
||||
const canReadCities = authStore.canRead("cities");
|
||||
const { selectedCityId, selectedCity } = useSelectedCity();
|
||||
const [selectedMediaId, setSelectedMediaId] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSelectMediaOpen, setIsSelectMediaOpen] = useState(false);
|
||||
const [isUploadMediaOpen, setIsUploadMediaOpen] = useState(false);
|
||||
const [isPreviewMediaOpen, setIsPreviewMediaOpen] = useState(false);
|
||||
const [mediaId, setMediaId] = useState("");
|
||||
const [activeMediaField, setActiveMediaField] = useState<
|
||||
"preview_media" | "video_preview"
|
||||
>("preview_media");
|
||||
const [activeMenuType, setActiveMenuType] = useState<
|
||||
"thumbnail" | "watermark_lu" | "watermark_rd" | "image" | null
|
||||
| "thumbnail"
|
||||
| "watermark_lu"
|
||||
| "watermark_rd"
|
||||
| "image"
|
||||
| "video_preview"
|
||||
| null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -137,12 +148,16 @@ export const CarrierCreatePage = observer(() => {
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
selectedCityId,
|
||||
createCarrierData[language].slogan,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
);
|
||||
}
|
||||
}, [selectedCityId, createCarrierData.city_id]);
|
||||
}, [
|
||||
selectedCityId,
|
||||
createCarrierData,
|
||||
createCarrierData.city_id,
|
||||
language,
|
||||
setCreateCarrierData,
|
||||
]);
|
||||
|
||||
const handleCreate = async () => {
|
||||
try {
|
||||
@@ -151,7 +166,7 @@ export const CarrierCreatePage = observer(() => {
|
||||
|
||||
toast.success("Перевозчик успешно создан");
|
||||
navigate("/carrier");
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error("Ошибка при создании перевозчика");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -164,27 +179,22 @@ export const CarrierCreatePage = observer(() => {
|
||||
media_name?: string;
|
||||
media_type: number;
|
||||
}) => {
|
||||
setSelectedMediaId(media.id);
|
||||
setCreateCarrierData(
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
createCarrierData.city_id,
|
||||
createCarrierData[language].slogan,
|
||||
media.id,
|
||||
language,
|
||||
);
|
||||
setCreateCarrierMedia(activeMediaField, media.id);
|
||||
};
|
||||
|
||||
const selectedMedia =
|
||||
selectedMediaId && !isMediaIdEmpty(selectedMediaId)
|
||||
? mediaStore.media.find((m) => m.id === selectedMediaId)
|
||||
: null;
|
||||
const effectiveLogoUrl = isMediaIdEmpty(selectedMediaId)
|
||||
const effectivePreviewMediaUrl = isMediaIdEmpty(
|
||||
createCarrierData.preview_media,
|
||||
)
|
||||
? null
|
||||
: selectedMedia?.id ?? selectedMediaId ?? null;
|
||||
: createCarrierData.preview_media;
|
||||
const effectiveVideoPreviewId = isMediaIdEmpty(
|
||||
createCarrierData.video_preview,
|
||||
)
|
||||
? null
|
||||
: createCarrierData.video_preview;
|
||||
|
||||
return (
|
||||
<Paper className="w-full h-full p-3 flex flex-col gap-10">
|
||||
<div className="w-full h-full p-3 flex flex-col gap-10">
|
||||
<LanguageSwitcher />
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
@@ -212,8 +222,6 @@ export const CarrierCreatePage = observer(() => {
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
e.target.value as number,
|
||||
createCarrierData[language].slogan,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
)
|
||||
}
|
||||
@@ -236,8 +244,6 @@ export const CarrierCreatePage = observer(() => {
|
||||
e.target.value,
|
||||
createCarrierData[language].short_name,
|
||||
createCarrierData.city_id,
|
||||
createCarrierData[language].slogan,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
)
|
||||
}
|
||||
@@ -253,24 +259,6 @@ export const CarrierCreatePage = observer(() => {
|
||||
createCarrierData[language].full_name,
|
||||
e.target.value,
|
||||
createCarrierData.city_id,
|
||||
createCarrierData[language].slogan,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Слоган"
|
||||
value={createCarrierData[language].slogan}
|
||||
onChange={(e) =>
|
||||
setCreateCarrierData(
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
createCarrierData.city_id,
|
||||
e.target.value,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
)
|
||||
}
|
||||
@@ -286,8 +274,6 @@ export const CarrierCreatePage = observer(() => {
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
createCarrierData.city_id,
|
||||
createCarrierData[language].slogan,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
{ ...colorFields(createCarrierData), main_color: val }
|
||||
)
|
||||
@@ -306,8 +292,6 @@ export const CarrierCreatePage = observer(() => {
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
createCarrierData.city_id,
|
||||
createCarrierData[language].slogan,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
{ ...colorFields(createCarrierData), left_color: val }
|
||||
)
|
||||
@@ -326,8 +310,6 @@ export const CarrierCreatePage = observer(() => {
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
createCarrierData.city_id,
|
||||
createCarrierData[language].slogan,
|
||||
selectedMediaId || "",
|
||||
language,
|
||||
{ ...colorFields(createCarrierData), right_color: val }
|
||||
)
|
||||
@@ -339,37 +321,58 @@ export const CarrierCreatePage = observer(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full flex flex-col gap-4 max-w-[300px] mx-auto">
|
||||
<div className="w-full flex justify-center items-start gap-10 flex-wrap">
|
||||
<div className="w-full max-w-[300px]">
|
||||
<ImageUploadCard
|
||||
title="Логотип перевозчика"
|
||||
imageKey="thumbnail"
|
||||
imageUrl={effectiveLogoUrl}
|
||||
title="Изображение сайд-меню"
|
||||
imageKey="image"
|
||||
imageUrl={effectivePreviewMediaUrl}
|
||||
onImageClick={() => {
|
||||
setIsPreviewMediaOpen(true);
|
||||
setMediaId(effectiveLogoUrl ?? "");
|
||||
}}
|
||||
onDeleteImageClick={() => {
|
||||
setSelectedMediaId(null);
|
||||
setActiveMenuType(null);
|
||||
setCreateCarrierData(
|
||||
createCarrierData[language].full_name,
|
||||
createCarrierData[language].short_name,
|
||||
createCarrierData.city_id,
|
||||
createCarrierData[language].slogan,
|
||||
"",
|
||||
language,
|
||||
);
|
||||
setMediaId(effectivePreviewMediaUrl ?? "");
|
||||
}}
|
||||
onDeleteImageClick={() =>
|
||||
setCreateCarrierMedia("preview_media", "")
|
||||
}
|
||||
onSelectFileClick={() => {
|
||||
setActiveMediaField("preview_media");
|
||||
setActiveMenuType("image");
|
||||
setIsSelectMediaOpen(true);
|
||||
}}
|
||||
setUploadMediaOpen={() => {
|
||||
setIsUploadMediaOpen(true);
|
||||
setActiveMediaField("preview_media");
|
||||
setActiveMenuType("image");
|
||||
setIsUploadMediaOpen(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full max-w-[300px]">
|
||||
<VideoPreviewCard
|
||||
title="Видео загрузки"
|
||||
videoId={effectiveVideoPreviewId}
|
||||
onVideoClick={() => {
|
||||
if (effectiveVideoPreviewId) {
|
||||
setMediaId(effectiveVideoPreviewId);
|
||||
setIsPreviewMediaOpen(true);
|
||||
}
|
||||
}}
|
||||
onDeleteVideoClick={() =>
|
||||
setCreateCarrierMedia("video_preview", "")
|
||||
}
|
||||
onSelectVideoClick={(file) => {
|
||||
setActiveMediaField("video_preview");
|
||||
setActiveMenuType("video_preview");
|
||||
if (file) {
|
||||
editSightStore.setFileToUpload(file);
|
||||
setIsUploadMediaOpen(true);
|
||||
} else {
|
||||
setIsSelectMediaOpen(true);
|
||||
}
|
||||
}}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -395,7 +398,7 @@ export const CarrierCreatePage = observer(() => {
|
||||
open={isSelectMediaOpen}
|
||||
onClose={() => setIsSelectMediaOpen(false)}
|
||||
onSelectMedia={handleMediaSelect}
|
||||
mediaType={1}
|
||||
mediaType={activeMediaField === "video_preview" ? 2 : 1}
|
||||
/>
|
||||
|
||||
<UploadMediaDialog
|
||||
@@ -412,6 +415,6 @@ export const CarrierCreatePage = observer(() => {
|
||||
onClose={() => setIsPreviewMediaOpen(false)}
|
||||
mediaId={mediaId}
|
||||
/>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
Button,
|
||||
Paper,
|
||||
TextField,
|
||||
Select,
|
||||
MenuItem,
|
||||
@@ -22,9 +21,14 @@ import {
|
||||
isMediaIdEmpty,
|
||||
LoadingSpinner,
|
||||
selectedCityStore,
|
||||
editSightStore,
|
||||
} from "@shared";
|
||||
import { useState, useEffect } from "react";
|
||||
import { ImageUploadCard, LanguageSwitcher, DeleteModal } from "@widgets";
|
||||
import {
|
||||
ImageUploadCard,
|
||||
LanguageSwitcher,
|
||||
VideoPreviewCard,
|
||||
} from "@widgets";
|
||||
import {
|
||||
SelectMediaDialog,
|
||||
PreviewMediaDialog,
|
||||
@@ -88,7 +92,12 @@ const ColorPickerField = ({
|
||||
export const CarrierEditPage = observer(() => {
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
const { getCarrier, setEditCarrierData, editCarrierData } = carrierStore;
|
||||
const {
|
||||
getCarrier,
|
||||
setEditCarrierData,
|
||||
setEditCarrierMedia,
|
||||
editCarrierData,
|
||||
} = carrierStore;
|
||||
const { language } = languageStore;
|
||||
const canReadCities = authStore.canRead("cities");
|
||||
|
||||
@@ -98,10 +107,17 @@ export const CarrierEditPage = observer(() => {
|
||||
const [isUploadMediaOpen, setIsUploadMediaOpen] = useState(false);
|
||||
const [isPreviewMediaOpen, setIsPreviewMediaOpen] = useState(false);
|
||||
const [mediaId, setMediaId] = useState("");
|
||||
const [isDeleteLogoModalOpen, setIsDeleteLogoModalOpen] = useState(false);
|
||||
const [initialCityName, setInitialCityName] = useState("");
|
||||
const [activeMediaField, setActiveMediaField] = useState<
|
||||
"preview_media" | "video_preview"
|
||||
>("preview_media");
|
||||
const [activeMenuType, setActiveMenuType] = useState<
|
||||
"thumbnail" | "watermark_lu" | "watermark_rd" | "image" | null
|
||||
| "thumbnail"
|
||||
| "watermark_lu"
|
||||
| "watermark_rd"
|
||||
| "image"
|
||||
| "video_preview"
|
||||
| null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -137,8 +153,6 @@ export const CarrierEditPage = observer(() => {
|
||||
carrierData.ru?.full_name || "",
|
||||
carrierData.ru?.short_name || "",
|
||||
carrierData.ru?.city_id || 0,
|
||||
carrierData.ru?.slogan || "",
|
||||
carrierData.ru?.logo || "",
|
||||
"ru",
|
||||
colors,
|
||||
);
|
||||
@@ -146,18 +160,22 @@ export const CarrierEditPage = observer(() => {
|
||||
carrierData.en?.full_name || "",
|
||||
carrierData.en?.short_name || "",
|
||||
carrierData.en?.city_id || 0,
|
||||
carrierData.en?.slogan || "",
|
||||
carrierData.en?.logo || "",
|
||||
"en",
|
||||
);
|
||||
setEditCarrierData(
|
||||
carrierData.zh?.full_name || "",
|
||||
carrierData.zh?.short_name || "",
|
||||
carrierData.zh?.city_id || 0,
|
||||
carrierData.zh?.slogan || "",
|
||||
carrierData.zh?.logo || "",
|
||||
"zh",
|
||||
);
|
||||
setEditCarrierMedia(
|
||||
"video_preview",
|
||||
carrierData.ru?.video_preview || "",
|
||||
);
|
||||
setEditCarrierMedia(
|
||||
"preview_media",
|
||||
carrierData.ru?.preview_media || "",
|
||||
);
|
||||
setInitialCityName(carrierData.ru?.city || "");
|
||||
}
|
||||
|
||||
@@ -168,7 +186,7 @@ export const CarrierEditPage = observer(() => {
|
||||
})();
|
||||
|
||||
languageStore.setLanguage("ru");
|
||||
}, [id]);
|
||||
}, [id, getCarrier, setEditCarrierData, setEditCarrierMedia]);
|
||||
|
||||
const handleEdit = async () => {
|
||||
try {
|
||||
@@ -176,7 +194,7 @@ export const CarrierEditPage = observer(() => {
|
||||
await carrierStore.editCarrier(Number(id));
|
||||
toast.success("Перевозчик успешно обновлен");
|
||||
navigate("/carrier");
|
||||
} catch (error) {
|
||||
} catch {
|
||||
toast.error("Ошибка при обновлении перевозчика");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -189,23 +207,19 @@ export const CarrierEditPage = observer(() => {
|
||||
media_name?: string;
|
||||
media_type: number;
|
||||
}) => {
|
||||
setEditCarrierData(
|
||||
editCarrierData[language].full_name,
|
||||
editCarrierData[language].short_name,
|
||||
editCarrierData.city_id,
|
||||
editCarrierData[language].slogan,
|
||||
media.id,
|
||||
language,
|
||||
);
|
||||
setEditCarrierMedia(activeMediaField, media.id);
|
||||
};
|
||||
|
||||
const selectedMedia =
|
||||
editCarrierData.logo && !isMediaIdEmpty(editCarrierData.logo)
|
||||
? mediaStore.media.find((m) => m.id === editCarrierData.logo)
|
||||
: null;
|
||||
const effectiveLogoUrl = isMediaIdEmpty(editCarrierData.logo)
|
||||
const effectivePreviewMediaUrl = isMediaIdEmpty(
|
||||
editCarrierData.preview_media,
|
||||
)
|
||||
? null
|
||||
: (selectedMedia?.id ?? editCarrierData.logo);
|
||||
: editCarrierData.preview_media;
|
||||
const effectiveVideoPreviewId = isMediaIdEmpty(
|
||||
editCarrierData.video_preview,
|
||||
)
|
||||
? null
|
||||
: editCarrierData.video_preview;
|
||||
|
||||
const baseCities = canReadCities
|
||||
? cityStore.cities["ru"].data
|
||||
@@ -248,7 +262,7 @@ export const CarrierEditPage = observer(() => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper className="w-full h-full p-3 flex flex-col gap-10">
|
||||
<div className="w-full h-full p-3 flex flex-col gap-10">
|
||||
<LanguageSwitcher />
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
@@ -275,8 +289,6 @@ export const CarrierEditPage = observer(() => {
|
||||
editCarrierData[language].full_name,
|
||||
editCarrierData[language].short_name,
|
||||
Number(e.target.value),
|
||||
editCarrierData[language].slogan,
|
||||
editCarrierData.logo,
|
||||
language,
|
||||
)
|
||||
}
|
||||
@@ -299,8 +311,6 @@ export const CarrierEditPage = observer(() => {
|
||||
e.target.value,
|
||||
editCarrierData[language].short_name,
|
||||
editCarrierData.city_id,
|
||||
editCarrierData[language].slogan,
|
||||
editCarrierData.logo,
|
||||
language,
|
||||
)
|
||||
}
|
||||
@@ -316,24 +326,6 @@ export const CarrierEditPage = observer(() => {
|
||||
editCarrierData[language].full_name,
|
||||
e.target.value,
|
||||
editCarrierData.city_id,
|
||||
editCarrierData[language].slogan,
|
||||
editCarrierData.logo,
|
||||
language,
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Слоган"
|
||||
value={editCarrierData[language].slogan}
|
||||
onChange={(e) =>
|
||||
setEditCarrierData(
|
||||
editCarrierData[language].full_name,
|
||||
editCarrierData[language].short_name,
|
||||
editCarrierData.city_id,
|
||||
e.target.value,
|
||||
editCarrierData.logo,
|
||||
language,
|
||||
)
|
||||
}
|
||||
@@ -349,8 +341,6 @@ export const CarrierEditPage = observer(() => {
|
||||
editCarrierData[language].full_name,
|
||||
editCarrierData[language].short_name,
|
||||
editCarrierData.city_id,
|
||||
editCarrierData[language].slogan,
|
||||
editCarrierData.logo,
|
||||
language,
|
||||
{ ...colorFields(editCarrierData), main_color: val },
|
||||
)
|
||||
@@ -370,8 +360,6 @@ export const CarrierEditPage = observer(() => {
|
||||
editCarrierData[language].full_name,
|
||||
editCarrierData[language].short_name,
|
||||
editCarrierData.city_id,
|
||||
editCarrierData[language].slogan,
|
||||
editCarrierData.logo,
|
||||
language,
|
||||
{ ...colorFields(editCarrierData), left_color: val },
|
||||
)
|
||||
@@ -391,8 +379,6 @@ export const CarrierEditPage = observer(() => {
|
||||
editCarrierData[language].full_name,
|
||||
editCarrierData[language].short_name,
|
||||
editCarrierData.city_id,
|
||||
editCarrierData[language].slogan,
|
||||
editCarrierData.logo,
|
||||
language,
|
||||
{ ...colorFields(editCarrierData), right_color: val },
|
||||
)
|
||||
@@ -405,28 +391,58 @@ export const CarrierEditPage = observer(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full flex flex-col gap-4 max-w-[300px] mx-auto">
|
||||
<div className="w-full flex justify-center items-start gap-10 flex-wrap">
|
||||
<div className="w-full max-w-[300px]">
|
||||
<ImageUploadCard
|
||||
title="Логотип перевозчика"
|
||||
imageKey="thumbnail"
|
||||
imageUrl={effectiveLogoUrl}
|
||||
title="Изображение сайд-меню"
|
||||
imageKey="image"
|
||||
imageUrl={effectivePreviewMediaUrl}
|
||||
onImageClick={() => {
|
||||
setIsPreviewMediaOpen(true);
|
||||
setMediaId(effectiveLogoUrl ?? "");
|
||||
}}
|
||||
onDeleteImageClick={() => {
|
||||
setIsDeleteLogoModalOpen(true);
|
||||
setMediaId(effectivePreviewMediaUrl ?? "");
|
||||
}}
|
||||
onDeleteImageClick={() =>
|
||||
setEditCarrierMedia("preview_media", "")
|
||||
}
|
||||
onSelectFileClick={() => {
|
||||
setActiveMediaField("preview_media");
|
||||
setActiveMenuType("image");
|
||||
setIsSelectMediaOpen(true);
|
||||
}}
|
||||
setUploadMediaOpen={() => {
|
||||
setIsUploadMediaOpen(true);
|
||||
setActiveMediaField("preview_media");
|
||||
setActiveMenuType("image");
|
||||
setIsUploadMediaOpen(true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full max-w-[300px]">
|
||||
<VideoPreviewCard
|
||||
title="Видео загрузки"
|
||||
videoId={effectiveVideoPreviewId}
|
||||
onVideoClick={() => {
|
||||
if (effectiveVideoPreviewId) {
|
||||
setMediaId(effectiveVideoPreviewId);
|
||||
setIsPreviewMediaOpen(true);
|
||||
}
|
||||
}}
|
||||
onDeleteVideoClick={() =>
|
||||
setEditCarrierMedia("video_preview", "")
|
||||
}
|
||||
onSelectVideoClick={(file) => {
|
||||
setActiveMediaField("video_preview");
|
||||
setActiveMenuType("video_preview");
|
||||
if (file) {
|
||||
editSightStore.setFileToUpload(file);
|
||||
setIsUploadMediaOpen(true);
|
||||
} else {
|
||||
setIsSelectMediaOpen(true);
|
||||
}
|
||||
}}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -451,7 +467,7 @@ export const CarrierEditPage = observer(() => {
|
||||
open={isSelectMediaOpen}
|
||||
onClose={() => setIsSelectMediaOpen(false)}
|
||||
onSelectMedia={handleMediaSelect}
|
||||
mediaType={1}
|
||||
mediaType={activeMediaField === "video_preview" ? 2 : 1}
|
||||
/>
|
||||
|
||||
<UploadMediaDialog
|
||||
@@ -469,22 +485,6 @@ export const CarrierEditPage = observer(() => {
|
||||
mediaId={mediaId}
|
||||
/>
|
||||
|
||||
<DeleteModal
|
||||
open={isDeleteLogoModalOpen}
|
||||
onDelete={() => {
|
||||
setEditCarrierData(
|
||||
editCarrierData[language].full_name,
|
||||
editCarrierData[language].short_name,
|
||||
editCarrierData.city_id,
|
||||
editCarrierData[language].slogan,
|
||||
"",
|
||||
language,
|
||||
);
|
||||
setIsDeleteLogoModalOpen(false);
|
||||
}}
|
||||
onCancel={() => setIsDeleteLogoModalOpen(false)}
|
||||
edit
|
||||
/>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -7,16 +7,8 @@ import {
|
||||
InputLabel,
|
||||
Typography,
|
||||
Box,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
MediaViewer,
|
||||
VideoPreviewCard,
|
||||
ImageUploadCard,
|
||||
} from "@widgets";
|
||||
import { ImageUploadCard } from "@widgets";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ArrowLeft, Loader2, Save, Plus, X } from "lucide-react";
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
@@ -59,14 +51,10 @@ export const RouteCreatePage = observer(() => {
|
||||
const [centerLat, setCenterLat] = useState("");
|
||||
const [centerLng, setCenterLng] = useState("");
|
||||
const [videoTimer, setVideoTimer] = useState(420);
|
||||
const [videoPreview, setVideoPreview] = useState<string>("");
|
||||
const [icon, setIcon] = useState<string>("");
|
||||
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);
|
||||
@@ -74,7 +62,6 @@ export const RouteCreatePage = observer(() => {
|
||||
const [activeIconMenuType, setActiveIconMenuType] = useState<
|
||||
"thumbnail" | "watermark_lu" | "watermark_rd" | "image" | null
|
||||
>(null);
|
||||
const [fileToUpload, setFileToUpload] = useState<File | null>(null);
|
||||
const { language } = languageStore;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -140,40 +127,6 @@ export const RouteCreatePage = observer(() => {
|
||||
articlesStore.getArticleList();
|
||||
};
|
||||
|
||||
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 handleVideoPreviewClick = () => {
|
||||
setIsVideoPreviewOpen(true);
|
||||
};
|
||||
|
||||
const handleIconSelect = (media: {
|
||||
id: string;
|
||||
filename: string;
|
||||
@@ -189,7 +142,6 @@ export const RouteCreatePage = observer(() => {
|
||||
? 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 {
|
||||
@@ -284,7 +236,6 @@ export const RouteCreatePage = observer(() => {
|
||||
center_latitude,
|
||||
center_longitude,
|
||||
path,
|
||||
video_preview: !isMediaIdEmpty(videoPreview) ? videoPreview : undefined,
|
||||
icon: !isMediaIdEmpty(icon) ? icon : undefined,
|
||||
video_timer: videoTimer,
|
||||
};
|
||||
@@ -485,16 +436,6 @@ export const RouteCreatePage = observer(() => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 max-w-[300px]">
|
||||
<VideoPreviewCard
|
||||
title="Видеозаставка"
|
||||
videoId={effectiveVideoId}
|
||||
onVideoClick={handleVideoPreviewClick}
|
||||
onDeleteVideoClick={() => setVideoPreview("")}
|
||||
onSelectVideoClick={handleVideoFileSelect}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<FormControl fullWidth required>
|
||||
@@ -578,7 +519,7 @@ export const RouteCreatePage = observer(() => {
|
||||
/>
|
||||
<TextField
|
||||
className="w-full"
|
||||
label="Таймер видео заставки (сек)"
|
||||
label="Таймер показа видео при бездействии (сек)"
|
||||
type="number"
|
||||
value={videoTimer}
|
||||
onChange={(e) => {
|
||||
@@ -611,51 +552,6 @@ export const RouteCreatePage = observer(() => {
|
||||
onClose={() => setIsSelectArticleDialogOpen(false)}
|
||||
onSelectArticle={handleArticleSelect}
|
||||
/>
|
||||
<SelectMediaDialog
|
||||
open={isSelectVideoDialogOpen}
|
||||
onClose={() => setIsSelectVideoDialogOpen(false)}
|
||||
onSelectMedia={handleVideoSelect}
|
||||
mediaType={2}
|
||||
/>
|
||||
{effectiveVideoId && (
|
||||
<Dialog
|
||||
open={isVideoPreviewOpen}
|
||||
onClose={() => setIsVideoPreviewOpen(false)}
|
||||
maxWidth="md"
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitle>Предпросмотр видео</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box className="flex justify-center items-center p-4">
|
||||
<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={routeName || "Маршрут"}
|
||||
contextType="sight"
|
||||
initialFile={fileToUpload || undefined}
|
||||
afterUpload={handleVideoUpload}
|
||||
/>
|
||||
|
||||
<SelectMediaDialog
|
||||
open={isSelectIconDialogOpen}
|
||||
onClose={() => setIsSelectIconDialogOpen(false)}
|
||||
|
||||
@@ -7,14 +7,8 @@ import {
|
||||
InputLabel,
|
||||
Typography,
|
||||
Box,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
MediaViewer,
|
||||
VideoPreviewCard,
|
||||
ImageUploadCard,
|
||||
DeleteModal,
|
||||
} from "@widgets";
|
||||
@@ -54,9 +48,6 @@ 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);
|
||||
@@ -65,7 +56,6 @@ export const RouteEditPage = observer(() => {
|
||||
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>("");
|
||||
|
||||
@@ -114,9 +104,6 @@ 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) {
|
||||
@@ -238,46 +225,6 @@ 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 handleVideoPreviewClick = () => {
|
||||
if (editRouteData.video_preview && editRouteData.video_preview !== "") {
|
||||
setIsVideoPreviewOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
// Получаем название выбранной статьи для отображения
|
||||
const selectedArticle = articlesStore.articleList.ru.data.find(
|
||||
(article) => article.id === editRouteData.governor_appeal
|
||||
@@ -555,7 +502,7 @@ export const RouteEditPage = observer(() => {
|
||||
/>
|
||||
<TextField
|
||||
className="w-full"
|
||||
label="Таймер видео заставки (сек)"
|
||||
label="Таймер показа видео при бездействии (сек)"
|
||||
type="number"
|
||||
value={editRouteData.video_timer ?? 420}
|
||||
onChange={(e) => {
|
||||
@@ -646,18 +593,6 @@ export const RouteEditPage = observer(() => {
|
||||
}}
|
||||
/>
|
||||
</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>
|
||||
|
||||
@@ -703,49 +638,6 @@ export const RouteEditPage = observer(() => {
|
||||
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)}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Button } from "@mui/material";
|
||||
import { useNavigate, useNavigationType } from "react-router";
|
||||
import { MediaViewer } from "@widgets";
|
||||
import { useMapData } from "./MapDataContext";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState } from "react";
|
||||
import { authInstance, isMediaIdEmpty } from "@shared";
|
||||
import { authInstance } from "@shared";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import LanguageSelector from "./web-gl/LanguageSelector";
|
||||
|
||||
@@ -17,9 +16,8 @@ export const LeftSidebar = observer(({ open, onToggle }: LeftSidebarProps) => {
|
||||
const navigate = useNavigate();
|
||||
const navigationType = useNavigationType();
|
||||
const { routeData } = useMapData();
|
||||
const [carrierLogo, setCarrierLogo] = useState<string | null>(null);
|
||||
const [carrierSlogan, setCarrierSlogan] = useState<string | null>(null);
|
||||
const [carrierShortName, setCarrierShortName] = useState<string | null>(null);
|
||||
const [previewMedia, setPreviewMedia] = useState<string | null>(null);
|
||||
const [isPreviewMediaError, setIsPreviewMediaError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchCarrierData() {
|
||||
@@ -27,9 +25,8 @@ export const LeftSidebar = observer(({ open, onToggle }: LeftSidebarProps) => {
|
||||
const carrier = (
|
||||
await authInstance.get(`/carrier/${routeData.carrier_id}`)
|
||||
).data;
|
||||
setCarrierLogo(carrier.logo);
|
||||
setCarrierSlogan(carrier.slogan ?? null);
|
||||
setCarrierShortName(carrier.short_name ?? null);
|
||||
setPreviewMedia(carrier.preview_media ?? null);
|
||||
setIsPreviewMediaError(false);
|
||||
}
|
||||
}
|
||||
fetchCarrierData();
|
||||
@@ -94,43 +91,6 @@ export const LeftSidebar = observer(({ open, onToggle }: LeftSidebarProps) => {
|
||||
pointerEvents: open ? "auto" : "none",
|
||||
}}
|
||||
>
|
||||
{/* Герб — .side-menu-crest */}
|
||||
<div
|
||||
style={{
|
||||
width: 170,
|
||||
height: 170,
|
||||
alignSelf: "flex-start",
|
||||
marginLeft: 20,
|
||||
backgroundColor: "rgba(255,255,255,0.15)",
|
||||
borderRadius: 8,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
color: "rgba(255,255,255,0.5)",
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
Герб
|
||||
</div>
|
||||
|
||||
{/* Слоган — .side-menu-label */}
|
||||
{carrierSlogan && (
|
||||
<div
|
||||
style={{
|
||||
marginTop: 10,
|
||||
textAlign: "left",
|
||||
fontSize: 15,
|
||||
padding: "0 20px",
|
||||
alignSelf: "flex-start",
|
||||
fontWeight: 400,
|
||||
lineHeight: "150%",
|
||||
}}
|
||||
>
|
||||
{carrierSlogan}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Кнопки — .side-menu-buttons */}
|
||||
<div
|
||||
style={{
|
||||
@@ -175,39 +135,14 @@ export const LeftSidebar = observer(({ open, onToggle }: LeftSidebarProps) => {
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
{/* .side-menu-carrier-block */}
|
||||
<div style={{ padding: "0 20px" }}>
|
||||
{carrierLogo && !isMediaIdEmpty(carrierLogo) && (
|
||||
<div style={{ width: 170 }}>
|
||||
<MediaViewer
|
||||
media={{
|
||||
id: carrierLogo,
|
||||
media_type: 1,
|
||||
filename: "carrier_logo",
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{carrierShortName && (
|
||||
<div
|
||||
style={{
|
||||
marginTop: 4,
|
||||
textAlign: "left",
|
||||
fontSize: 16,
|
||||
fontWeight: 700,
|
||||
lineHeight: "150%",
|
||||
color: "#fff",
|
||||
}}
|
||||
>
|
||||
{carrierShortName}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* .side-menu-bottom-photo */}
|
||||
<img
|
||||
src="/side-menu-photo.png"
|
||||
src={
|
||||
previewMedia && !isPreviewMediaError
|
||||
? `${import.meta.env.VITE_KRBL_MEDIA}${previewMedia}/download?token=${localStorage.getItem("token") ?? ""}`
|
||||
: "/side-menu-photo.png"
|
||||
}
|
||||
onError={() => setIsPreviewMediaError(true)}
|
||||
alt=""
|
||||
style={{
|
||||
width: "288px",
|
||||
|
||||
@@ -131,7 +131,7 @@ export const SnapshotListPage = observer(() => {
|
||||
},
|
||||
{
|
||||
field: "created_at",
|
||||
headerName: "Дата создания",
|
||||
headerName: "Дата создания (UTC+03:00, MSK)",
|
||||
flex: 1,
|
||||
renderCell: (params: GridRenderCellParams) => {
|
||||
return <div>{params.value ? params.value : "-"}</div>;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { mediaStore, MEDIA_TYPE_LABELS } from "@shared";
|
||||
import {
|
||||
mediaStore,
|
||||
MEDIA_TYPE_LABELS,
|
||||
PreviewBackgroundToggle,
|
||||
} from "@shared";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
@@ -29,6 +33,8 @@ export const PreviewMediaDialog = observer(
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [isPreviewBackgroundDark, setIsPreviewBackgroundDark] =
|
||||
useState(false);
|
||||
const media = mediaId
|
||||
? mediaStore.media.find((m) => m.id === mediaId)
|
||||
: null;
|
||||
@@ -40,6 +46,7 @@ export const PreviewMediaDialog = observer(
|
||||
if (media) {
|
||||
setMediaName(media.media_name);
|
||||
setMediaFilename(media.filename);
|
||||
setIsPreviewBackgroundDark(false);
|
||||
}
|
||||
}, [media]);
|
||||
|
||||
@@ -141,8 +148,19 @@ export const PreviewMediaDialog = observer(
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
backgroundColor: isPreviewBackgroundDark ? "#000" : "#fff",
|
||||
transition: "background-color 150ms ease",
|
||||
}}
|
||||
>
|
||||
{[1, 3, 4, 5].includes(media.media_type) && (
|
||||
<PreviewBackgroundToggle
|
||||
dark={isPreviewBackgroundDark}
|
||||
onToggle={() =>
|
||||
setIsPreviewBackgroundDark((value) => !value)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<MediaViewer
|
||||
media={{
|
||||
id: mediaId,
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
clearBlobAndGLTFCache,
|
||||
authStore,
|
||||
snapshotStore,
|
||||
PreviewBackgroundToggle,
|
||||
} from "@shared";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
@@ -90,6 +91,8 @@ export const UploadMediaDialog = observer(
|
||||
[]
|
||||
);
|
||||
const [isPreviewLoaded, setIsPreviewLoaded] = useState(false);
|
||||
const [isPreviewBackgroundDark, setIsPreviewBackgroundDark] =
|
||||
useState(false);
|
||||
const previousMediaUrlRef = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -347,6 +350,7 @@ export const UploadMediaDialog = observer(
|
||||
setMediaUrl(null);
|
||||
setMediaFile(null);
|
||||
setIsPreviewLoaded(false);
|
||||
setIsPreviewBackgroundDark(false);
|
||||
previousMediaUrlRef.current = null;
|
||||
onClose();
|
||||
};
|
||||
@@ -416,8 +420,18 @@ export const UploadMediaDialog = observer(
|
||||
justifyContent: "center",
|
||||
height: "100%",
|
||||
position: "relative",
|
||||
backgroundColor: isPreviewBackgroundDark ? "#000" : "#fff",
|
||||
transition: "background-color 150ms ease",
|
||||
}}
|
||||
>
|
||||
{mediaUrl && mediaType !== 2 && mediaType !== 6 && (
|
||||
<PreviewBackgroundToggle
|
||||
dark={isPreviewBackgroundDark}
|
||||
onToggle={() =>
|
||||
setIsPreviewBackgroundDark((value) => !value)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{!isPreviewLoaded && mediaUrl && (
|
||||
<Box
|
||||
sx={{
|
||||
|
||||
@@ -12,10 +12,10 @@ export type Carrier = {
|
||||
id: number;
|
||||
short_name: string;
|
||||
full_name: string;
|
||||
slogan: string;
|
||||
city: string;
|
||||
city_id: number;
|
||||
logo: string;
|
||||
preview_media?: string;
|
||||
video_preview?: string;
|
||||
main_color: string;
|
||||
left_color: string;
|
||||
right_color: string;
|
||||
@@ -111,38 +111,40 @@ class CarrierStore {
|
||||
|
||||
createCarrierData = {
|
||||
city_id: 0,
|
||||
logo: "",
|
||||
preview_media: "",
|
||||
video_preview: "",
|
||||
main_color: "",
|
||||
left_color: "",
|
||||
right_color: "",
|
||||
ru: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
en: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
zh: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
};
|
||||
|
||||
setCreateCarrierMedia = (
|
||||
field: "preview_media" | "video_preview",
|
||||
mediaId: string
|
||||
) => {
|
||||
this.createCarrierData[field] = mediaId;
|
||||
};
|
||||
|
||||
setCreateCarrierData = (
|
||||
fullName: string,
|
||||
shortName: string,
|
||||
cityId: number,
|
||||
slogan: string,
|
||||
logoId: string,
|
||||
language: Language,
|
||||
colors?: { main_color?: string; left_color?: string; right_color?: string }
|
||||
) => {
|
||||
this.createCarrierData.city_id = cityId;
|
||||
this.createCarrierData.logo = logoId;
|
||||
if (colors) {
|
||||
if (colors.main_color !== undefined) this.createCarrierData.main_color = colors.main_color;
|
||||
if (colors.left_color !== undefined) this.createCarrierData.left_color = colors.left_color;
|
||||
@@ -151,7 +153,6 @@ class CarrierStore {
|
||||
this.createCarrierData[language] = {
|
||||
full_name: fullName,
|
||||
short_name: shortName,
|
||||
slogan: slogan,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -206,8 +207,12 @@ class CarrierStore {
|
||||
short_name: (this.createCarrierData[language].short_name || "").trim(),
|
||||
city: cityName,
|
||||
city_id: this.createCarrierData.city_id,
|
||||
slogan: (this.createCarrierData[language].slogan || "").trim(),
|
||||
...(this.createCarrierData.logo ? { logo: this.createCarrierData.logo } : {}),
|
||||
...(this.createCarrierData.preview_media
|
||||
? { preview_media: this.createCarrierData.preview_media }
|
||||
: {}),
|
||||
...(this.createCarrierData.video_preview
|
||||
? { video_preview: this.createCarrierData.video_preview }
|
||||
: {}),
|
||||
...(this.createCarrierData.main_color ? { main_color: this.createCarrierData.main_color } : {}),
|
||||
...(this.createCarrierData.left_color ? { left_color: this.createCarrierData.left_color } : {}),
|
||||
...(this.createCarrierData.right_color ? { right_color: this.createCarrierData.right_color } : {}),
|
||||
@@ -221,55 +226,55 @@ class CarrierStore {
|
||||
this.carriers[language].data.push(response.data);
|
||||
});
|
||||
|
||||
for (const lang of ["ru", "en", "zh"].filter((l) => l !== language)) {
|
||||
for (const lang of (["ru", "en", "zh"] as const).filter(
|
||||
(item) => item !== language
|
||||
)) {
|
||||
const cityNameForLang = this.resolveCityName(
|
||||
this.createCarrierData.city_id,
|
||||
lang as Language
|
||||
lang
|
||||
);
|
||||
const localizedData = this.createCarrierData[lang];
|
||||
const patchPayload = {
|
||||
// @ts-ignore
|
||||
full_name: ((this.createCarrierData[lang as any].full_name as string) || "").trim(),
|
||||
// @ts-ignore
|
||||
short_name: ((this.createCarrierData[lang as any].short_name as string) || "").trim(),
|
||||
full_name: (localizedData.full_name || "").trim(),
|
||||
short_name: (localizedData.short_name || "").trim(),
|
||||
city: cityNameForLang || cityName,
|
||||
city_id: this.createCarrierData.city_id,
|
||||
// @ts-ignore
|
||||
slogan: ((this.createCarrierData[lang as any].slogan as string) || "").trim(),
|
||||
...(this.createCarrierData.logo
|
||||
? { logo: this.createCarrierData.logo }
|
||||
...(this.createCarrierData.preview_media
|
||||
? { preview_media: this.createCarrierData.preview_media }
|
||||
: {}),
|
||||
...(this.createCarrierData.video_preview
|
||||
? { video_preview: this.createCarrierData.video_preview }
|
||||
: {}),
|
||||
};
|
||||
|
||||
const response = await languageInstance(lang as Language).patch(
|
||||
const response = await languageInstance(lang).patch(
|
||||
`/carrier/${carrierId}`,
|
||||
patchPayload
|
||||
);
|
||||
|
||||
runInAction(() => {
|
||||
this.carriers[lang as keyof Carriers].data.push(response.data);
|
||||
this.carriers[lang].data.push(response.data);
|
||||
});
|
||||
}
|
||||
|
||||
this.createCarrierData = {
|
||||
city_id: 0,
|
||||
logo: "",
|
||||
preview_media: "",
|
||||
video_preview: "",
|
||||
main_color: "",
|
||||
left_color: "",
|
||||
right_color: "",
|
||||
ru: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
en: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
zh: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -278,36 +283,38 @@ class CarrierStore {
|
||||
ru: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
en: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
zh: {
|
||||
full_name: "",
|
||||
short_name: "",
|
||||
slogan: "",
|
||||
},
|
||||
city_id: 0,
|
||||
logo: "",
|
||||
preview_media: "",
|
||||
video_preview: "",
|
||||
main_color: "",
|
||||
left_color: "",
|
||||
right_color: "",
|
||||
};
|
||||
|
||||
setEditCarrierMedia = (
|
||||
field: "preview_media" | "video_preview",
|
||||
mediaId: string
|
||||
) => {
|
||||
this.editCarrierData[field] = mediaId;
|
||||
};
|
||||
|
||||
setEditCarrierData = (
|
||||
fullName: string,
|
||||
shortName: string,
|
||||
cityId: number,
|
||||
slogan: string,
|
||||
logoId: string,
|
||||
language: Language,
|
||||
colors?: { main_color?: string; left_color?: string; right_color?: string }
|
||||
) => {
|
||||
this.editCarrierData.city_id = cityId;
|
||||
this.editCarrierData.logo = logoId;
|
||||
if (colors) {
|
||||
if (colors.main_color !== undefined) this.editCarrierData.main_color = colors.main_color;
|
||||
if (colors.left_color !== undefined) this.editCarrierData.left_color = colors.left_color;
|
||||
@@ -316,7 +323,6 @@ class CarrierStore {
|
||||
this.editCarrierData[language] = {
|
||||
full_name: fullName,
|
||||
short_name: shortName,
|
||||
slogan: slogan,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -327,10 +333,10 @@ class CarrierStore {
|
||||
...this.editCarrierData[lang],
|
||||
full_name: (this.editCarrierData[lang].full_name || "").trim(),
|
||||
short_name: (this.editCarrierData[lang].short_name || "").trim(),
|
||||
slogan: (this.editCarrierData[lang].slogan || "").trim(),
|
||||
city: cityName,
|
||||
city_id: this.editCarrierData.city_id,
|
||||
...(this.editCarrierData.logo ? { logo: this.editCarrierData.logo } : {}),
|
||||
preview_media: this.editCarrierData.preview_media || null,
|
||||
video_preview: this.editCarrierData.video_preview || null,
|
||||
...(this.editCarrierData.main_color ? { main_color: this.editCarrierData.main_color } : {}),
|
||||
...(this.editCarrierData.left_color ? { left_color: this.editCarrierData.left_color } : {}),
|
||||
...(this.editCarrierData.right_color ? { right_color: this.editCarrierData.right_color } : {}),
|
||||
|
||||
@@ -23,7 +23,6 @@ export type Route = {
|
||||
route_sys_number: string;
|
||||
scale_max: number;
|
||||
scale_min: number;
|
||||
video_preview: string;
|
||||
video_timer: number;
|
||||
};
|
||||
|
||||
@@ -154,21 +153,16 @@ class RouteStore {
|
||||
route_sys_number: "",
|
||||
scale_max: 0,
|
||||
scale_min: 0,
|
||||
video_preview: "" as string | undefined,
|
||||
video_timer: 420,
|
||||
};
|
||||
|
||||
setEditRouteData = (data: any) => {
|
||||
this.editRouteData = { ...this.editRouteData, ...data };
|
||||
const routeData = { ...data };
|
||||
delete routeData.video_preview;
|
||||
this.editRouteData = { ...this.editRouteData, ...routeData };
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -180,6 +174,7 @@ 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
|
||||
|
||||
40
src/shared/ui/PreviewBackgroundToggle/index.tsx
Normal file
40
src/shared/ui/PreviewBackgroundToggle/index.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { IconButton, Tooltip } from "@mui/material";
|
||||
import { Contrast } from "lucide-react";
|
||||
|
||||
interface PreviewBackgroundToggleProps {
|
||||
dark: boolean;
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
export const PreviewBackgroundToggle = ({
|
||||
dark,
|
||||
onToggle,
|
||||
}: PreviewBackgroundToggleProps) => (
|
||||
<Tooltip title={dark ? "Сделать фон белым" : "Сделать фон чёрным"}>
|
||||
<IconButton
|
||||
size="small"
|
||||
aria-label={dark ? "Сделать фон белым" : "Сделать фон чёрным"}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onToggle();
|
||||
}}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 8,
|
||||
left: 8,
|
||||
zIndex: 2,
|
||||
color: dark ? "#fff" : "#000",
|
||||
backgroundColor: dark
|
||||
? "rgba(255, 255, 255, 0.16)"
|
||||
: "rgba(255, 255, 255, 0.8)",
|
||||
"&:hover": {
|
||||
backgroundColor: dark
|
||||
? "rgba(255, 255, 255, 0.28)"
|
||||
: "rgba(255, 255, 255, 1)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Contrast size={18} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
@@ -6,3 +6,4 @@ export * from "./AnimatedCircleButton";
|
||||
export * from "./LoadingSpinner";
|
||||
export * from "./MultiSelect";
|
||||
export * from "./SearchInput";
|
||||
export * from "./PreviewBackgroundToggle";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useRef, DragEvent } from "react";
|
||||
import React, { useRef, useState, DragEvent } from "react";
|
||||
import { Paper, Box, Typography, Button, Tooltip } from "@mui/material";
|
||||
import { X, Info, Plus } from "lucide-react";
|
||||
import { editSightStore } from "@shared";
|
||||
import { editSightStore, PreviewBackgroundToggle } from "@shared";
|
||||
import { toast } from "react-toastify";
|
||||
interface ImageUploadCardProps {
|
||||
title: string;
|
||||
@@ -33,6 +33,7 @@ export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
|
||||
tooltipText,
|
||||
}) => {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [isPreviewBackgroundDark, setIsPreviewBackgroundDark] = useState(false);
|
||||
const { setFileToUpload } = editSightStore;
|
||||
|
||||
const handleZoneClick = () => {
|
||||
@@ -124,9 +125,21 @@ export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
|
||||
borderRadius: 1,
|
||||
mb: 1,
|
||||
cursor: imageUrl ? "pointer" : "default",
|
||||
backgroundColor: imageUrl
|
||||
? isPreviewBackgroundDark
|
||||
? "#000"
|
||||
: "#fff"
|
||||
: "transparent",
|
||||
transition: "background-color 150ms ease",
|
||||
}}
|
||||
onClick={onImageClick}
|
||||
>
|
||||
{imageUrl && (
|
||||
<PreviewBackgroundToggle
|
||||
dark={isPreviewBackgroundDark}
|
||||
onToggle={() => setIsPreviewBackgroundDark((value) => !value)}
|
||||
/>
|
||||
)}
|
||||
{imageUrl && (
|
||||
<button
|
||||
className="absolute top-2 right-2"
|
||||
|
||||
Reference in New Issue
Block a user