This commit is contained in:
2026-03-23 01:21:44 +03:00
parent b6a9cecba6
commit 442160ba38
16 changed files with 973 additions and 174 deletions

12
.env
View File

@@ -1,4 +1,8 @@
VITE_API_URL='https://wn.krbl.ru' VITE_API_URL='https://wn.st.unprism.ru'
VITE_REACT_APP ='https://wn.krbl.ru/' VITE_REACT_APP ='https://wn.st.unprism.ru/'
VITE_KRBL_MEDIA='https://wn.krbl.ru/media/' VITE_KRBL_MEDIA='https://wn.st.unprism.ru/media/'
VITE_NEED_AUTH='true' VITE_NEED_AUTH='true'
# VITE_API_URL='https://wn.krbl.ru'
# VITE_REACT_APP ='https://wn.krbl.ru/'
# VITE_KRBL_MEDIA='https://wn.krbl.ru/media/'
# VITE_NEED_AUTH='true'

View File

@@ -1,7 +1,7 @@
{ {
"name": "white-nights", "name": "white-nights",
"private": true, "private": true,
"version": "1.0.4", "version": "1.0.5",
"type": "module", "type": "module",
"license": "UNLICENSED", "license": "UNLICENSED",
"scripts": { "scripts": {

View File

@@ -36,12 +36,13 @@ const MapDataContext = createContext<{
setMapCenter: (x: number, y: number) => void; setMapCenter: (x: number, y: number) => void;
setStationOffset: (stationId: number, x: number, y: number) => void; setStationOffset: (stationId: number, x: number, y: number) => void;
setStationAlign: (stationId: number, align: number) => void; setStationAlign: (stationId: number, align: number) => void;
setStationIconSize: (stationId: number, size: number) => void;
setSightCoordinates: ( setSightCoordinates: (
sightId: number, sightId: number,
latitude: number, latitude: number,
longitude: number longitude: number
) => void; ) => void;
setIconSize: (size: number) => void; setSightIconSize: (sightId: number, size: number) => void;
setFontSize: (size: number) => void; setFontSize: (size: number) => void;
saveChanges: () => void; saveChanges: () => void;
}>({ }>({
@@ -62,8 +63,9 @@ const MapDataContext = createContext<{
setMapCenter: () => {}, setMapCenter: () => {},
setStationOffset: () => {}, setStationOffset: () => {},
setStationAlign: () => {}, setStationAlign: () => {},
setStationIconSize: () => {},
setSightCoordinates: () => {}, setSightCoordinates: () => {},
setIconSize: () => {}, setSightIconSize: () => {},
setFontSize: () => {}, setFontSize: () => {},
saveChanges: () => {}, saveChanges: () => {},
}); });
@@ -145,16 +147,16 @@ export const MapDataProvider = observer(
}, [routeId]); }, [routeId]);
useEffect(() => { useEffect(() => {
if (originalRouteData) if (originalRouteData) {
setRouteData({ ...originalRouteData, ...routeChanges }); setRouteData({ ...originalRouteData, ...routeChanges });
if (originalSightData) setSightData(originalSightData); }
}, [ }, [originalRouteData, routeChanges]);
originalRouteData,
originalSightData, useEffect(() => {
routeChanges, if (originalSightData) {
stationChanges, setSightData(originalSightData);
sightChanges, }
]); }, [originalSightData]);
function setScaleRange(min: number, max: number) { function setScaleRange(min: number, max: number) {
setRouteChanges((prev) => { setRouteChanges((prev) => {
@@ -168,16 +170,6 @@ export const MapDataProvider = observer(
}); });
} }
function setIconSize(size: number) {
const clamped = Math.max(1, Math.min(300, size));
setRouteChanges((prev) => {
if (prev.icon_size === clamped) {
return prev;
}
return { ...prev, icon_size: clamped };
});
}
function setFontSize(size: number) { function setFontSize(size: number) {
const clamped = Math.max(1, Math.min(300, size)); const clamped = Math.max(1, Math.min(300, size));
setRouteChanges((prev) => { setRouteChanges((prev) => {
@@ -241,6 +233,11 @@ export const MapDataProvider = observer(
...s, ...s,
offset_x: station.offset_x, offset_x: station.offset_x,
offset_y: station.offset_y, offset_y: station.offset_y,
align: station.align,
icon_size:
typeof station.icon_size === "number"
? station.icon_size
: s.icon_size,
} }
: s : s
); );
@@ -262,6 +259,10 @@ export const MapDataProvider = observer(
...s, ...s,
latitude: sight.latitude, latitude: sight.latitude,
longitude: sight.longitude, longitude: sight.longitude,
icon_size:
typeof sight.icon_size === "number"
? sight.icon_size
: s.icon_size,
} }
: s : s
) )
@@ -306,6 +307,7 @@ export const MapDataProvider = observer(
offset_x: x, offset_x: x,
offset_y: y, offset_y: y,
align: originalStation?.align ?? 1, align: originalStation?.align ?? 1,
icon_size: originalStation?.icon_size,
transfers: originalStation?.transfers ?? { transfers: originalStation?.transfers ?? {
bus: "", bus: "",
metro_blue: "", metro_blue: "",
@@ -367,6 +369,7 @@ export const MapDataProvider = observer(
align: align, align: align,
offset_x: originalStation?.offset_x ?? 0, offset_x: originalStation?.offset_x ?? 0,
offset_y: originalStation?.offset_y ?? 0, offset_y: originalStation?.offset_y ?? 0,
icon_size: originalStation?.icon_size,
transfers: originalStation?.transfers ?? { transfers: originalStation?.transfers ?? {
bus: "", bus: "",
metro_blue: "", metro_blue: "",
@@ -397,6 +400,70 @@ export const MapDataProvider = observer(
}); });
} }
function setStationIconSize(stationId: number, size: number) {
const clamped = Math.max(1, Math.min(300, Math.round(size)));
const currentStation = stationData.ru?.find(
(station) => station.id === stationId
);
if (currentStation?.icon_size === clamped) {
return;
}
setStationChanges((prev) => {
const existingIndex = prev.findIndex(
(station) => station.station_id === stationId
);
if (existingIndex !== -1) {
const next = [...prev];
next[existingIndex] = {
...next[existingIndex],
icon_size: clamped,
};
return next;
}
const originalStation = originalStationData?.find(
(s) => s.id === stationId
);
return [
...prev,
{
station_id: stationId,
offset_x: currentStation?.offset_x ?? originalStation?.offset_x ?? 0,
offset_y: currentStation?.offset_y ?? originalStation?.offset_y ?? 0,
align: currentStation?.align ?? originalStation?.align ?? 1,
icon_size: clamped,
transfers: currentStation?.transfers ??
originalStation?.transfers ?? {
bus: "",
metro_blue: "",
metro_green: "",
metro_orange: "",
metro_purple: "",
metro_red: "",
train: "",
tram: "",
trolleybus: "",
},
},
];
});
setStationData((prev) => {
const updated = { ...prev };
Object.keys(updated).forEach((lang) => {
updated[lang] = updated[lang].map((station) =>
station.id === stationId
? { ...station, icon_size: clamped }
: station
);
});
return updated;
});
}
function setSightCoordinates( function setSightCoordinates(
sightId: number, sightId: number,
latitude: number, latitude: number,
@@ -435,6 +502,7 @@ export const MapDataProvider = observer(
sight_id: sightId, sight_id: sightId,
latitude, latitude,
longitude, longitude,
icon_size: foundSight.icon_size,
}, },
]; ];
} }
@@ -443,6 +511,49 @@ export const MapDataProvider = observer(
}); });
} }
function setSightIconSize(sightId: number, size: number) {
const clamped = Math.max(1, Math.min(300, Math.round(size)));
setSightData((prev) =>
prev
? prev.map((sight) =>
sight.id === sightId ? { ...sight, icon_size: clamped } : sight
)
: prev
);
setSightChanges((prev) => {
const existingIndex = prev.findIndex(
(sight) => sight.sight_id === sightId
);
if (existingIndex !== -1) {
const next = [...prev];
next[existingIndex] = {
...next[existingIndex],
icon_size: clamped,
};
return next;
}
const foundSight =
sightData?.find((sight) => sight.id === sightId) ??
originalSightData?.find((sight) => sight.id === sightId);
if (!foundSight) {
return prev;
}
return [
...prev,
{
sight_id: sightId,
latitude: foundSight.latitude,
longitude: foundSight.longitude,
icon_size: clamped,
},
];
});
}
useEffect(() => {}, [sightChanges]); useEffect(() => {}, [sightChanges]);
const value = useMemo( const value = useMemo(
@@ -464,8 +575,9 @@ export const MapDataProvider = observer(
saveChanges, saveChanges,
setStationOffset, setStationOffset,
setStationAlign, setStationAlign,
setStationIconSize,
setSightCoordinates, setSightCoordinates,
setIconSize, setSightIconSize,
setFontSize, setFontSize,
}), }),
[ [
@@ -479,7 +591,8 @@ export const MapDataProvider = observer(
isStationLoading, isStationLoading,
isSightLoading, isSightLoading,
selectedSight, selectedSight,
setIconSize, setStationIconSize,
setSightIconSize,
setFontSize, setFontSize,
] ]
); );

