feat: fix sight edit
This commit is contained in:
@ -1,5 +1,10 @@
|
|||||||
import { Box, Tab, Tabs } from "@mui/material";
|
import { Box, Tab, Tabs } from "@mui/material";
|
||||||
import { articlesStore, cityStore, languageStore } from "@shared";
|
import {
|
||||||
|
articlesStore,
|
||||||
|
cityStore,
|
||||||
|
createSightStore,
|
||||||
|
languageStore,
|
||||||
|
} from "@shared";
|
||||||
import {
|
import {
|
||||||
CreateInformationTab,
|
CreateInformationTab,
|
||||||
CreateLeftTab,
|
CreateLeftTab,
|
||||||
@ -22,13 +27,15 @@ export const CreateSightPage = observer(() => {
|
|||||||
const [value, setValue] = useState(0);
|
const [value, setValue] = useState(0);
|
||||||
const { getCities } = cityStore;
|
const { getCities } = cityStore;
|
||||||
const { getArticles } = articlesStore;
|
const { getArticles } = articlesStore;
|
||||||
|
const { needLeaveAgree } = createSightStore;
|
||||||
|
|
||||||
const handleChange = (_: React.SyntheticEvent, newValue: number) => {
|
const handleChange = (_: React.SyntheticEvent, newValue: number) => {
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
let blocker = useBlocker(
|
let blocker = useBlocker(
|
||||||
({ currentLocation, nextLocation }) =>
|
({ currentLocation, nextLocation }) =>
|
||||||
true && currentLocation.pathname !== nextLocation.pathname
|
needLeaveAgree && currentLocation.pathname !== nextLocation.pathname
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -20,7 +20,7 @@ function a11yProps(index: number) {
|
|||||||
|
|
||||||
export const EditSightPage = observer(() => {
|
export const EditSightPage = observer(() => {
|
||||||
const [value, setValue] = useState(0);
|
const [value, setValue] = useState(0);
|
||||||
const { sight, getSightInfo } = editSightStore;
|
const { sight, getSightInfo, needLeaveAgree } = editSightStore;
|
||||||
const { getArticles } = articlesStore;
|
const { getArticles } = articlesStore;
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
@ -28,7 +28,7 @@ export const EditSightPage = observer(() => {
|
|||||||
|
|
||||||
let blocker = useBlocker(
|
let blocker = useBlocker(
|
||||||
({ currentLocation, nextLocation }) =>
|
({ currentLocation, nextLocation }) =>
|
||||||
true && currentLocation.pathname !== nextLocation.pathname
|
needLeaveAgree && currentLocation.pathname !== nextLocation.pathname
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = (_: React.SyntheticEvent, newValue: number) => {
|
const handleChange = (_: React.SyntheticEvent, newValue: number) => {
|
||||||
|
@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Pencil, Trash2 } from "lucide-react";
|
import { Pencil, Trash2 } from "lucide-react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { DeleteModal, LanguageSwitcher } from "@widgets";
|
import { CreateButton, DeleteModal, LanguageSwitcher } from "@widgets";
|
||||||
|
|
||||||
export const SightListPage = observer(() => {
|
export const SightListPage = observer(() => {
|
||||||
const { sights, getSights, deleteListSight } = sightsStore;
|
const { sights, getSights, deleteListSight } = sightsStore;
|
||||||
@ -69,6 +69,13 @@ export const SightListPage = observer(() => {
|
|||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
<div className="flex justify-between items-center mb-10">
|
||||||
|
<h1 className="text-2xl">Достопримечательности</h1>
|
||||||
|
<CreateButton
|
||||||
|
label="Создать достопримечательность"
|
||||||
|
path="/sight/create"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -202,87 +202,100 @@ export const SelectArticleModal = observer(
|
|||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
</Paper>
|
</Paper>
|
||||||
<Paper
|
<Box className="max-h-[80%] overflow-y-scroll mx-auto rounded-[10px]">
|
||||||
elevation={3}
|
<Paper
|
||||||
sx={{
|
elevation={3}
|
||||||
width: "100%",
|
|
||||||
minWidth: 320,
|
|
||||||
maxWidth: 310,
|
|
||||||
|
|
||||||
background:
|
|
||||||
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
|
||||||
|
|
||||||
padding: 0,
|
|
||||||
margin: "0px auto",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: 175,
|
minWidth: 320,
|
||||||
display: "flex",
|
maxWidth: 310,
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
padding: "3px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{articlesStore.articleMedia ? (
|
|
||||||
<MediaViewer
|
|
||||||
media={{
|
|
||||||
id: articlesStore.articleMedia.id,
|
|
||||||
media_type: articlesStore.articleMedia.media_type,
|
|
||||||
filename: articlesStore.articleMedia.filename,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ImagePlus size={48} color="white" />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
background:
|
background:
|
||||||
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
||||||
color: "white",
|
|
||||||
margin: "5px 0px 5px 0px",
|
padding: 0,
|
||||||
|
margin: "0px auto",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: 1,
|
|
||||||
padding: 1,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
|
||||||
variant="h5"
|
|
||||||
component="h2"
|
|
||||||
sx={{
|
|
||||||
wordBreak: "break-word",
|
|
||||||
fontSize: "24px",
|
|
||||||
fontWeight: 700,
|
|
||||||
lineHeight: "120%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{articlesStore.articleData?.heading || "Название cтатьи"}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{articlesStore.articleData?.body && (
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
padding: 1,
|
overflow: "hidden",
|
||||||
maxHeight: "200px",
|
width: "100%",
|
||||||
overflowY: "scroll",
|
minHeight: 100,
|
||||||
background:
|
padding: "3px",
|
||||||
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
display: "flex",
|
||||||
|
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
|
||||||
|
"& img": {
|
||||||
|
borderTopLeftRadius: "10px",
|
||||||
|
borderTopRightRadius: "10px",
|
||||||
|
width: "100%",
|
||||||
|
height: "auto",
|
||||||
|
objectFit: "contain",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ReactMarkdownComponent
|
{articlesStore.articleMedia ? (
|
||||||
value={articlesStore.articleData?.body || "Описание"}
|
<MediaViewer
|
||||||
/>
|
media={{
|
||||||
|
id: articlesStore.articleMedia.id,
|
||||||
|
media_type: articlesStore.articleMedia.media_type,
|
||||||
|
filename: articlesStore.articleMedia.filename,
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ImagePlus size={48} color="white" />
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
|
||||||
</Paper>
|
<Box
|
||||||
|
sx={{
|
||||||
|
background:
|
||||||
|
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
||||||
|
color: "white",
|
||||||
|
margin: "5px 0px 5px 0px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
padding: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="h5"
|
||||||
|
component="h2"
|
||||||
|
sx={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
fontSize: "24px",
|
||||||
|
fontWeight: 700,
|
||||||
|
lineHeight: "120%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{articlesStore.articleData?.heading || "Название cтатьи"}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{articlesStore.articleData?.body && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
padding: 1,
|
||||||
|
maxHeight: "200px",
|
||||||
|
overflowY: "scroll",
|
||||||
|
background:
|
||||||
|
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ReactMarkdownComponent
|
||||||
|
value={articlesStore.articleData?.body || "Описание"}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions sx={{ p: 2 }}>
|
<DialogActions sx={{ p: 2 }}>
|
||||||
<Button onClick={onClose}>Отмена</Button>
|
<Button onClick={onClose}>Отмена</Button>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { MEDIA_TYPE_LABELS, editSightStore } from "@shared";
|
import { MEDIA_TYPE_LABELS, MEDIA_TYPE_VALUES, editSightStore } from "@shared";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
@ -31,6 +31,7 @@ interface UploadMediaDialogProps {
|
|||||||
media_type: number;
|
media_type: number;
|
||||||
}) => void;
|
}) => void;
|
||||||
afterUploadSight?: (id: string) => void;
|
afterUploadSight?: (id: string) => void;
|
||||||
|
hardcodeType?: "thumbnail" | "watermark_lu" | "watermark_rd" | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UploadMediaDialog = observer(
|
export const UploadMediaDialog = observer(
|
||||||
@ -39,6 +40,7 @@ export const UploadMediaDialog = observer(
|
|||||||
onClose,
|
onClose,
|
||||||
afterUpload,
|
afterUpload,
|
||||||
afterUploadSight,
|
afterUploadSight,
|
||||||
|
hardcodeType,
|
||||||
}: UploadMediaDialogProps) => {
|
}: UploadMediaDialogProps) => {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@ -165,10 +167,12 @@ export const UploadMediaDialog = observer(
|
|||||||
<FormControl fullWidth sx={{ width: "50%" }}>
|
<FormControl fullWidth sx={{ width: "50%" }}>
|
||||||
<InputLabel>Тип медиа</InputLabel>
|
<InputLabel>Тип медиа</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={mediaType}
|
value={
|
||||||
|
hardcodeType ? MEDIA_TYPE_VALUES[hardcodeType] : mediaType
|
||||||
|
}
|
||||||
|
disabled={!!hardcodeType}
|
||||||
label="Тип медиа"
|
label="Тип медиа"
|
||||||
onChange={(e) => setMediaType(Number(e.target.value))}
|
onChange={(e) => setMediaType(Number(e.target.value))}
|
||||||
disabled={isLoading}
|
|
||||||
>
|
>
|
||||||
{availableMediaTypes.map((type) => (
|
{availableMediaTypes.map((type) => (
|
||||||
<MenuItem key={type} value={type}>
|
<MenuItem key={type} value={type}>
|
||||||
|
@ -98,18 +98,22 @@ class CreateSightStore {
|
|||||||
heading: "New Heading (EN)",
|
heading: "New Heading (EN)",
|
||||||
body: "New Text (EN)",
|
body: "New Text (EN)",
|
||||||
};
|
};
|
||||||
const articleZhData = { heading: "新标题 (ZH)", body: "新文本 (ZH)" };
|
const articleZhData = {
|
||||||
|
heading: "Новый заголовок (ZH)",
|
||||||
|
body: "Новый текст (ZH)",
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const articleRes = await languageInstance("ru").post(
|
this.needLeaveAgree = true;
|
||||||
"/article",
|
const articleRes = await authInstance.post("/article", {
|
||||||
articleRuData
|
translation: {
|
||||||
);
|
ru: articleRuData,
|
||||||
|
en: articleEnData,
|
||||||
|
zh: articleZhData,
|
||||||
|
},
|
||||||
|
});
|
||||||
const { id } = articleRes.data; // New article's ID
|
const { id } = articleRes.data; // New article's ID
|
||||||
|
|
||||||
await languageInstance("en").patch(`/article/${id}`, articleEnData);
|
|
||||||
await languageInstance("zh").patch(`/article/${id}`, articleZhData);
|
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
const newArticleEntry = { id, media: [] };
|
const newArticleEntry = { id, media: [] };
|
||||||
this.sight.ru.right.push({ ...newArticleEntry, ...articleRuData });
|
this.sight.ru.right.push({ ...newArticleEntry, ...articleRuData });
|
||||||
@ -376,6 +380,7 @@ class CreateSightStore {
|
|||||||
|
|
||||||
// --- General Store Methods ---
|
// --- General Store Methods ---
|
||||||
clearCreateSight = () => {
|
clearCreateSight = () => {
|
||||||
|
this.needLeaveAgree = false;
|
||||||
this.sight = JSON.parse(JSON.stringify(initialSightState)); // Reset to initial
|
this.sight = JSON.parse(JSON.stringify(initialSightState)); // Reset to initial
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -383,6 +388,7 @@ class CreateSightStore {
|
|||||||
content: Partial<SightLanguageInfo | SightCommonInfo>, // Corrected types
|
content: Partial<SightLanguageInfo | SightCommonInfo>, // Corrected types
|
||||||
language?: Language
|
language?: Language
|
||||||
) => {
|
) => {
|
||||||
|
this.needLeaveAgree = true;
|
||||||
if (language) {
|
if (language) {
|
||||||
this.sight[language] = { ...this.sight[language], ...content };
|
this.sight[language] = { ...this.sight[language], ...content };
|
||||||
} else {
|
} else {
|
||||||
@ -587,10 +593,31 @@ class CreateSightStore {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
updateRightArticles = async (articles: any[], language: Language) => {
|
updateRightArticles = async (articles: any[]) => {
|
||||||
runInAction(() => {
|
const articlesIds = articles.map((article) => article.id);
|
||||||
this.sight[language].right = articles;
|
|
||||||
});
|
const sortArticles = (existing: any[]) => {
|
||||||
|
const articleMap = new Map(
|
||||||
|
existing.map((article) => [article.id, article])
|
||||||
|
);
|
||||||
|
return articlesIds
|
||||||
|
.map((id) => articleMap.get(id))
|
||||||
|
.filter(
|
||||||
|
(article): article is (typeof existing)[number] =>
|
||||||
|
article !== undefined
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sight.ru.right = sortArticles(this.sight.ru.right);
|
||||||
|
this.sight.en.right = sortArticles(this.sight.en.right);
|
||||||
|
this.sight.zh.right = sortArticles(this.sight.zh.right); // теперь zh тоже сортируется одинаково
|
||||||
|
|
||||||
|
this.needLeaveAgree = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
needLeaveAgree = false;
|
||||||
|
setNeedLeaveAgree = (need: boolean) => {
|
||||||
|
this.needLeaveAgree = need;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +170,7 @@ class EditSightStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
clearSightInfo = () => {
|
clearSightInfo = () => {
|
||||||
|
this.needLeaveAgree = false;
|
||||||
this.sight = {
|
this.sight = {
|
||||||
common: {
|
common: {
|
||||||
id: 0,
|
id: 0,
|
||||||
@ -215,6 +216,7 @@ class EditSightStore {
|
|||||||
content: Partial<SightLanguageInfo | SightCommonInfo>,
|
content: Partial<SightLanguageInfo | SightCommonInfo>,
|
||||||
common: boolean = false
|
common: boolean = false
|
||||||
) => {
|
) => {
|
||||||
|
this.needLeaveAgree = true;
|
||||||
if (common) {
|
if (common) {
|
||||||
this.sight.common = {
|
this.sight.common = {
|
||||||
...this.sight.common,
|
...this.sight.common,
|
||||||
@ -262,61 +264,72 @@ class EditSightStore {
|
|||||||
this.sight.common.left_article != 0 &&
|
this.sight.common.left_article != 0 &&
|
||||||
this.sight.common.left_article != null
|
this.sight.common.left_article != null
|
||||||
) {
|
) {
|
||||||
await languageInstance("ru").patch(
|
await authInstance.patch(`/article/${this.sight.common.left_article}`, {
|
||||||
`/article/${this.sight.common.left_article}`,
|
translation: {
|
||||||
{
|
ru: {
|
||||||
heading: this.sight.ru.left.heading,
|
heading: this.sight.ru.left.heading,
|
||||||
body: this.sight.ru.left.body,
|
body: this.sight.ru.left.body,
|
||||||
}
|
},
|
||||||
);
|
en: {
|
||||||
await languageInstance("en").patch(
|
heading: this.sight.en.left.heading,
|
||||||
`/article/${this.sight.common.left_article}`,
|
body: this.sight.en.left.body,
|
||||||
{
|
},
|
||||||
heading: this.sight.en.left.heading,
|
zh: {
|
||||||
body: this.sight.en.left.body,
|
heading: this.sight.zh.left.heading,
|
||||||
}
|
body: this.sight.zh.left.body,
|
||||||
);
|
},
|
||||||
|
},
|
||||||
await languageInstance("zh").patch(
|
});
|
||||||
`/article/${this.sight.common.left_article}`,
|
|
||||||
{
|
|
||||||
heading: this.sight.zh.left.heading,
|
|
||||||
body: this.sight.zh.left.body,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await languageInstance("ru").patch(`/sight/${this.sight.common.id}`, {
|
await authInstance.patch(`/sight/${this.sight.common.id}`, {
|
||||||
...this.sight.common,
|
...this.sight.common,
|
||||||
name: this.sight.ru.name,
|
translation: {
|
||||||
address: this.sight.ru.address,
|
ru: {
|
||||||
left_article: createdLeftArticleId,
|
name: this.sight.ru.name,
|
||||||
});
|
address: this.sight.ru.address,
|
||||||
await languageInstance("en").patch(`/sight/${this.sight.common.id}`, {
|
},
|
||||||
...this.sight.common,
|
en: {
|
||||||
name: this.sight.en.name,
|
name: this.sight.en.name,
|
||||||
address: this.sight.en.address,
|
address: this.sight.en.address,
|
||||||
left_article: createdLeftArticleId,
|
},
|
||||||
});
|
zh: {
|
||||||
await languageInstance("zh").patch(`/sight/${this.sight.common.id}`, {
|
name: this.sight.zh.name,
|
||||||
...this.sight.common,
|
address: this.sight.zh.address,
|
||||||
name: this.sight.zh.name,
|
},
|
||||||
address: this.sight.zh.address,
|
},
|
||||||
left_article: createdLeftArticleId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const language of ["ru", "en", "zh"] as Language[]) {
|
for (const article of this.sight.ru.right) {
|
||||||
for (const article of this.sight[language].right) {
|
if (article.id == 0 || article.id == null) {
|
||||||
if (article.id == 0 || article.id == null) {
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await languageInstance(language).patch(`/article/${article.id}`, {
|
|
||||||
heading: article.heading,
|
|
||||||
body: article.body,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
await authInstance.patch(`/article/${article.id}`, {
|
||||||
|
translation: {
|
||||||
|
ru: {
|
||||||
|
heading: article.heading,
|
||||||
|
body: article.body,
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
heading: article.heading,
|
||||||
|
body: article.body,
|
||||||
|
},
|
||||||
|
zh: {
|
||||||
|
heading: article.heading,
|
||||||
|
body: article.body,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const articleIdsInObject = this.sight.ru.right.map((article) => ({
|
||||||
|
id: article.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
await authInstance.post(`/sight/${this.sight.common.id}/article/order`, {
|
||||||
|
articles: articleIdsInObject,
|
||||||
|
});
|
||||||
|
|
||||||
// await languageInstance("ru").patch(
|
// await languageInstance("ru").patch(
|
||||||
// `/sight/${this.sight.common.left_article}/article`,
|
// `/sight/${this.sight.common.left_article}/article`,
|
||||||
// {
|
// {
|
||||||
@ -578,19 +591,27 @@ class EditSightStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
createNewRightArticle = async () => {
|
createNewRightArticle = async () => {
|
||||||
const articleId = await languageInstance("ru").post("/article", {
|
const articleRuData = {
|
||||||
heading: "Введите русский заголовок",
|
heading: "Новый заголовок (RU)",
|
||||||
body: "Введите русский текст",
|
body: "Новый текст (RU)",
|
||||||
|
};
|
||||||
|
const articleEnData = {
|
||||||
|
heading: "New Heading (EN)",
|
||||||
|
body: "New Text (EN)",
|
||||||
|
};
|
||||||
|
const articleZhData = {
|
||||||
|
heading: "Новый заголовок (ZH)",
|
||||||
|
body: "Новый текст (ZH)",
|
||||||
|
};
|
||||||
|
const articleId = await authInstance.post("/article", {
|
||||||
|
translation: {
|
||||||
|
ru: articleRuData,
|
||||||
|
en: articleEnData,
|
||||||
|
zh: articleZhData,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const { id } = articleId.data;
|
const { id } = articleId.data;
|
||||||
await languageInstance("en").patch(`/article/${id}`, {
|
|
||||||
heading: "Enter the English heading",
|
|
||||||
body: "Enter the English text",
|
|
||||||
});
|
|
||||||
await languageInstance("zh").patch(`/article/${id}`, {
|
|
||||||
heading: "Введите китайский заголовок",
|
|
||||||
body: "Введите китайский текст",
|
|
||||||
});
|
|
||||||
await authInstance.post(`/sight/${this.sight.common.id}/article`, {
|
await authInstance.post(`/sight/${this.sight.common.id}/article`, {
|
||||||
article_id: id,
|
article_id: id,
|
||||||
page_num: this.sight.ru.right.length + 1,
|
page_num: this.sight.ru.right.length + 1,
|
||||||
@ -598,20 +619,20 @@ class EditSightStore {
|
|||||||
|
|
||||||
this.sight.ru.right.push({
|
this.sight.ru.right.push({
|
||||||
id: id,
|
id: id,
|
||||||
heading: "Введите русский заголовок",
|
heading: articleRuData.heading,
|
||||||
body: "Введите русский текст",
|
body: articleRuData.body,
|
||||||
media: [],
|
media: [],
|
||||||
});
|
});
|
||||||
this.sight.en.right.push({
|
this.sight.en.right.push({
|
||||||
id: id,
|
id: id,
|
||||||
heading: "Enter the English heading",
|
heading: articleEnData.heading,
|
||||||
body: "Enter the English text",
|
body: articleEnData.body,
|
||||||
media: [],
|
media: [],
|
||||||
});
|
});
|
||||||
this.sight.zh.right.push({
|
this.sight.zh.right.push({
|
||||||
id: id,
|
id: id,
|
||||||
heading: "Введите китайский заголовок",
|
heading: articleZhData.heading,
|
||||||
body: "Введите китайский текст",
|
body: articleZhData.body,
|
||||||
media: [],
|
media: [],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -671,14 +692,31 @@ class EditSightStore {
|
|||||||
this.sight[language].right[index].body = body;
|
this.sight[language].right[index].body = body;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateRightArticles = async (articles: any[], language: Language) => {
|
updateRightArticles = async (articles: any[]) => {
|
||||||
this.sight[language].right = articles;
|
const articlesIds = articles.map((article) => article.id);
|
||||||
const articleIdsInObject = articles.map((article) => ({
|
|
||||||
id: article.id,
|
const sortArticles = (existing: any[]) => {
|
||||||
}));
|
const articleMap = new Map(
|
||||||
await authInstance.post(`/sight/${this.sight.common.id}/article/order`, {
|
existing.map((article) => [article.id, article])
|
||||||
articles: articleIdsInObject,
|
);
|
||||||
});
|
return articlesIds
|
||||||
|
.map((id) => articleMap.get(id))
|
||||||
|
.filter(
|
||||||
|
(article): article is (typeof existing)[number] =>
|
||||||
|
article !== undefined
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sight.ru.right = sortArticles(this.sight.ru.right);
|
||||||
|
this.sight.en.right = sortArticles(this.sight.en.right);
|
||||||
|
this.sight.zh.right = sortArticles(this.sight.zh.right); // теперь zh тоже сортируется одинаково
|
||||||
|
|
||||||
|
this.needLeaveAgree = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
needLeaveAgree = false;
|
||||||
|
setNeedLeaveAgree = (need: boolean) => {
|
||||||
|
this.needLeaveAgree = need;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { useRef, useState, DragEvent, useEffect } from "react";
|
import React, { useRef, useState, DragEvent, useEffect } from "react";
|
||||||
import { Paper, Box, Typography, Button, Tooltip } from "@mui/material";
|
import { Paper, Box, Typography, Button, Tooltip } from "@mui/material";
|
||||||
import { X, Info, MousePointer } from "lucide-react"; // Assuming lucide-react for icons
|
import { X, Info, Plus } from "lucide-react"; // Assuming lucide-react for icons
|
||||||
import { editSightStore } from "@shared";
|
import { editSightStore } from "@shared";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
interface ImageUploadCardProps {
|
interface ImageUploadCardProps {
|
||||||
title: string;
|
title: string;
|
||||||
imageKey?: "thumbnail" | "watermark_lu" | "watermark_rd";
|
imageKey?: "thumbnail" | "watermark_lu" | "watermark_rd";
|
||||||
@ -12,16 +12,18 @@ interface ImageUploadCardProps {
|
|||||||
onSelectFileClick: () => void;
|
onSelectFileClick: () => void;
|
||||||
setUploadMediaOpen: (open: boolean) => void;
|
setUploadMediaOpen: (open: boolean) => void;
|
||||||
tooltipText?: string;
|
tooltipText?: string;
|
||||||
|
setHardcodeType?: (type: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
|
export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
|
||||||
title,
|
title,
|
||||||
|
imageKey,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
onImageClick,
|
onImageClick,
|
||||||
onDeleteImageClick,
|
onDeleteImageClick,
|
||||||
onSelectFileClick,
|
onSelectFileClick,
|
||||||
setUploadMediaOpen,
|
setUploadMediaOpen,
|
||||||
|
setHardcodeType,
|
||||||
tooltipText,
|
tooltipText,
|
||||||
}) => {
|
}) => {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
@ -46,6 +48,9 @@ export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
|
|||||||
if (file) {
|
if (file) {
|
||||||
setFileToUpload(file);
|
setFileToUpload(file);
|
||||||
setUploadMediaOpen(true);
|
setUploadMediaOpen(true);
|
||||||
|
if (imageKey && setHardcodeType) {
|
||||||
|
setHardcodeType(imageKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Reset the input value so selecting the same file again triggers change
|
// Reset the input value so selecting the same file again triggers change
|
||||||
event.target.value = "";
|
event.target.value = "";
|
||||||
@ -73,8 +78,12 @@ export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
|
|||||||
const files = event.dataTransfer.files;
|
const files = event.dataTransfer.files;
|
||||||
if (files && files.length > 0) {
|
if (files && files.length > 0) {
|
||||||
const file = files[0];
|
const file = files[0];
|
||||||
setFileToUpload(file);
|
if (file.type.startsWith("image/")) {
|
||||||
setUploadMediaOpen(true);
|
setFileToUpload(file);
|
||||||
|
setUploadMediaOpen(true);
|
||||||
|
} else {
|
||||||
|
toast.error("Пожалуйста, выберите изображение");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -159,7 +168,7 @@ export const ImageUploadCard: React.FC<ImageUploadCardProps> = ({
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
startIcon={<MousePointer color="white" size={18} />}
|
startIcon={<Plus color="white" size={18} />}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation(); // Prevent `handleZoneClick` from firing
|
e.stopPropagation(); // Prevent `handleZoneClick` from firing
|
||||||
onSelectFileClick(); // This button might trigger a different modal
|
onSelectFileClick(); // This button might trigger a different modal
|
||||||
|
@ -96,10 +96,10 @@ export const MediaArea = observer(
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full flex flex-start flex-wrap gap-2 mt-4">
|
<div className="w-full flex flex-start flex-wrap gap-2 mt-4 py-10">
|
||||||
{mediaIds.map((m) => (
|
{mediaIds.map((m) => (
|
||||||
<button
|
<button
|
||||||
className="relative w-40 h-40"
|
className="relative w-20 h-20"
|
||||||
key={m.id}
|
key={m.id}
|
||||||
onClick={() => handleMediaModal(m.id)}
|
onClick={() => handleMediaModal(m.id)}
|
||||||
>
|
>
|
||||||
|
@ -12,29 +12,17 @@ export interface MediaData {
|
|||||||
export function MediaViewer({
|
export function MediaViewer({
|
||||||
media,
|
media,
|
||||||
className,
|
className,
|
||||||
}: Readonly<{ media?: MediaData; className?: string }>) {
|
fullWidth,
|
||||||
|
}: Readonly<{ media?: MediaData; className?: string; fullWidth?: boolean }>) {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box className={className} width={fullWidth ? "100%" : "auto"}>
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexGrow: 1,
|
|
||||||
}}
|
|
||||||
className={className}
|
|
||||||
>
|
|
||||||
{media?.media_type === 1 && (
|
{media?.media_type === 1 && (
|
||||||
<img
|
<img
|
||||||
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
src={`${import.meta.env.VITE_KRBL_MEDIA}${
|
||||||
media?.id
|
media?.id
|
||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
alt={media?.filename}
|
alt={media?.filename}
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "contain",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -46,7 +34,7 @@ export function MediaViewer({
|
|||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: "contain",
|
objectFit: "cover",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
}}
|
}}
|
||||||
controls
|
controls
|
||||||
@ -60,12 +48,6 @@ export function MediaViewer({
|
|||||||
media?.id
|
media?.id
|
||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
alt={media?.filename}
|
alt={media?.filename}
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "contain",
|
|
||||||
borderRadius: 8,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{media?.media_type === 4 && (
|
{media?.media_type === 4 && (
|
||||||
@ -75,10 +57,7 @@ export function MediaViewer({
|
|||||||
}/download?token=${token}`}
|
}/download?token=${token}`}
|
||||||
alt={media?.filename}
|
alt={media?.filename}
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
objectFit: "cover",
|
||||||
height: "100%",
|
|
||||||
objectFit: "contain",
|
|
||||||
borderRadius: 8,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -25,13 +25,20 @@ const StyledMarkdownEditor = styled("div")(({ theme }) => ({
|
|||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
borderColor: theme.palette.divider,
|
borderColor: theme.palette.divider,
|
||||||
height: "auto",
|
height: "auto",
|
||||||
minHeight: "200px",
|
minHeight: "400px",
|
||||||
maxHeight: "500px",
|
maxHeight: "500px",
|
||||||
overflow: "auto",
|
overflowY: "auto",
|
||||||
|
overflowX: "hidden",
|
||||||
|
wordWrap: "break-word", // ✅ добавлено
|
||||||
|
whiteSpace: "pre-wrap", // ✅ добавлено
|
||||||
},
|
},
|
||||||
|
|
||||||
"& .CodeMirror-scroll": {
|
"& .CodeMirror-scroll": {
|
||||||
minHeight: "200px",
|
minHeight: "400px",
|
||||||
maxHeight: "500px",
|
maxHeight: "500px",
|
||||||
|
overflowY: "auto",
|
||||||
|
overflowX: "hidden",
|
||||||
|
wordBreak: "break-word", // ✅ добавлено
|
||||||
},
|
},
|
||||||
"& .CodeMirror-selected": {
|
"& .CodeMirror-selected": {
|
||||||
backgroundColor: `${theme.palette.action.selected} !important`,
|
backgroundColor: `${theme.palette.action.selected} !important`,
|
||||||
@ -76,6 +83,9 @@ export const ReactMarkdownEditor = ({
|
|||||||
const mergedOptions = useMemo(() => {
|
const mergedOptions = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
...incomingOptions,
|
...incomingOptions,
|
||||||
|
codeMirrorOptions: {
|
||||||
|
lineWrapping: true,
|
||||||
|
},
|
||||||
forceSync: true,
|
forceSync: true,
|
||||||
spellChecker: false,
|
spellChecker: false,
|
||||||
toolbar: [
|
toolbar: [
|
||||||
|
@ -48,6 +48,9 @@ export const CreateInformationTab = observer(
|
|||||||
"thumbnail" | "watermark_lu" | "watermark_rd" | null
|
"thumbnail" | "watermark_lu" | "watermark_rd" | null
|
||||||
>(null);
|
>(null);
|
||||||
const [isAddMediaOpen, setIsAddMediaOpen] = useState(false);
|
const [isAddMediaOpen, setIsAddMediaOpen] = useState(false);
|
||||||
|
const [hardcodeType, setHardcodeType] = useState<
|
||||||
|
"thumbnail" | "watermark_lu" | "watermark_rd" | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
// const handleMenuOpen = (
|
// const handleMenuOpen = (
|
||||||
// event: React.MouseEvent<HTMLElement>,
|
// event: React.MouseEvent<HTMLElement>,
|
||||||
@ -260,10 +263,15 @@ export const CreateInformationTab = observer(
|
|||||||
setIsUploadMediaOpen(true);
|
setIsUploadMediaOpen(true);
|
||||||
setActiveMenuType("thumbnail");
|
setActiveMenuType("thumbnail");
|
||||||
}}
|
}}
|
||||||
|
setHardcodeType={(type) => {
|
||||||
|
setHardcodeType(
|
||||||
|
type as "thumbnail" | "watermark_lu" | "watermark_rd"
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ImageUploadCard
|
<ImageUploadCard
|
||||||
title="Водяной знак (л.в)"
|
title="Водяной знак (леввый верхний)"
|
||||||
imageKey="watermark_lu"
|
imageKey="watermark_lu"
|
||||||
imageUrl={sight.watermark_lu}
|
imageUrl={sight.watermark_lu}
|
||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
@ -284,10 +292,15 @@ export const CreateInformationTab = observer(
|
|||||||
setIsUploadMediaOpen(true);
|
setIsUploadMediaOpen(true);
|
||||||
setActiveMenuType("watermark_lu");
|
setActiveMenuType("watermark_lu");
|
||||||
}}
|
}}
|
||||||
|
setHardcodeType={(type) => {
|
||||||
|
setHardcodeType(
|
||||||
|
type as "thumbnail" | "watermark_lu" | "watermark_rd"
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ImageUploadCard
|
<ImageUploadCard
|
||||||
title="Водяной знак (п.в)"
|
title="Водяной знак (правый нижний)"
|
||||||
imageKey="watermark_rd"
|
imageKey="watermark_rd"
|
||||||
imageUrl={sight.watermark_rd}
|
imageUrl={sight.watermark_rd}
|
||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
@ -308,6 +321,11 @@ export const CreateInformationTab = observer(
|
|||||||
setIsUploadMediaOpen(true);
|
setIsUploadMediaOpen(true);
|
||||||
setActiveMenuType("watermark_rd");
|
setActiveMenuType("watermark_rd");
|
||||||
}}
|
}}
|
||||||
|
setHardcodeType={(type) => {
|
||||||
|
setHardcodeType(
|
||||||
|
type as "thumbnail" | "watermark_lu" | "watermark_rd"
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -397,6 +415,7 @@ export const CreateInformationTab = observer(
|
|||||||
setActiveMenuType(null);
|
setActiveMenuType(null);
|
||||||
setIsUploadMediaOpen(false);
|
setIsUploadMediaOpen(false);
|
||||||
}}
|
}}
|
||||||
|
hardcodeType={hardcodeType}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
createSightStore,
|
createSightStore,
|
||||||
SelectArticleModal,
|
SelectArticleModal,
|
||||||
UploadMediaDialog,
|
UploadMediaDialog,
|
||||||
|
Language,
|
||||||
} from "@shared";
|
} from "@shared";
|
||||||
import {
|
import {
|
||||||
LanguageSwitcher,
|
LanguageSwitcher,
|
||||||
@ -308,8 +309,8 @@ export const CreateLeftTab = observer(
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: 1.5,
|
|
||||||
maxWidth: "320px",
|
maxWidth: "320px",
|
||||||
|
gap: 0.5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Paper
|
<Paper
|
||||||
@ -317,49 +318,32 @@ export const CreateLeftTab = observer(
|
|||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
minWidth: 320,
|
minWidth: 320,
|
||||||
maxWidth: 400,
|
background:
|
||||||
height: "auto",
|
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
||||||
minHeight: 500,
|
|
||||||
backgroundColor: "#877361",
|
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
padding: 0,
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* {data.left.media?.filename ? (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
height: 200,
|
|
||||||
backgroundColor: "grey.300",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={data.left.media?.filename ?? ""}
|
|
||||||
alt="Превью медиа"
|
|
||||||
style={{
|
|
||||||
objectFit: "cover",
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
|
flexDirection: "column",
|
||||||
|
borderRadius: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
overflow: "hidden",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: 200,
|
minHeight: 100,
|
||||||
backgroundColor: "grey.300",
|
padding: "3px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
|
|
||||||
|
"& img": {
|
||||||
|
borderTopLeftRadius: "10px",
|
||||||
|
borderTopRightRadius: "10px",
|
||||||
|
width: "100%",
|
||||||
|
height: "auto",
|
||||||
|
objectFit: "contain",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{sight[language].left.media.length > 0 ? (
|
{sight[language].left.media.length > 0 ? (
|
||||||
@ -370,40 +354,67 @@ export const CreateLeftTab = observer(
|
|||||||
sight[language].left.media[0].media_type,
|
sight[language].left.media[0].media_type,
|
||||||
filename: sight[language].left.media[0].filename,
|
filename: sight[language].left.media[0].filename,
|
||||||
}}
|
}}
|
||||||
|
fullWidth
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ImagePlus size={48} color="grey" />
|
<ImagePlus size={48} color="white" />
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Заголовок в превью */}
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#877361",
|
background:
|
||||||
|
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
||||||
color: "white",
|
color: "white",
|
||||||
padding: 1.5,
|
margin: "5px 0px 5px 0px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: 1,
|
||||||
|
padding: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h5"
|
variant="h5"
|
||||||
component="h2"
|
component="h2"
|
||||||
sx={{ wordBreak: "break-word" }}
|
sx={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
fontSize: "24px",
|
||||||
|
fontWeight: 700,
|
||||||
|
lineHeight: "120%",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{sight[language].left.heading || "Название информации"}
|
{sight[language].left.heading || "Название информации"}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="h6"
|
||||||
|
component="h2"
|
||||||
|
sx={{
|
||||||
|
wordBreak: "break-word",
|
||||||
|
fontSize: "18px",
|
||||||
|
|
||||||
|
lineHeight: "120%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{sight[language as Language].address}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Текст статьи в превью */}
|
{sight[language].left.body && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
padding: 2,
|
padding: 1,
|
||||||
flexGrow: 1,
|
maxHeight: "300px",
|
||||||
}}
|
overflowY: "scroll",
|
||||||
>
|
background:
|
||||||
<ReactMarkdownComponent
|
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
||||||
value={sight[language].left.body}
|
flexGrow: 1,
|
||||||
/>
|
}}
|
||||||
</Box>
|
>
|
||||||
|
<ReactMarkdownComponent
|
||||||
|
value={sight[language].left.body}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -239,7 +239,7 @@ export const CreateRightTab = observer(
|
|||||||
|
|
||||||
// 4. Update the store with the new order:
|
// 4. Update the store with the new order:
|
||||||
// This will typically trigger a re-render of the component with the updated list.
|
// This will typically trigger a re-render of the component with the updated list.
|
||||||
updateRightArticles(newRightArticles, language);
|
updateRightArticles(newRightArticles);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -511,7 +511,7 @@ export const CreateRightTab = observer(
|
|||||||
<Paper
|
<Paper
|
||||||
className="flex-1 flex flex-col max-w-[500px]"
|
className="flex-1 flex flex-col max-w-[500px]"
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: "16px",
|
borderRadius: "10px",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
elevation={2}
|
elevation={2}
|
||||||
@ -531,13 +531,17 @@ export const CreateRightTab = observer(
|
|||||||
0 ? (
|
0 ? (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
overflow: "hidden",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
maxHeight: "290px",
|
padding: "2px 2px 0px 2px",
|
||||||
flexShrink: 0,
|
|
||||||
|
|
||||||
display: "flex",
|
"& img": {
|
||||||
alignItems: "center",
|
borderTopLeftRadius: "10px",
|
||||||
justifyContent: "center",
|
borderTopRightRadius: "10px",
|
||||||
|
width: "100%",
|
||||||
|
height: "auto",
|
||||||
|
objectFit: "contain",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MediaViewer
|
<MediaViewer
|
||||||
@ -571,6 +575,7 @@ export const CreateRightTab = observer(
|
|||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
lineHeight: "120%",
|
lineHeight: "120%",
|
||||||
backdropFilter: "blur(12px)",
|
backdropFilter: "blur(12px)",
|
||||||
|
borderBottom: "1px solid #A89F90",
|
||||||
boxShadow:
|
boxShadow:
|
||||||
"inset 4px 4px 12px 0 rgba(255,255,255,0.12)",
|
"inset 4px 4px 12px 0 rgba(255,255,255,0.12)",
|
||||||
background:
|
background:
|
||||||
|
@ -50,7 +50,9 @@ export const InformationTab = observer(
|
|||||||
"thumbnail" | "watermark_lu" | "watermark_rd" | null
|
"thumbnail" | "watermark_lu" | "watermark_rd" | null
|
||||||
>(null);
|
>(null);
|
||||||
const [isAddMediaOpen, setIsAddMediaOpen] = useState(false);
|
const [isAddMediaOpen, setIsAddMediaOpen] = useState(false);
|
||||||
|
const [hardcodeType, setHardcodeType] = useState<
|
||||||
|
"thumbnail" | "watermark_lu" | "watermark_rd" | null
|
||||||
|
>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Показывать только при инициализации (не менять при ошибках пользователя)
|
// Показывать только при инициализации (не менять при ошибках пользователя)
|
||||||
if (sight.common.latitude !== 0 || sight.common.longitude !== 0) {
|
if (sight.common.latitude !== 0 || sight.common.longitude !== 0) {
|
||||||
@ -260,9 +262,14 @@ export const InformationTab = observer(
|
|||||||
setIsUploadMediaOpen(true);
|
setIsUploadMediaOpen(true);
|
||||||
setActiveMenuType("thumbnail");
|
setActiveMenuType("thumbnail");
|
||||||
}}
|
}}
|
||||||
|
setHardcodeType={(type) => {
|
||||||
|
setHardcodeType(
|
||||||
|
type as "thumbnail" | "watermark_lu" | "watermark_rd"
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<ImageUploadCard
|
<ImageUploadCard
|
||||||
title="Водяной знак (л.в)"
|
title="Водяной знак (левый верхний)"
|
||||||
imageKey="watermark_lu"
|
imageKey="watermark_lu"
|
||||||
imageUrl={sight.common.watermark_lu}
|
imageUrl={sight.common.watermark_lu}
|
||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
@ -287,9 +294,14 @@ export const InformationTab = observer(
|
|||||||
setIsUploadMediaOpen(true);
|
setIsUploadMediaOpen(true);
|
||||||
setActiveMenuType("watermark_lu");
|
setActiveMenuType("watermark_lu");
|
||||||
}}
|
}}
|
||||||
|
setHardcodeType={(type) => {
|
||||||
|
setHardcodeType(
|
||||||
|
type as "thumbnail" | "watermark_lu" | "watermark_rd"
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<ImageUploadCard
|
<ImageUploadCard
|
||||||
title="Водяной знак (п.в)"
|
title="Водяной знак (правый нижний)"
|
||||||
imageKey="watermark_rd"
|
imageKey="watermark_rd"
|
||||||
imageUrl={sight.common.watermark_rd}
|
imageUrl={sight.common.watermark_rd}
|
||||||
onImageClick={() => {
|
onImageClick={() => {
|
||||||
@ -314,6 +326,11 @@ export const InformationTab = observer(
|
|||||||
setIsUploadMediaOpen(true);
|
setIsUploadMediaOpen(true);
|
||||||
setActiveMenuType("watermark_rd");
|
setActiveMenuType("watermark_rd");
|
||||||
}}
|
}}
|
||||||
|
setHardcodeType={(type) => {
|
||||||
|
setHardcodeType(
|
||||||
|
type as "thumbnail" | "watermark_lu" | "watermark_rd"
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@ -399,6 +416,7 @@ export const InformationTab = observer(
|
|||||||
setActiveMenuType(null);
|
setActiveMenuType(null);
|
||||||
setIsUploadMediaOpen(false);
|
setIsUploadMediaOpen(false);
|
||||||
}}
|
}}
|
||||||
|
hardcodeType={hardcodeType}
|
||||||
/>
|
/>
|
||||||
<PreviewMediaDialog
|
<PreviewMediaDialog
|
||||||
open={isPreviewMediaOpen}
|
open={isPreviewMediaOpen}
|
||||||
|
@ -260,22 +260,32 @@ export const LeftWidgetTab = observer(
|
|||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
minWidth: 320,
|
minWidth: 320,
|
||||||
|
|
||||||
background:
|
background:
|
||||||
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
"#806c59 linear-gradient(90deg, rgba(255, 255, 255, 0.2) 12.5%, rgba(255, 255, 255, 0.2) 100%)",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
padding: 0,
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
borderRadius: "10px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
overflow: "hidden",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: 175,
|
minHeight: 100,
|
||||||
|
padding: "3px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
padding: "3px",
|
justifyContent: "center",
|
||||||
|
|
||||||
|
"& img": {
|
||||||
|
borderTopLeftRadius: "10px",
|
||||||
|
borderTopRightRadius: "10px",
|
||||||
|
width: "100%",
|
||||||
|
height: "auto",
|
||||||
|
objectFit: "contain",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{data.left.media.length > 0 ? (
|
{data.left.media.length > 0 ? (
|
||||||
@ -285,9 +295,10 @@ export const LeftWidgetTab = observer(
|
|||||||
media_type: data.left.media[0].media_type,
|
media_type: data.left.media[0].media_type,
|
||||||
filename: data.left.media[0].filename,
|
filename: data.left.media[0].filename,
|
||||||
}}
|
}}
|
||||||
|
fullWidth
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ImagePlus size={48} color="grey" />
|
<ImagePlus size={48} color="white" />
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ export const RightWidgetTab = observer(
|
|||||||
|
|
||||||
// 4. Update the store with the new order:
|
// 4. Update the store with the new order:
|
||||||
// This will typically trigger a re-render of the component with the updated list.
|
// This will typically trigger a re-render of the component with the updated list.
|
||||||
updateRightArticles(newRightArticles, language);
|
updateRightArticles(newRightArticles);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -347,7 +347,7 @@ export const RightWidgetTab = observer(
|
|||||||
value={
|
value={
|
||||||
sight[language].right[activeArticleIndex].body
|
sight[language].right[activeArticleIndex].body
|
||||||
}
|
}
|
||||||
onChange={(value) =>
|
onChange={(value: any) =>
|
||||||
updateRightArticleInfo(
|
updateRightArticleInfo(
|
||||||
activeArticleIndex,
|
activeArticleIndex,
|
||||||
language,
|
language,
|
||||||
@ -381,11 +381,11 @@ export const RightWidgetTab = observer(
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{type === "media" && (
|
{type === "media" && (
|
||||||
<Box className="w-[80%] border border-gray-300 rounded-2xl relative">
|
<Box className="w-[80%] border border-gray-300 rounded-2xl relative flex justify-center items-center">
|
||||||
{sight.common.preview_media && (
|
{sight.common.preview_media && (
|
||||||
<>
|
<>
|
||||||
{type === "media" && (
|
{type === "media" && (
|
||||||
<Box className="w-[80%] h-full rounded-2xl relative flex items-center justify-center">
|
<Box className="w-full h-full rounded-2xl relative flex items-center justify-center">
|
||||||
{previewMedia && (
|
{previewMedia && (
|
||||||
<>
|
<>
|
||||||
<Box className="absolute top-4 right-4 z-10">
|
<Box className="absolute top-4 right-4 z-10">
|
||||||
@ -397,7 +397,7 @@ export const RightWidgetTab = observer(
|
|||||||
</button>
|
</button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box className="w-1/2 h-1/2">
|
<Box className="w-1/2 h-1/2 flex justify-center items-center">
|
||||||
<MediaViewer
|
<MediaViewer
|
||||||
media={{
|
media={{
|
||||||
id: previewMedia.id || "",
|
id: previewMedia.id || "",
|
||||||
@ -453,7 +453,7 @@ export const RightWidgetTab = observer(
|
|||||||
<Paper
|
<Paper
|
||||||
className="flex-1 flex flex-col max-w-[500px]"
|
className="flex-1 flex flex-col max-w-[500px]"
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: "16px",
|
borderRadius: "10px",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
elevation={2}
|
elevation={2}
|
||||||
@ -473,13 +473,17 @@ export const RightWidgetTab = observer(
|
|||||||
0 ? (
|
0 ? (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
overflow: "hidden",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
maxHeight: "290px",
|
padding: "2px 2px 0px 2px",
|
||||||
flexShrink: 0,
|
|
||||||
|
|
||||||
display: "flex",
|
"& img": {
|
||||||
alignItems: "center",
|
borderTopLeftRadius: "10px",
|
||||||
justifyContent: "center",
|
borderTopRightRadius: "10px",
|
||||||
|
width: "100%",
|
||||||
|
height: "auto",
|
||||||
|
objectFit: "contain",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MediaViewer
|
<MediaViewer
|
||||||
@ -513,6 +517,7 @@ export const RightWidgetTab = observer(
|
|||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
lineHeight: "120%",
|
lineHeight: "120%",
|
||||||
backdropFilter: "blur(12px)",
|
backdropFilter: "blur(12px)",
|
||||||
|
borderBottom: "1px solid #A89F90",
|
||||||
boxShadow:
|
boxShadow:
|
||||||
"inset 4px 4px 12px 0 rgba(255,255,255,0.12)",
|
"inset 4px 4px 12px 0 rgba(255,255,255,0.12)",
|
||||||
background:
|
background:
|
||||||
|
Reference in New Issue
Block a user