feat: add markdown, lang atribute, button_text

This commit is contained in:
2026-05-25 12:46:53 +03:00
parent 55cdea17ea
commit d4c5db61ea
29 changed files with 281 additions and 117 deletions

View File

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

View File

@@ -55,6 +55,7 @@ export type GetRouteResponse = {
center_latitude: number; center_latitude: number;
center_longitude: number; center_longitude: number;
governor_appeal: number; governor_appeal: number;
button_text?: string;
id: number; id: number;
path: [number, number][]; path: [number, number][];
rotate: number; rotate: number;

View File

@@ -20,6 +20,8 @@ function useThumbSync(scrollableRef: React.RefObject<HTMLDivElement | null>) {
top: 0, top: 0,
hasScroll: false, hasScroll: false,
}); });
const [visible, setVisible] = useState(false);
const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const rafRef = useRef<number | null>(null); const rafRef = useRef<number | null>(null);
const update = useCallback(() => { const update = useCallback(() => {
@@ -32,7 +34,7 @@ function useThumbSync(scrollableRef: React.RefObject<HTMLDivElement | null>) {
const th = ch; const th = ch;
if (sh <= ch) { if (sh <= ch) {
setState({ height: th, top: 0, hasScroll: false }); setState((prev) => ({ ...prev, hasScroll: false }));
return; return;
} }
@@ -68,7 +70,24 @@ function useThumbSync(scrollableRef: React.RefObject<HTMLDivElement | null>) {
}; };
}, [update]); }, [update]);
return state; useEffect(() => {
if (state.hasScroll) {
if (hideTimerRef.current) {
clearTimeout(hideTimerRef.current);
hideTimerRef.current = null;
}
setVisible(true);
} else {
hideTimerRef.current = setTimeout(() => {
setVisible(false);
}, 200);
}
return () => {
if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
};
}, [state.hasScroll]);
return { ...state, visible };
} }
export const TouchableLayout = forwardRef<HTMLDivElement, TouchableLayoutProps>( export const TouchableLayout = forwardRef<HTMLDivElement, TouchableLayoutProps>(
@@ -251,17 +270,21 @@ export const TouchableLayout = forwardRef<HTMLDivElement, TouchableLayoutProps>(
<div ref={scrollableRef} className="scrollable"> <div ref={scrollableRef} className="scrollable">
{children} {children}
</div> </div>
{thumb.hasScroll && ( <div
<div ref={trackRef} className="custom-scrollbar-track"> ref={trackRef}
className="custom-scrollbar-track"
style={{ opacity: thumb.hasScroll ? 1 : 0 }}
>
{thumb.visible && (
<div <div
ref={thumbRef} ref={thumbRef}
className="custom-scrollbar-thumb" className="custom-scrollbar-thumb"
style={{ height: thumb.height, top: thumb.top }} style={{ height: thumb.height, top: thumb.top }}
/> />
</div>
)} )}
</div> </div>
</div> </div>
</div>
); );
}, },
); );

View File