View File

@@ -21,7 +21,6 @@ export function RightSidebar() {
originalRouteData, originalRouteData,
setMapRotation, setMapRotation,
setMapCenter, setMapCenter,
setIconSize: updateIconSize,
setFontSize: updateFontSize, setFontSize: updateFontSize,
} = useMapData(); } = useMapData();
const { rotation, rotateToAngle, scale, setScaleAtCenter } = useTransform(); const { rotation, rotateToAngle, scale, setScaleAtCenter } = useTransform();
@@ -34,7 +33,6 @@ export function RightSidebar() {
}); });
const [rotationDegrees, setRotationDegrees] = useState<number>(0); const [rotationDegrees, setRotationDegrees] = useState<number>(0);
const [isUserEditing, setIsUserEditing] = useState<boolean>(false); const [isUserEditing, setIsUserEditing] = useState<boolean>(false);
const [iconSize, setIconSize] = useState<number>(100);
const [fontSize, setFontSize] = useState<number>(100); const [fontSize, setFontSize] = useState<number>(100);
const [isSaving, setIsSaving] = useState<boolean>(false); const [isSaving, setIsSaving] = useState<boolean>(false);
@@ -53,7 +51,6 @@ export function RightSidebar() {
x: originalRouteData.center_latitude ?? 0, x: originalRouteData.center_latitude ?? 0,
y: originalRouteData.center_longitude ?? 0, y: originalRouteData.center_longitude ?? 0,
}); });
setIconSize(originalRouteData.icon_size ?? 100);
setFontSize(originalRouteData.font_size ?? 100); setFontSize(originalRouteData.font_size ?? 100);
} }
}, [originalRouteData]); }, [originalRouteData]);
@@ -97,15 +94,6 @@ export function RightSidebar() {
rotateToAngle((degrees * Math.PI) / 180); rotateToAngle((degrees * Math.PI) / 180);
} }
const handleIconSizeChange = (value: number) => {
if (!Number.isFinite(value)) {
return;
}
const clamped = Math.max(1, Math.min(300, Math.round(value)));
setIconSize(clamped);
updateIconSize(clamped);
};
const handleFontSizeChange = (value: number) => { const handleFontSizeChange = (value: number) => {
if (!Number.isFinite(value)) { if (!Number.isFinite(value)) {
return; return;
@@ -115,11 +103,6 @@ export function RightSidebar() {
updateFontSize(clamped); updateFontSize(clamped);
}; };
useEffect(() => {
const next = routeData?.icon_size ?? originalRouteData?.icon_size ?? 100;
setIconSize((prev) => (Math.abs(prev - next) > 0.5 ? next : prev));
}, [routeData?.icon_size, originalRouteData?.icon_size]);
useEffect(() => { useEffect(() => {
const next = routeData?.font_size ?? originalRouteData?.font_size ?? 100; const next = routeData?.font_size ?? originalRouteData?.font_size ?? 100;
setFontSize((prev) => (Math.abs(prev - next) > 0.5 ? next : prev)); setFontSize((prev) => (Math.abs(prev - next) > 0.5 ? next : prev));
@@ -307,33 +290,6 @@ export function RightSidebar() {
}} }}
/> />
<TextField
type="number"
label="Размер иконок (%)"
variant="filled"
value={iconSize}
onChange={(e) => {
const value = Number(e.target.value);
if (!isNaN(value)) {
handleIconSizeChange(value);
}
}}
style={{ backgroundColor: "#222", borderRadius: 4 }}
sx={{
"& .MuiInputLabel-root": {
color: "#fff",
},
"& .MuiInputBase-input": {
color: "#fff",
},
}}
inputProps={{
min: 1,
max: 300,
step: 1,
}}
/>
<TextField <TextField
type="number" type="number"
label="Размер шрифта (%)" label="Размер шрифта (%)"

View File

@@ -3,7 +3,7 @@ export interface RouteData {
carrier_id: number; carrier_id: number;
center_latitude: number; center_latitude: number;
center_longitude: number; center_longitude: number;
icon_size: number; icon_size?: number;
font_size: number; font_size: number;
governor_appeal: number; governor_appeal: number;
id: number; id: number;
@@ -34,6 +34,7 @@ export interface StationData {
city_id?: number; city_id?: number;
description: string; description: string;
icon?: string; icon?: string;
icon_size?: number;
id: number; id: number;
latitude: number; latitude: number;
longitude: number; longitude: number;
@@ -51,25 +52,33 @@ export interface StationPatchData {
offset_y: number; offset_y: number;
align: number; align: number;
transfers: StationTransferData; transfers: StationTransferData;
icon_size?: number;
} }
export interface SightPatchData { export interface SightPatchData {
sight_id: number; sight_id: number;
latitude: number; latitude: number;
longitude: number; longitude: number;
icon_size?: number;
} }
export interface SightData { export interface SightData {
address: string; address: string;
alt_icon?: string | null; // uuid
city: string; city: string;
city_id: number; city_id: number;
id: number; id: number;
is_default_icon?: boolean;
icon?: string | null; // uuid
icon_size?: number;
latitude: number; latitude: number;
left_article: number; left_article: number;
longitude: number; longitude: number;
name: string; name: string;
offset_x?: number;
offset_y?: number;
preview_media: number; preview_media: number;
thumbnail: string; // uuid thumbnail?: string | null; // uuid
watermark_lu: string; // uuid watermark_lu: string; // uuid
watermark_rd: string; // uuid watermark_rd: string; // uuid
} }

View File

@@ -33,6 +33,7 @@ export const MEDIA_TYPE_VALUES = {
video: 2, video: 2,
icon: 3, icon: 3,
thumbnail: 3, thumbnail: 3,
alt_icon: 3,
watermark_lu: 4, watermark_lu: 4,
watermark_rd: 4, watermark_rd: 4,
panorama: 5, panorama: 5,
@@ -42,4 +43,4 @@ export const MEDIA_TYPE_VALUES = {
export const RU_COUNTRIES = generateCountriesList("ru"); export const RU_COUNTRIES = generateCountriesList("ru");
export const EN_COUNTRIES = generateCountriesList("en"); export const EN_COUNTRIES = generateCountriesList("en");
export const ZH_COUNTRIES = generateCountriesList("zh"); export const ZH_COUNTRIES = generateCountriesList("zh");

View File

@@ -39,6 +39,8 @@ interface UploadMediaDialogProps {
afterUploadSight?: (id: string) => void; afterUploadSight?: (id: string) => void;
hardcodeType?: hardcodeType?:
| "thumbnail" | "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu" | "watermark_lu"
| "watermark_rd" | "watermark_rd"
| "image" | "image"

View File

@@ -30,7 +30,10 @@ type SightCommonInfo = {
city: string; city: string;
latitude: number; latitude: number;
longitude: number; longitude: number;
is_default_icon: boolean;
thumbnail: string | null; thumbnail: string | null;
icon: string | null;
alt_icon: string | null;
watermark_lu: string | null; watermark_lu: string | null;
watermark_rd: string | null; watermark_rd: string | null;
left_article: number; left_article: number;
@@ -47,7 +50,10 @@ const initialSightState: SightBaseInfo = {
city: "", city: "",
latitude: 0, latitude: 0,
longitude: 0, longitude: 0,
is_default_icon: false,
thumbnail: null, thumbnail: null,
icon: null,
alt_icon: null,
watermark_lu: null, watermark_lu: null,
watermark_rd: null, watermark_rd: null,
left_article: 0, left_article: 0,
@@ -486,9 +492,12 @@ class CreateSightStore {
city: this.sight.city, city: this.sight.city,
latitude: this.sight.latitude, latitude: this.sight.latitude,
longitude: this.sight.longitude, longitude: this.sight.longitude,
is_default_icon: this.sight.is_default_icon,
name: this.sight[primaryLanguage].name, name: this.sight[primaryLanguage].name,
address: this.sight[primaryLanguage].address, address: this.sight[primaryLanguage].address,
thumbnail: this.sight.thumbnail, thumbnail: this.sight.thumbnail,
icon: this.sight.icon,
alt_icon: this.sight.alt_icon,
watermark_lu: this.sight.watermark_lu, watermark_lu: this.sight.watermark_lu,
watermark_rd: this.sight.watermark_rd, watermark_rd: this.sight.watermark_rd,
left_article: finalLeftArticleId === 0 ? null : finalLeftArticleId, left_article: finalLeftArticleId === 0 ? null : finalLeftArticleId,
@@ -511,9 +520,12 @@ class CreateSightStore {
city: this.sight.city, city: this.sight.city,
latitude: this.sight.latitude, latitude: this.sight.latitude,
longitude: this.sight.longitude, longitude: this.sight.longitude,
is_default_icon: this.sight.is_default_icon,
name: this.sight[lang].name, name: this.sight[lang].name,
address: this.sight[lang].address, address: this.sight[lang].address,
thumbnail: this.sight.thumbnail, thumbnail: this.sight.thumbnail,
icon: this.sight.icon,
alt_icon: this.sight.alt_icon,
watermark_lu: this.sight.watermark_lu, watermark_lu: this.sight.watermark_lu,
watermark_rd: this.sight.watermark_rd, watermark_rd: this.sight.watermark_rd,
left_article: finalLeftArticleId === 0 ? null : finalLeftArticleId, left_article: finalLeftArticleId === 0 ? null : finalLeftArticleId,

View File

@@ -30,7 +30,10 @@ export type SightCommonInfo = {
city: string; city: string;
latitude: number; latitude: number;
longitude: number; longitude: number;
is_default_icon: boolean;
thumbnail: string | null; thumbnail: string | null;
icon: string | null;
alt_icon: string | null;
watermark_lu: string | null; watermark_lu: string | null;
watermark_rd: string | null; watermark_rd: string | null;
left_article: number; left_article: number;
@@ -51,7 +54,10 @@ class EditSightStore {
city: "", city: "",
latitude: 0, latitude: 0,
longitude: 0, longitude: 0,
is_default_icon: false,
thumbnail: null, thumbnail: null,
icon: null,
alt_icon: null,
watermark_lu: null, watermark_lu: null,
watermark_rd: null, watermark_rd: null,
left_article: 0, left_article: 0,
@@ -184,7 +190,10 @@ class EditSightStore {
city: "", city: "",
latitude: 0, latitude: 0,
longitude: 0, longitude: 0,
is_default_icon: false,
thumbnail: null, thumbnail: null,
icon: null,
alt_icon: null,
watermark_lu: null, watermark_lu: null,
watermark_rd: null, watermark_rd: null,
left_article: 0, left_article: 0,

View File

@@ -23,7 +23,10 @@ export type Sight = {
address: string; address: string;
latitude: number; latitude: number;
longitude: number; longitude: number;
is_default_icon: boolean;
thumbnail: string | null; thumbnail: string | null;
icon: string | null;
alt_icon: string | null;
watermark_lu: string | null; watermark_lu: string | null;
watermark_rd: string | null; watermark_rd: string | null;
left_article: number; left_article: number;
@@ -174,7 +177,10 @@ class SightsStore {
city_id: this.sight?.city_id, city_id: this.sight?.city_id,
latitude: this.sight?.latitude, latitude: this.sight?.latitude,
longitude: this.sight?.longitude, longitude: this.sight?.longitude,
is_default_icon: this.sight?.is_default_icon,
thumbnail: this.sight?.thumbnail, thumbnail: this.sight?.thumbnail,
icon: this.sight?.icon,
alt_icon: this.sight?.alt_icon,
watermark_lu: this.sight?.watermark_lu, watermark_lu: this.sight?.watermark_lu,
watermark_rd: this.sight?.watermark_rd, watermark_rd: this.sight?.watermark_rd,
left_article: this.sight?.left_article, left_article: this.sight?.left_article,

View File

@@ -97,7 +97,10 @@ class SnapshotStore {
city: "", city: "",
latitude: 0, latitude: 0,
longitude: 0, longitude: 0,
is_default_icon: false,
thumbnail: null, thumbnail: null,
icon: null,
alt_icon: null,
watermark_lu: null, watermark_lu: null,
watermark_rd: null, watermark_rd: null,
left_article: 0, left_article: 0,
@@ -134,7 +137,10 @@ class SnapshotStore {
city: "", city: "",
latitude: 0, latitude: 0,
longitude: 0, longitude: 0,
is_default_icon: false,
thumbnail: null, thumbnail: null,
icon: null,
alt_icon: null,
watermark_lu: null, watermark_lu: null,
watermark_rd: null, watermark_rd: null,
left_article: 0, left_article: 0,

View File

@@ -5,7 +5,13 @@ import { editSightStore } from "@shared";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
interface ImageUploadCardProps { interface ImageUploadCardProps {
title: string; title: string;
imageKey?: "thumbnail" | "watermark_lu" | "watermark_rd" | "image"; imageKey?:
| "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu"
| "watermark_rd"
| "image";
imageUrl: string | null | undefined; imageUrl: string | null | undefined;
onImageClick: () => void; onImageClick: () => void;
onDeleteImageClick: () => void; onDeleteImageClick: () => void;

View File

@@ -9,6 +9,8 @@ import {
DialogTitle, DialogTitle,
DialogContent, DialogContent,
DialogActions, DialogActions,
Checkbox,
FormControlLabel,
} from "@mui/material"; } from "@mui/material";
import { import {
BackButton, BackButton,
@@ -54,12 +56,24 @@ export const CreateInformationTab = observer(
const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null); const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
const [activeMenuType, setActiveMenuType] = useState< const [activeMenuType, setActiveMenuType] = useState<
"thumbnail" | "watermark_lu" | "watermark_rd" | "video_preview" | null | "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu"
| "watermark_rd"
| "video_preview"
| null
>(null); >(null);
const [isAddMediaOpen, setIsAddMediaOpen] = useState(false); const [isAddMediaOpen, setIsAddMediaOpen] = useState(false);
const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false); const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false);
const [hardcodeType, setHardcodeType] = useState< const [hardcodeType, setHardcodeType] = useState<
"thumbnail" | "watermark_lu" | "watermark_rd" | "video_preview" | null | "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu"
| "watermark_rd"
| "video_preview"
| null
>(null); >(null);
// НОВОЕ СОСТОЯНИЕ ДЛЯ ПРЕДУПРЕЖДАЮЩЕГО ОКНА // НОВОЕ СОСТОЯНИЕ ДЛЯ ПРЕДУПРЕЖДАЮЩЕГО ОКНА
@@ -129,7 +143,13 @@ export const CreateInformationTab = observer(
media_name?: string; media_name?: string;
media_type: number; media_type: number;
}, },
type: "thumbnail" | "watermark_lu" | "watermark_rd" | "video_preview" type:
| "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu"
| "watermark_rd"
| "video_preview"
) => { ) => {
handleChange({ handleChange({
[type]: media.id, [type]: media.id,
@@ -284,6 +304,20 @@ export const CreateInformationTab = observer(
variant="outlined" variant="outlined"
placeholder="Введите координаты в формате: широта долгота" placeholder="Введите координаты в формате: широта долгота"
/> />
<FormControlLabel
control={
<Checkbox
checked={!!sight.is_default_icon}
onChange={(e) =>
handleChange({
is_default_icon: e.target.checked,
})
}
/>
}
label="Использовать иконку по умолчанию"
/>
</Box> </Box>
<Box <Box
@@ -295,11 +329,14 @@ export const CreateInformationTab = observer(
> >
<Box <Box
sx={{ sx={{
display: "flex", display: "grid",
justifyContent: "space-around",
width: "80%", width: "80%",
gap: 2, gap: 2,
flexDirection: { xs: "column", sm: "row" }, gridTemplateColumns: {
xs: "1fr",
sm: "repeat(2, minmax(0, 1fr))",
md: "repeat(4, minmax(0, 1fr))",
},
}} }}
> >
<ImageUploadCard <ImageUploadCard
@@ -330,6 +367,66 @@ export const CreateInformationTab = observer(
}} }}
/> />
{!sight.is_default_icon && (
<>
<ImageUploadCard
title="Иконка (на карте)"
imageKey="icon"
imageUrl={isMediaIdEmpty(sight.icon) ? null : sight.icon}
onImageClick={() => {
setIsPreviewMediaOpen(true);
setMediaId(sight.icon ?? "");
}}
onDeleteImageClick={() => {
handleChange({
icon: null,
});
setActiveMenuType(null);
}}
onSelectFileClick={() => {
setActiveMenuType("icon");
setIsAddMediaOpen(true);
}}
setUploadMediaOpen={() => {
setIsUploadMediaOpen(true);
setActiveMenuType("icon");
setHardcodeType("icon");
}}
setHardcodeType={() => {
setHardcodeType("icon");
}}
/>
<ImageUploadCard
title="Иконка (при выборе)"
imageKey="alt_icon"
imageUrl={isMediaIdEmpty(sight.alt_icon) ? null : sight.alt_icon}
onImageClick={() => {
setIsPreviewMediaOpen(true);
setMediaId(sight.alt_icon ?? "");
}}
onDeleteImageClick={() => {
handleChange({
alt_icon: null,
});
setActiveMenuType(null);
}}
onSelectFileClick={() => {
setActiveMenuType("alt_icon");
setIsAddMediaOpen(true);
}}
setUploadMediaOpen={() => {
setIsUploadMediaOpen(true);
setActiveMenuType("alt_icon");
setHardcodeType("alt_icon");
}}
setHardcodeType={() => {
setHardcodeType("alt_icon");
}}
/>
</>
)}
<ImageUploadCard <ImageUploadCard
title="Водяной знак (левый верхний)" title="Водяной знак (левый верхний)"
imageKey="watermark_lu" imageKey="watermark_lu"

View File

@@ -9,6 +9,8 @@ import {
DialogTitle, DialogTitle,
DialogContent, DialogContent,
DialogActions, DialogActions,
Checkbox,
FormControlLabel,
} from "@mui/material"; } from "@mui/material";
import { import {
BackButton, BackButton,
@@ -56,12 +58,24 @@ export const InformationTab = observer(
const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null); const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
const [activeMenuType, setActiveMenuType] = useState< const [activeMenuType, setActiveMenuType] = useState<
"thumbnail" | "watermark_lu" | "watermark_rd" | "video_preview" | null | "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu"
| "watermark_rd"
| "video_preview"
| null
>(null); >(null);
const [isAddMediaOpen, setIsAddMediaOpen] = useState(false); const [isAddMediaOpen, setIsAddMediaOpen] = useState(false);
const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false); const [isVideoPreviewOpen, setIsVideoPreviewOpen] = useState(false);
const [hardcodeType, setHardcodeType] = useState< const [hardcodeType, setHardcodeType] = useState<
"thumbnail" | "watermark_lu" | "watermark_rd" | "video_preview" | null | "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu"
| "watermark_rd"
| "video_preview"
| null
>(null); >(null);
const canReadCities = authStore.canRead("cities"); const canReadCities = authStore.canRead("cities");
@@ -121,7 +135,13 @@ export const InformationTab = observer(
media_name?: string; media_name?: string;
media_type: number; media_type: number;
}, },
type: "thumbnail" | "watermark_lu" | "watermark_rd" | "video_preview" type:
| "thumbnail"
| "icon"
| "alt_icon"
| "watermark_lu"
| "watermark_rd"
| "video_preview"
) => { ) => {
handleChange( handleChange(
language as Language, language as Language,
@@ -295,6 +315,24 @@ export const InformationTab = observer(
variant="outlined" variant="outlined"
placeholder="Введите координаты в формате: широта долгота (можно использовать запятые или пробелы)" placeholder="Введите координаты в формате: широта долгота (можно использовать запятые или пробелы)"
/> />
<FormControlLabel
control={
<Checkbox
checked={!!sight.common.is_default_icon}
onChange={(e) =>
handleChange(
language as Language,
{
is_default_icon: e.target.checked,
},
true
)
}
/>
}
label="Использовать иконку по умолчанию"
/>
</Box> </Box>
<Box sx={{ width: "80%" }}> <Box sx={{ width: "80%" }}>
@@ -319,11 +357,14 @@ export const InformationTab = observer(
> >
<Box <Box
sx={{ sx={{
display: "flex", display: "grid",
justifyContent: "space-around",
width: "80%", width: "80%",
gap: 2, gap: 2,
flexDirection: { xs: "column", sm: "row" }, gridTemplateColumns: {
xs: "1fr",
sm: "repeat(2, minmax(0, 1fr))",
md: "repeat(4, minmax(0, 1fr))",
},
}} }}
> >
<ImageUploadCard <ImageUploadCard
@@ -357,6 +398,72 @@ export const InformationTab = observer(
setHardcodeType("thumbnail"); setHardcodeType("thumbnail");
}} }}
/> />
{!sight.common.is_default_icon && (
<>
<ImageUploadCard
title="Иконка (на карте)"
imageKey="icon"
imageUrl={isMediaIdEmpty(sight.common.icon) ? null : sight.common.icon}
onImageClick={() => {
setIsPreviewMediaOpen(true);
setMediaId(sight.common.icon ?? "");
}}
onDeleteImageClick={() => {
handleChange(
language as Language,
{
icon: null,
},
true
);
setActiveMenuType(null);
}}
onSelectFileClick={() => {
setActiveMenuType("icon");
setIsAddMediaOpen(true);
}}
setUploadMediaOpen={() => {
setIsUploadMediaOpen(true);
setActiveMenuType("icon");
setHardcodeType("icon");
}}
setHardcodeType={() => {
setHardcodeType("icon");
}}
/>
<ImageUploadCard
title="Иконка (при выборе)"
imageKey="alt_icon"
imageUrl={isMediaIdEmpty(sight.common.alt_icon) ? null : sight.common.alt_icon}
onImageClick={() => {
setIsPreviewMediaOpen(true);
setMediaId(sight.common.alt_icon ?? "");
}}
onDeleteImageClick={() => {
handleChange(
language as Language,
{
alt_icon: null,
},
true
);
setActiveMenuType(null);
}}
onSelectFileClick={() => {
setActiveMenuType("alt_icon");
setIsAddMediaOpen(true);
}}
setUploadMediaOpen={() => {
setIsUploadMediaOpen(true);
setActiveMenuType("alt_icon");
setHardcodeType("alt_icon");
}}
setHardcodeType={() => {
setHardcodeType("alt_icon");
}}
/>
</>
)}
<ImageUploadCard <ImageUploadCard
title="Водяной знак (левый верхний)" title="Водяной знак (левый верхний)"
imageKey="watermark_lu" imageKey="watermark_lu"

View File

@@ -34,7 +34,7 @@ export const VideoPreviewCard: React.FC<VideoPreviewCardProps> = ({
}; };
const handleFileInputChange = async ( const handleFileInputChange = async (
event: React.ChangeEvent<HTMLInputElement> event: React.ChangeEvent<HTMLInputElement>,
) => { ) => {
const file = event.target.files?.[0]; const file = event.target.files?.[0];
if (file) { if (file) {
@@ -87,7 +87,7 @@ export const VideoPreviewCard: React.FC<VideoPreviewCardProps> = ({
gap: 1, gap: 1,
flex: 1, flex: 1,
minWidth: 150, minWidth: 150,
width: "min-content", width: "100%",
mx: "auto", mx: "auto",
}} }}
className={className} className={className}