@@ -5,6 +5,8 @@ import { useGeolocationStore } from "../../stores";
import "../../styles/LeftWidget.css"; import "../../styles/LeftWidget.css";
import { apiStore } from "../../api/ApiStore/store"; import { apiStore } from "../../api/ApiStore/store";
import { apiBaseURL } from "../../api/apiConfig"; import { apiBaseURL } from "../../api/apiConfig";
import { ReactMarkdownComponent } from "../ReactMarkdown";
import { TouchableLayout } from "../TouchableLayout";
const LeftWidget = observer( const LeftWidget = observer(
({ selectedSightId, onClose, isVisible, sightTop }) => { ({ selectedSightId, onClose, isVisible, sightTop }) => {
@@ -15,8 +17,7 @@ const LeftWidget = observer(
const [isImageLoaded, setIsImageLoaded] = useState(false); const [isImageLoaded, setIsImageLoaded] = useState(false);
const [widgetHeight, setWidgetHeight] = useState(0); const [widgetHeight, setWidgetHeight] = useState(0);
const textRef = useRef(null); const layoutRef = useRef(null);
const activeTouch = useRef(null);
const widgetRef = useRef(null); const widgetRef = useRef(null);
const store = useGeolocationStore(); const store = useGeolocationStore();
@@ -37,64 +38,10 @@ const LeftWidget = observer(
}, [selectedSightData, isImageLoaded, isVisible, isLoading, error]); }, [selectedSightData, isImageLoaded, isVisible, isLoading, error]);
useEffect(() => { useEffect(() => {
const scrollContainer = textRef.current; if (layoutRef.current) {
if (!scrollContainer) return; const scrollable = layoutRef.current.querySelector(".scrollable");
if (scrollable) scrollable.scrollTop = 0;
const handleTouchStart = (e) => {
e.stopPropagation();
if (e.touches.length === 1) {
activeTouch.current = {
identifier: e.touches[0].identifier,
lastY: e.touches[0].clientY,
};
} }
};
const handleTouchMove = (e) => {
e.preventDefault();
if (activeTouch.current) {
for (const touch of e.changedTouches) {
if (touch.identifier === activeTouch.current.identifier) {
const deltaY = touch.clientY - activeTouch.current.lastY;
scrollContainer.scrollTop -= deltaY;
activeTouch.current.lastY = touch.clientY;
break;
}
}
}
};
const handleTouchEnd = (e) => {
for (const touch of e.changedTouches) {
if (
activeTouch.current &&
touch.identifier === activeTouch.current.identifier
) {
activeTouch.current = null;
break;
}
}
};
scrollContainer.addEventListener("touchstart", handleTouchStart, {
passive: true,
});
scrollContainer.addEventListener("touchmove", handleTouchMove, {
passive: false,
});
scrollContainer.addEventListener("touchend", handleTouchEnd, {
passive: true,
});
scrollContainer.addEventListener("touchcancel", handleTouchEnd, {
passive: true,
});
return () => {
scrollContainer.removeEventListener("touchstart", handleTouchStart);
scrollContainer.removeEventListener("touchmove", handleTouchMove);
scrollContainer.removeEventListener("touchend", handleTouchEnd);
scrollContainer.removeEventListener("touchcancel", handleTouchEnd);
};
}, [selectedSightData]); }, [selectedSightData]);
useEffect(() => { useEffect(() => {
@@ -238,9 +185,11 @@ const LeftWidget = observer(
<div className="left-widget-address"> <div className="left-widget-address">
{selectedSightData.address} {selectedSightData.address}
</div> </div>
<div ref={textRef} className="left-widget-text"> <TouchableLayout ref={layoutRef} className="left-widget-text-scroll">
{selectedSightData.text} <div className="left-widget-text">
<ReactMarkdownComponent value={selectedSightData.text} />
</div> </div>
</TouchableLayout>
</div> </div>
</> </>
) : (isVisible || selectedSightData) && !isLoading ? ( ) : (isVisible || selectedSightData) && !isLoading ? (

View File

@@ -447,7 +447,9 @@ const SideMenu = observer(({ onMenuToggle }) => {
}} }}
className="appeal-button" className="appeal-button"
> >
{selectedLanguage == "ru" {route?.button_text
? route.button_text
: selectedLanguage == "ru"
? "Обращение губернатора" ? "Обращение губернатора"
: selectedLanguage == "zh" : selectedLanguage == "zh"
? "州长致辞" ? "州长致辞"

View File

@@ -84,6 +84,12 @@ const SightItem = ({
return () => window.removeEventListener("resize", checkWidth); return () => window.removeEventListener("resize", checkWidth);
}, [sightName]); }, [sightName]);
useEffect(() => {
if (localSelectedSightId !== sight.id) {
setIsExpanded(false);
}
}, [localSelectedSightId, sight.id]);
const handleClick = (e) => { const handleClick = (e) => {
const newExpanded = !isExpanded; const newExpanded = !isExpanded;
setIsExpanded(newExpanded); setIsExpanded(newExpanded);
@@ -96,15 +102,19 @@ const SightItem = ({
const stations = sightStationsCache.get(cacheKey) || []; const stations = sightStationsCache.get(cacheKey) || [];
return ( return (
<div> <div
className={
localSelectedSightId === sight.id
? "side-menu-sight-selected-wrapper"
: ""
}
>
<div <div
ref={containerRef} ref={containerRef}
id={`sight-${sight.id}`} id={`sight-${sight.id}`}
onPointerDown={(e) => handlePointerDown(e, sight.id)} onPointerDown={(e) => handlePointerDown(e, sight.id)}
onPointerUp={(e) => handlePointerUp(e, sight.id, handleClick)} onPointerUp={(e) => handlePointerUp(e, sight.id, handleClick)}
className={`side-menu-sight pointer ${ className="side-menu-sight pointer"
localSelectedSightId === sight.id ? "selected" : ""
}`}
> >
<span ref={textRef} className={shouldAnimate ? "marquee-text" : ""}> <span ref={textRef} className={shouldAnimate ? "marquee-text" : ""}>
{sightName} {sightName}

View File

@@ -1,6 +1,7 @@
import { useRef, useEffect } from "react"; import { useRef, useEffect } from "react";
import "../../styles/AppealWidget.css"; import "../../styles/AppealWidget.css";
import { TouchableLayout } from "../TouchableLayout"; import { TouchableLayout } from "../TouchableLayout";
import { ReactMarkdownComponent } from "../ReactMarkdown";
function AppealWidget({ function AppealWidget({
widgetImgPath, widgetImgPath,
@@ -38,7 +39,9 @@ function AppealWidget({
ref={layoutRef} ref={layoutRef}
className="dynamic-widget-text-scroll" className="dynamic-widget-text-scroll"
> >
<div className="dynamic-widget-text">{widgetText}</div> <div className="dynamic-widget-text">
<ReactMarkdownComponent value={widgetText} />
</div>
</TouchableLayout> </TouchableLayout>
</div> </div>
); );

View File

@@ -288,7 +288,7 @@ export const ThreeView: React.FC<ThreeViewProps> = ({
{!isReady && ( {!isReady && (
<div style={{ <div style={{
position: "absolute", inset: 0, position: "absolute", inset: 0,
backgroundColor: BACKGROUND_COLOR, backgroundColor: `#${BACKGROUND_COLOR.toString(16).padStart(6, "0")}`,
zIndex: 1, zIndex: 1,
}} /> }} />
)} )}

View File

@@ -14,9 +14,9 @@
.side-menu-sights-block { .side-menu-sights-block {
height: calc(60%); height: calc(60%);
overflow-y: scroll; overflow-y: scroll;
margin-left: 20px; margin-left: 0;
margin-top: 8px; margin-top: 8px;
margin-right: 5px; padding-right: 5px;
touch-action: none; /* Отключаем стандартные действия */ touch-action: none; /* Отключаем стандартные действия */
overscroll-behavior: contain; /* Предотвращаем прокрутку родительских элементов */ overscroll-behavior: contain; /* Предотвращаем прокрутку родительских элементов */
} }

View File

@@ -66,8 +66,63 @@
.dynamic-widget-text { .dynamic-widget-text {
font-size: 14px; font-size: 16px;
font-weight: 400; font-weight: 300;
line-height: 190%; line-height: 135%;
padding-right: 5px; padding-right: 5px;
} }
.dynamic-widget-text .react-markdown-container {
font-size: 16px;
line-height: 135%;
font-weight: 300;
}
.dynamic-widget-text .react-markdown-container p {
font-size: 16px;
line-height: 135%;
margin-bottom: 8px;
}
.dynamic-widget-text .react-markdown-container p:last-child {
margin-bottom: 0;
}
.dynamic-widget-text .react-markdown-container h1,
.dynamic-widget-text .react-markdown-container h2,
.dynamic-widget-text .react-markdown-container h3,
.dynamic-widget-text .react-markdown-container h4,
.dynamic-widget-text .react-markdown-container h5,
.dynamic-widget-text .react-markdown-container h6 {
font-size: 18px;
margin-top: 10px;
margin-bottom: 4px;
font-weight: 600;
}
.dynamic-widget-text .react-markdown-container ul,
.dynamic-widget-text .react-markdown-container ol {
margin-bottom: 8px;
padding-left: 20px;
}
.dynamic-widget-text .react-markdown-container li {
margin-bottom: 4px;
}
.dynamic-widget-text .react-markdown-container blockquote {
margin-top: 8px;
margin-bottom: 8px;
padding-left: 12px;
border-left: 3px solid rgba(255, 255, 255, 0.4);
}
.dynamic-widget-text .react-markdown-container img {
max-width: 100%;
border-radius: 6px;
}
.dynamic-widget-text .react-markdown-container a {
color: rgba(255, 255, 255, 0.9);
text-decoration: underline;
}

View File

@@ -67,17 +67,78 @@
line-height: 150%; line-height: 150%;
} }
.left-widget-text { .left-widget-text-scroll.scrollable-container {
margin-top: 15px; margin-top: 15px;
overflow: hidden;
width: 100%;
}
.left-widget-text-scroll .scrollable-viewport {
max-height: 200px;
}
.left-widget-text {
color: #fff; color: #fff;
font-family: "Roboto"; font-family: "Roboto";
font-size: 16px; font-size: 16px;
font-weight: 300; font-weight: 300;
line-height: 135%; line-height: 135%;
max-height: 200px; /* Пример ограничения высоты */ padding-right: 3px;
overflow-y: auto; }
touch-action: none;
overscroll-behavior: contain; .left-widget-text .react-markdown-container {
font-size: 16px;
line-height: 135%;
font-weight: 300;
}
.left-widget-text .react-markdown-container p {
font-size: 16px;
line-height: 135%;
margin-bottom: 8px;
}
.left-widget-text .react-markdown-container p:last-child {
margin-bottom: 0;
}
.left-widget-text .react-markdown-container h1,
.left-widget-text .react-markdown-container h2,
.left-widget-text .react-markdown-container h3,
.left-widget-text .react-markdown-container h4,
.left-widget-text .react-markdown-container h5,
.left-widget-text .react-markdown-container h6 {
font-size: 18px;
margin-top: 10px;
margin-bottom: 4px;
font-weight: 600;
}
.left-widget-text .react-markdown-container ul,
.left-widget-text .react-markdown-container ol {
margin-bottom: 8px;
padding-left: 20px;
}
.left-widget-text .react-markdown-container li {
margin-bottom: 4px;
}
.left-widget-text .react-markdown-container blockquote {
margin-top: 8px;
margin-bottom: 8px;
padding-left: 12px;
border-left: 3px solid rgba(255, 255, 255, 0.4);
}
.left-widget-text .react-markdown-container img {
max-width: 100%;
border-radius: 6px;
}
.left-widget-text .react-markdown-container a {
color: rgba(255, 255, 255, 0.9);
text-decoration: underline;
} }
.left-widget-image { .left-widget-image {

View File

@@ -1,8 +1,16 @@
@keyframes pulse-chevron { @keyframes pulse-chevron {
0% { transform: rotate(var(--r, 0deg)) translateY(0px) scale(1); } 0% {
40% { transform: rotate(var(--r, 0deg)) translateY(-4px) scale(1.12); } transform: rotate(var(--r, 0deg)) translateY(0px) scale(1);
60% { transform: rotate(var(--r, 0deg)) translateY(-5px) scale(1.14); } }
100% { transform: rotate(var(--r, 0deg)) translateY(0px) scale(1); } 40% {
transform: rotate(var(--r, 0deg)) translateY(-4px) scale(1.12);
}
60% {
transform: rotate(var(--r, 0deg)) translateY(-5px) scale(1.14);
}
100% {
transform: rotate(var(--r, 0deg)) translateY(0px) scale(1);
}
} }
.chevron-svg { .chevron-svg {
@@ -39,7 +47,7 @@
rgba(255, 255, 255, 0) 8.71%, rgba(255, 255, 255, 0) 8.71%,
rgba(255, 255, 255, 0.16) 69.69% rgba(255, 255, 255, 0.16) 69.69%
), ),
var(--carrier-right, #806C59); var(--carrier-right, #806c59);
color: white; color: white;
max-height: 68px; max-height: 68px;
@@ -87,7 +95,7 @@
background-color: color-mix( background-color: color-mix(
in srgb, in srgb,
var(--carrier-right, #806C59) 80%, var(--carrier-right, #806c59) 80%,
black black
); );
} }
@@ -220,7 +228,7 @@
rgba(255, 255, 255, 0) 8.71%, rgba(255, 255, 255, 0) 8.71%,
rgba(255, 255, 255, 0.16) 69.69% rgba(255, 255, 255, 0.16) 69.69%
), ),
var(--carrier-right, #806C59); var(--carrier-right, #806c59);
max-height: calc(100vh - 128px); max-height: calc(100vh - 128px);
} }
@@ -340,7 +348,7 @@
background: linear-gradient( background: linear-gradient(
to right, to right,
transparent 35%, transparent 35%,
color-mix(in srgb, var(--carrier-right, #806C59) 80%, black) 50%, color-mix(in srgb, var(--carrier-right, #806c59) 80%, black) 50%,
transparent 65% transparent 65%
); );
border-radius: 3px; border-radius: 3px;
@@ -380,17 +388,26 @@
.sight-frame-menu-fade.left { .sight-frame-menu-fade.left {
left: 0; left: 0;
background: linear-gradient(to right, rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95), transparent); background: linear-gradient(
to right,
rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95),
transparent
);
border-radius: 0 0 0 10px; border-radius: 0 0 0 10px;
} }
.sight-frame-menu-fade.right { .sight-frame-menu-fade.right {
right: 0; right: 0;
background: linear-gradient(to left, rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95), transparent); background: linear-gradient(
to left,
rgba(var(--carrier-right-menu-rgb, 179, 165, 152), 0.95),
transparent
);
border-radius: 0 0 10px 0; border-radius: 0 0 10px 0;
} }
.sight-frame-menu { .sight-frame-menu {
z-index: 10000;
position: relative; position: relative;
padding: 7px 60px; padding: 7px 60px;
width: 100%; width: 100%;
@@ -825,7 +842,7 @@
border-radius: 32px; border-radius: 32px;
right: 20px; right: 20px;
bottom: 20px; bottom: 20px;
background: var(--carrier-right, #806C59); background: var(--carrier-right, #806c59);
z-index: 9999; z-index: 9999;
display: flex; display: flex;
} }

View File

@@ -35,6 +35,8 @@
font-size: 16px; font-size: 16px;
margin-top: 120px; margin-top: 120px;
font-weight: 500; font-weight: 500;
width: 220px;
text-align: center;
} }
.side-menu-buttons { .side-menu-buttons {
@@ -227,12 +229,10 @@
.side-menu-sights-block { .side-menu-sights-block {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
margin-left: 20px;
margin-top: 8px; margin-top: 8px;
touch-action: none; touch-action: none;
overscroll-behavior: contain; overscroll-behavior: contain;
width: auto; width: 100%;
max-width: calc(100% - 20px);
box-sizing: border-box; box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
@@ -240,7 +240,6 @@
.side-menu-sight { .side-menu-sight {
padding-bottom: 2px; padding-bottom: 2px;
margin-right: 20px;
margin-bottom: 6px; margin-bottom: 6px;
margin-top: 6px; margin-top: 6px;
border-bottom: 1px solid border-bottom: 1px solid
@@ -254,6 +253,12 @@
position: relative; position: relative;
} }
.side-menu-sight-selected-wrapper {
background: rgba(0, 0, 0, 0.2);
margin-left: -20px;
padding-left: 20px;
}
.side-menu-sight > span { .side-menu-sight > span {
display: inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;

View File

@@ -43,6 +43,7 @@
position: relative; position: relative;
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
border-radius: 3px; border-radius: 3px;
transition: opacity 0.2s ease;
} }
.custom-scrollbar-thumb { .custom-scrollbar-thumb {
@@ -67,6 +68,7 @@
.side-menu-sights-block .scrollable { .side-menu-sights-block .scrollable {
height: 100%; height: 100%;
padding-left: 20px;
} }
.list-of-sights-content .scrollable-viewport { .list-of-sights-content .scrollable-viewport {

View File

@@ -108,7 +108,7 @@ export const ArticleListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -162,7 +162,7 @@ export const CarrierListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -159,7 +159,7 @@ export const CityListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -111,7 +111,7 @@ export const CountryListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -113,7 +113,7 @@ export const MediaListPage = observer(() => {
return ( return (
<> <>
<div className="w-full"> <div className="w-full">
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -50,6 +50,7 @@ export const RouteCreatePage = observer(() => {
const [routeCoords, setRouteCoords] = useState(""); const [routeCoords, setRouteCoords] = useState("");
const [govRouteNumber, setGovRouteNumber] = useState(""); const [govRouteNumber, setGovRouteNumber] = useState("");
const [governorAppeal, setGovernorAppeal] = useState<string>(""); const [governorAppeal, setGovernorAppeal] = useState<string>("");
const [buttonText, setButtonText] = useState("");
const [direction, setDirection] = useState("backward"); const [direction, setDirection] = useState("backward");
const [scaleMin, setScaleMin] = useState("10"); const [scaleMin, setScaleMin] = useState("10");
const [scaleMax, setScaleMax] = useState("100"); const [scaleMax, setScaleMax] = useState("100");
@@ -292,6 +293,10 @@ export const RouteCreatePage = observer(() => {
newRoute.governor_appeal = governor_appeal; newRoute.governor_appeal = governor_appeal;
} }
if (buttonText.trim()) {
newRoute.button_text = buttonText.trim();
}
const newId = await routeStore.createRoute(newRoute); const newId = await routeStore.createRoute(newRoute);
toast.success("Маршрут успешно создан"); toast.success("Маршрут успешно создан");
navigate(`/route/${newId}/edit`); navigate(`/route/${newId}/edit`);
@@ -407,6 +412,18 @@ export const RouteCreatePage = observer(() => {
onChange={(e) => setGovRouteNumber(e.target.value)} onChange={(e) => setGovRouteNumber(e.target.value)}
/> />
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Текст кнопки обращения
</Typography>
<TextField
value={buttonText}
onChange={(e) => setButtonText(e.target.value)}
placeholder="Обращение губернатора"
fullWidth
size="small"
helperText="Если пусто, будет использован текст по умолчанию"
/>
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}> <Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Обращение к пассажирам Обращение к пассажирам
</Typography> </Typography>

View File

@@ -566,6 +566,22 @@ export const RouteEditPage = observer(() => {
}} }}
/> />
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Текст кнопки обращения
</Typography>
<TextField
value={editRouteData.button_text || ""}
onChange={(e) =>
routeStore.setEditRouteData({
button_text: e.target.value,
})
}
placeholder="Обращение губернатора"
fullWidth
size="small"
helperText="Если пусто, будет использован текст по умолчанию"
/>
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}> <Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Обращение к пассажирам Обращение к пассажирам
</Typography> </Typography>

View File

@@ -270,7 +270,7 @@ export const RouteListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -6,6 +6,7 @@ export interface RouteData {
icon_size?: number; icon_size?: number;
font_size: number; font_size: number;
governor_appeal: number; governor_appeal: number;
button_text?: string;
id: number; id: number;
path: [number, number][]; path: [number, number][];
rotate: number; rotate: number;

View File

@@ -181,7 +181,7 @@ export const SightListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -316,7 +316,7 @@ export const SnapshotListPage = observer(() => {
</Alert> </Alert>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -225,7 +225,7 @@ export const StationListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -147,7 +147,7 @@ export const UserListPage = observer(() => {
</div> </div>
)} )}
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -173,7 +173,7 @@ export const VehicleListPage = observer(() => {
/> />
</div> </div>
{rows.length > 0 && ( {(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} /> <SearchInput value={searchQuery} onChange={setSearchQuery} />
)} )}

View File

@@ -13,6 +13,7 @@ export type Route = {
center_latitude: number; center_latitude: number;
center_longitude: number; center_longitude: number;
governor_appeal: number; governor_appeal: number;
button_text?: string;
id: number; id: number;
icon: string; icon: string;
path: number[][]; path: number[][];
@@ -143,6 +144,7 @@ class RouteStore {
center_latitude: "", center_latitude: "",
center_longitude: "", center_longitude: "",
governor_appeal: 0, governor_appeal: 0,
button_text: "" as string | undefined,
id: 0, id: 0,
icon: "", icon: "",
path: [] as number[][], path: [] as number[][],