feat: Sight Page update

This commit is contained in:
2025-06-01 23:18:21 +03:00
parent 87386c6a73
commit a8777a974a
26 changed files with 3460 additions and 727 deletions

View File

@@ -1,4 +1,4 @@
import { articlesStore } from "@shared";
import { articlesStore, authInstance, languageStore } from "@shared";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
import {
@@ -22,8 +22,13 @@ import { ReactMarkdownComponent } from "@widgets";
interface SelectArticleModalProps {
open: boolean;
onClose: () => void;
onSelectArticle: (articleId: string) => void;
linkedArticleIds?: string[]; // Add optional prop for linked articles
onSelectArticle: (
articleId: number,
heading: string,
body: string,
media: { id: string; media_type: number; filename: string }[]
) => void;
linkedArticleIds?: number[]; // Add optional prop for linked articles
}
export const SelectArticleModal = observer(
@@ -35,7 +40,7 @@ export const SelectArticleModal = observer(
}: SelectArticleModalProps) => {
const { articles, getArticle, getArticleMedia } = articlesStore;
const [searchQuery, setSearchQuery] = useState("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
const [selectedArticleId, setSelectedArticleId] = useState<number | null>(
null
);
const [isLoading, setIsLoading] = useState(false);
@@ -50,12 +55,21 @@ export const SelectArticleModal = observer(
}, [open]);
useEffect(() => {
const handleKeyPress = (event: KeyboardEvent) => {
const handleKeyPress = async (event: KeyboardEvent) => {
if (event.key.toLowerCase() === "enter") {
event.preventDefault();
if (selectedArticleId) {
onSelectArticle(selectedArticleId);
const media = await authInstance.get(
`/article/${selectedArticleId}/media`
);
onSelectArticle(
selectedArticleId,
articlesStore.articleData?.heading || "",
articlesStore.articleData?.body || "",
media.data || []
);
onClose();
setSelectedArticleId(null);
}
}
};
@@ -66,9 +80,7 @@ export const SelectArticleModal = observer(
};
}, [selectedArticleId, onSelectArticle, onClose]);
const handleArticleClick = async (articleId: string) => {
if (selectedArticleId === articleId) return;
const handleArticleClick = async (articleId: number) => {
setSelectedArticleId(articleId);
setIsLoading(true);
@@ -86,14 +98,13 @@ export const SelectArticleModal = observer(
setIsLoading(false);
}
};
// @ts-ignore
const filteredArticles = articles
// @ts-ignore
.filter((article) => !linkedArticleIds.includes(article.id))
// @ts-ignore
.filter((article) =>
article.service_name.toLowerCase().includes(searchQuery.toLowerCase())
);
const filteredArticles = articles[languageStore.language].filter(
(article) => !linkedArticleIds.includes(article.id)
);
// .filter((article) =>
// article.service_name.toLowerCase().includes(searchQuery.toLowerCase())
// );
const token = localStorage.getItem("token");
return (
@@ -150,7 +161,17 @@ export const SelectArticleModal = observer(
<ListItemButton
key={article.id}
onClick={() => handleArticleClick(article.id)}
onDoubleClick={() => onSelectArticle(article.id)}
onDoubleClick={async () => {
const media = await authInstance.get(
`/article/${article.id}/media`
);
onSelectArticle(
article.id,
article.heading,
article.body,
media.data
);
}}
selected={selectedArticleId === article.id}
disabled={isLoading}
sx={{
@@ -288,9 +309,22 @@ export const SelectArticleModal = observer(
<Button onClick={onClose}>Отмена</Button>
<Button
variant="contained"
onClick={() =>
selectedArticleId && onSelectArticle(selectedArticleId)
}
onClick={async () => {
if (selectedArticleId) {
const media = await authInstance.get(
`/article/${selectedArticleId}/media`
);
onSelectArticle(
selectedArticleId,
articlesStore.articleData?.heading || "",
articlesStore.articleData?.body || "",
media.data
);
onClose();
setSelectedArticleId(null);
}
}}
disabled={!selectedArticleId || isLoading}
>
Выбрать

View File

@@ -1,6 +1,6 @@
import { mediaStore } from "@shared";
import { observer } from "mobx-react-lite";
import { useEffect, useRef, useState } from "react";
import { useEffect, useState } from "react";
import {
Dialog,
DialogTitle,
@@ -21,7 +21,12 @@ import { MediaViewer } from "@widgets";
interface SelectMediaDialogProps {
open: boolean; // Corrected prop name
onClose: () => void;
onSelectMedia: (mediaId: string) => void; // Renamed from onSelectArticle
onSelectMedia: (media: {
id: string;
filename: string;
media_name?: string;
media_type: number;
}) => void; // Renamed from onSelectArticle
linkedMediaIds?: string[]; // Renamed from linkedArticleIds, assuming it refers to media already in use
}
@@ -32,15 +37,14 @@ export const SelectMediaDialog = observer(
onSelectMedia, // Renamed prop
linkedMediaIds = [], // Default to empty array if not provided, renamed
}: SelectMediaDialogProps) => {
const { media, getMedia } = mediaStore; // Confirmed: using mediaStore for media
const { media, getMedia } = mediaStore;
const [searchQuery, setSearchQuery] = useState("");
const [hoveredMediaId, setHoveredMediaId] = useState<string | null>(null);
const hoverTimerRef = useRef<NodeJS.Timeout | null>(null);
// Fetch media on component mount
useEffect(() => {
getMedia();
}, [getMedia]); // getMedia should be a dependency to avoid lint warnings if it's not stable
}, [getMedia]);
// Keyboard event listener for "Enter" key to select hovered media
useEffect(() => {
@@ -49,7 +53,10 @@ export const SelectMediaDialog = observer(
event.preventDefault(); // Prevent browser default action (e.g., form submission)
if (hoveredMediaId) {
onSelectMedia(hoveredMediaId); // Call onSelectMedia
const mediaItem = media.find((m) => m.id === hoveredMediaId);
if (mediaItem) {
onSelectMedia(mediaItem);
}
onClose();
}
}
@@ -61,26 +68,6 @@ export const SelectMediaDialog = observer(
};
}, [hoveredMediaId, onSelectMedia, onClose]); // Dependencies for keyboard listener
// Effect for handling hover timeout (if you want to clear the preview after a delay)
// Based on the original code, it seemed like you wanted a delay for showing,
// but typically for a preview, it's immediate on hover and cleared on mouse leave.
// I've removed the 5-second timeout for setting the ID as it's counter-intuitive for a live preview.
// If you intend for the preview to disappear after a short while *after* the mouse leaves,
// you would implement a mouseleave timer. For now, it will clear on mouseleave.
const handleMouseEnter = (mediaId: string) => {
if (hoverTimerRef.current) {
clearTimeout(hoverTimerRef.current);
}
setHoveredMediaId(mediaId);
};
const handleMouseLeave = () => {
// You can add a small delay here if you want the preview to linger for a moment
// before disappearing, e.g., setTimeout(() => setHoveredMediaId(null), 200);
setHoveredMediaId(null);
};
const filteredMedia = media
.filter((mediaItem) => !linkedMediaIds.includes(mediaItem.id)) // Use mediaItem to avoid name collision
.filter((mediaItem) =>
@@ -125,9 +112,11 @@ export const SelectMediaDialog = observer(
) => (
<ListItemButton
key={mediaItem.id}
onClick={() => onSelectMedia(mediaItem.id)} // Call onSelectMedia
onMouseEnter={() => handleMouseEnter(mediaItem.id)}
onMouseLeave={handleMouseLeave}
onClick={() => setHoveredMediaId(mediaItem.id)} // Call onSelectMedia
onDoubleClick={() => {
onSelectMedia(mediaItem);
onClose();
}}
sx={{
borderRadius: 1,
mb: 0.5,

View File

@@ -0,0 +1,259 @@
import { MEDIA_TYPE_LABELS, editSightStore } from "@shared";
import { observer } from "mobx-react-lite";
import { useEffect, useState } from "react";
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
TextField,
Paper,
Box,
CircularProgress,
Alert,
Snackbar,
Select,
MenuItem,
FormControl,
InputLabel,
} from "@mui/material";
import { Save } from "lucide-react";
import { ModelViewer3D } from "@widgets";
interface UploadMediaDialogProps {
open: boolean;
onClose: () => void;
afterUpload: (media: {
id: string;
filename: string;
media_name?: string;
media_type: number;
}) => void;
}
export const UploadMediaDialog = observer(
({ open, onClose, afterUpload }: UploadMediaDialogProps) => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const [mediaName, setMediaName] = useState("");
const [mediaFilename, setMediaFilename] = useState("");
const [mediaType, setMediaType] = useState(0);
const [mediaFile, setMediaFile] = useState<File | null>(null);
const { fileToUpload, uploadMedia } = editSightStore;
const [mediaUrl, setMediaUrl] = useState<string | null>(null);
const [availableMediaTypes, setAvailableMediaTypes] = useState<number[]>(
[]
);
useEffect(() => {
if (fileToUpload) {
setMediaFile(fileToUpload);
setMediaFilename(fileToUpload.name);
// Try to determine media type from file extension
const extension = fileToUpload.name.split(".").pop()?.toLowerCase();
if (extension) {
if (["glb", "gltf"].includes(extension)) {
setAvailableMediaTypes([6]);
setMediaType(6);
}
if (["jpg", "jpeg", "png", "gif"].includes(extension)) {
// Для изображений доступны все типы кроме видео
setAvailableMediaTypes([1, 3, 4, 5]); // Фото, Иконка, Водяной знак, Панорама, 3Д-модель
setMediaType(1); // По умолчанию Фото
} else if (["mp4", "webm", "mov"].includes(extension)) {
// Для видео только тип Видео
setAvailableMediaTypes([2]);
setMediaType(2);
}
}
}
}, [fileToUpload]);
useEffect(() => {
if (mediaFile) {
setMediaUrl(URL.createObjectURL(mediaFile as Blob));
}
}, [mediaFile]);
// const fileFormat = useEffect(() => {
// const handleKeyPress = (event: KeyboardEvent) => {
// if (event.key.toLowerCase() === "enter" && !event.ctrlKey) {
// event.preventDefault();
// onClose();
// }
// };
// window.addEventListener("keydown", handleKeyPress);
// return () => window.removeEventListener("keydown", handleKeyPress);
// }, [onClose]);
const handleSave = async () => {
if (!mediaFile) return;
setIsLoading(true);
setError(null);
try {
const media = await uploadMedia(
mediaFilename,
mediaType,
mediaFile,
mediaName
);
if (media) {
await afterUpload(media);
}
setSuccess(true);
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to save media");
} finally {
setIsLoading(false);
}
};
const handleClose = () => {
setError(null);
setSuccess(false);
onClose();
};
return (
<>
<Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>
<DialogTitle>Просмотр медиа</DialogTitle>
<DialogContent
className="flex gap-4"
dividers
sx={{
height: "600px",
display: "flex",
flexDirection: "column",
gap: 2,
pt: 2,
}}
>
<Box className="flex flex-col gap-4">
<Box className="flex gap-2">
<TextField
fullWidth
value={mediaName}
onChange={(e) => setMediaName(e.target.value)}
label="Название медиа"
disabled={isLoading}
/>
<TextField
fullWidth
value={mediaFilename}
onChange={(e) => setMediaFilename(e.target.value)}
label="Название файла"
disabled={isLoading}
/>
</Box>
<FormControl fullWidth sx={{ width: "50%" }}>
<InputLabel>Тип медиа</InputLabel>
<Select
value={mediaType}
label="Тип медиа"
onChange={(e) => setMediaType(Number(e.target.value))}
disabled={isLoading}
>
{availableMediaTypes.map((type) => (
<MenuItem key={type} value={type}>
{
MEDIA_TYPE_LABELS[
type as keyof typeof MEDIA_TYPE_LABELS
]
}
</MenuItem>
))}
</Select>
</FormControl>
<Box className="flex gap-4 h-full">
<Paper
elevation={2}
sx={{
flex: 1,
p: 2,
display: "flex",
alignItems: "center",
justifyContent: "center",
minHeight: 400,
}}
>
{/* <MediaViewer
media={{
id: "",
media_type: mediaType,
filename: mediaFilename,
}}
/> */}
{mediaType === 6 && mediaUrl && (
<ModelViewer3D fileUrl={mediaUrl} height="100%" />
)}
{mediaType !== 6 && mediaType !== 2 && mediaUrl && (
<img
src={mediaUrl ?? ""}
alt="Uploaded media"
style={{
maxWidth: "100%",
maxHeight: "100%",
objectFit: "contain",
}}
/>
)}
</Paper>
<Box className="flex flex-col gap-2 self-end">
<Button
variant="contained"
color="success"
startIcon={
isLoading ? (
<CircularProgress size={16} />
) : (
<Save size={16} />
)
}
onClick={handleSave}
disabled={isLoading || (!mediaName && !mediaFilename)}
>
Сохранить
</Button>
</Box>
</Box>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} disabled={isLoading}>
Отмена
</Button>
</DialogActions>
</Dialog>
<Snackbar
open={!!error}
autoHideDuration={6000}
onClose={() => setError(null)}
>
<Alert severity="error" onClose={() => setError(null)}>
{error}
</Alert>
</Snackbar>
<Snackbar
open={success}
autoHideDuration={3000}
onClose={() => setSuccess(false)}
>
<Alert severity="success" onClose={() => setSuccess(false)}>
Медиа успешно сохранено
</Alert>
</Snackbar>
</>
);
}
);

View File

@@ -1,3 +1,4 @@
export * from "./SelectArticleDialog";
export * from "./SelectMediaDialog";
export * from "./PreviewMediaDialog";
export * from "./UploadMediaDialog";

View File

@@ -17,6 +17,8 @@ class CityStore {
}
getCities = async () => {
if (this.cities.length !== 0) return;
const response = await authInstance.get("/city");
runInAction(() => {
this.cities = response.data;

View File

@@ -0,0 +1,449 @@
// @shared/stores/createSightStore.ts
import {
Language,
authInstance,
languageInstance,
articlesStore,
languageStore,
mediaStore,
} from "@shared";
import { makeAutoObservable } from "mobx";
type SightLanguageInfo = {
name: string;
address: string;
left: {
heading: string;
body: string;
media: {
id: string;
filename: string;
media_name?: string;
media_type: number;
}[];
};
right: { heading: string; body: string }[];
};
type SightCommonInfo = {
id: number;
city_id: number;
city: string;
latitude: number;
longitude: number;
thumbnail: string | null;
watermark_lu: string | null;
watermark_rd: string | null;
left_article: number;
preview_media: string | null;
video_preview: string | null;
};
type SightBaseInfo = SightCommonInfo & {
[key in Language]: SightLanguageInfo;
};
class CreateSightStore {
sight: SightBaseInfo = {
id: 0,
city_id: 0,
city: "",
latitude: 0,
longitude: 0,
thumbnail: null,
watermark_lu: null,
watermark_rd: null,
left_article: 0,
preview_media: null,
video_preview: null,
ru: {
name: "",
address: "",
left: { heading: "", body: "", media: [] },
right: [],
},
en: {
name: "",
address: "",
left: { heading: "", body: "", media: [] },
right: [],
},
zh: {
name: "",
address: "",
left: { heading: "", body: "", media: [] },
right: [],
},
};
uploadMediaOpen = false;
setUploadMediaOpen = (open: boolean) => {
this.uploadMediaOpen = open;
};
fileToUpload: File | null = null;
setFileToUpload = (file: File | null) => {
this.fileToUpload = file;
};
constructor() {
makeAutoObservable(this);
}
createNewRightArticle = () => {
this.sight.ru.right.push({
heading: "Введите русский заголовок",
body: "Введите русский текст",
});
this.sight.en.right.push({
heading: "Enter the English heading",
body: "Enter the English text",
});
this.sight.zh.right.push({
heading: "Введите китайский заголовок",
body: "Введите китайский текст",
});
};
updateLeftInfo = (language: Language, heading: string, body: string) => {
this.sight[language].left.heading = heading;
this.sight[language].left.body = body;
};
clearCreateSight = () => {
this.sight = {
id: 0,
city_id: 0,
city: "",
latitude: 0,
longitude: 0,
thumbnail: null,
watermark_lu: null,
watermark_rd: null,
left_article: 0,
preview_media: null,
video_preview: null,
ru: {
name: "",
address: "",
left: { heading: "", body: "", media: [] },
right: [],
},
en: {
name: "",
address: "",
left: { heading: "", body: "", media: [] },
right: [],
},
zh: {
name: "",
address: "",
left: { heading: "", body: "", media: [] },
right: [],
},
};
};
updateSightInfo = (
content: Partial<SightLanguageInfo | SightCommonInfo>,
language?: Language
) => {
if (language) {
this.sight[language] = {
...this.sight[language],
...content,
};
} else {
this.sight = {
...this.sight,
...content,
};
}
};
unlinkLeftArticle = () => {
this.sight.left_article = 0;
this.sight.ru.left.heading = "";
this.sight.en.left.heading = "";
this.sight.zh.left.heading = "";
this.sight.ru.left.body = "";
this.sight.en.left.body = "";
this.sight.zh.left.body = "";
};
updateLeftArticle = async (articleId: number) => {
this.sight.left_article = articleId;
if (articleId) {
const ruArticleData = await languageInstance("ru").get(
`/article/${articleId}`
);
const enArticleData = await languageInstance("en").get(
`/article/${articleId}`
);
const zhArticleData = await languageInstance("zh").get(
`/article/${articleId}`
);
this.sight.ru.left.heading = ruArticleData.data.heading;
this.sight.en.left.heading = enArticleData.data.heading;
this.sight.zh.left.heading = zhArticleData.data.heading;
this.sight.ru.left.body = ruArticleData.data.body;
this.sight.en.left.body = enArticleData.data.body;
this.sight.zh.left.body = zhArticleData.data.body;
} else {
this.sight.left_article = 0;
this.sight.ru.left.heading = "";
this.sight.en.left.heading = "";
this.sight.zh.left.heading = "";
this.sight.ru.left.body = "";
this.sight.en.left.body = "";
this.sight.zh.left.body = "";
}
};
deleteLeftArticle = async (articleId: number) => {
await authInstance.delete(`/article/${articleId}`);
articlesStore.getArticles(languageStore.language);
this.sight.left_article = 0;
this.sight.ru.left.heading = "";
this.sight.en.left.heading = "";
this.sight.zh.left.heading = "";
this.sight.ru.left.body = "";
};
createLeftArticle = async () => {
const response = await languageInstance("ru").post("/article", {
heading: "Новая статья",
body: "Заполните статью контентом",
});
this.sight.left_article = response.data.id;
this.sight.ru.left.heading = "Новая статья ";
this.sight.en.left.heading = "";
this.sight.zh.left.heading = "";
this.sight.ru.left.body = "Заполните статью контентом";
this.sight.en.left.body = "";
this.sight.zh.left.body = "";
};
createSight = async (language: Language) => {
const rightArticles: number[] = [];
if (this.sight.left_article !== 0) {
if (this.sight.left_article == 10000000) {
const response = await languageInstance("ru").post("/article", {
heading: this.sight.ru.left.heading,
body: this.sight.ru.left.body,
});
const { id } = response.data;
await languageInstance("en").patch(`/article/${id}`, {
heading: this.sight.en.left.heading,
body: this.sight.en.left.body,
});
await languageInstance("zh").patch(`/article/${id}`, {
heading: this.sight.zh.left.heading,
body: this.sight.zh.left.body,
});
this.sight.left_article = id;
} else {
await languageInstance("ru").patch(
`/article/${this.sight.left_article}`,
{
heading: this.sight.ru.left.heading,
body: this.sight.ru.left.body,
}
);
await languageInstance("en").patch(
`/article/${this.sight.left_article}`,
{
heading: this.sight.en.left.heading,
body: this.sight.en.left.body,
}
);
await languageInstance("zh").patch(
`/article/${this.sight.left_article}`,
{
heading: this.sight.zh.left.heading,
body: this.sight.zh.left.body,
}
);
}
}
this.sight[language].right.map(async (article, index) => {
try {
const response = await languageInstance(language).post("/article", {
heading: article.heading,
body: article.body,
});
const { id } = response.data;
const anotherLanguages = ["en", "zh", "ru"].filter(
(lang) => lang !== language
);
await languageInstance(anotherLanguages[0] as Language).patch(
`/article/${id}`,
{
heading:
this.sight[anotherLanguages[0] as Language].right[index].heading,
body: this.sight[anotherLanguages[0] as Language].right[index].body,
}
);
await languageInstance(anotherLanguages[1] as Language).patch(
`/article/${id}`,
{
heading:
this.sight[anotherLanguages[1] as Language].right[index].heading,
body: this.sight[anotherLanguages[1] as Language].right[index].body,
}
);
rightArticles.push(id);
} catch (error) {
console.log(error);
}
});
const response = await languageInstance(language).post("/sight", {
city_id: this.sight.city_id,
city: this.sight.city,
latitude: this.sight.latitude,
longitude: this.sight.longitude,
name: this.sight[language].name,
address: this.sight[language].address,
thumbnail: this.sight.thumbnail ?? null,
watermark_lu: this.sight.watermark_lu,
watermark_rd: this.sight.watermark_rd,
left_article: this.sight.left_article,
preview_media: this.sight.preview_media,
video_preview: this.sight.video_preview,
});
const { id } = response.data;
const anotherLanguages = ["en", "zh", "ru"].filter(
(lang) => lang !== language
);
await languageInstance(anotherLanguages[0] as Language).patch(
`/sight/${id}`,
{
city_id: this.sight.city_id,
city: this.sight.city,
latitude: this.sight.latitude,
longitude: this.sight.longitude,
name: this.sight[anotherLanguages[0] as Language as Language].name,
address:
this.sight[anotherLanguages[0] as Language as Language].address,
thumbnail: this.sight.thumbnail ?? null,
watermark_lu: this.sight.watermark_lu,
watermark_rd: this.sight.watermark_rd,
left_article: this.sight.left_article,
preview_media: this.sight.preview_media,
video_preview: this.sight.video_preview,
}
);
await languageInstance(anotherLanguages[1] as Language).patch(
`/sight/${id}`,
{
city_id: this.sight.city_id,
city: this.sight.city,
latitude: this.sight.latitude,
longitude: this.sight.longitude,
name: this.sight[anotherLanguages[1] as Language].name,
address: this.sight[anotherLanguages[1] as Language].address,
thumbnail: this.sight.thumbnail ?? null,
watermark_lu: this.sight.watermark_lu,
watermark_rd: this.sight.watermark_rd,
left_article: this.sight.left_article,
preview_media: this.sight.preview_media,
video_preview: this.sight.video_preview,
}
);
rightArticles.map(async (article, index) => {
await authInstance.post(`/sight/${id}/article`, {
article_id: article,
page_num: index + 1,
});
});
console.log("created");
};
updateRightArticleInfo = (
index: number,
language: Language,
heading: string,
body: string
) => {
this.sight[language].right[index].heading = heading;
this.sight[language].right[index].body = body;
};
uploadMedia = async (
filename: string,
type: number,
file: File,
media_name?: string
) => {
const formData = new FormData();
formData.append("file", file);
formData.append("filename", filename);
if (media_name) {
formData.append("media_name", media_name);
}
formData.append("type", type.toString());
try {
const response = await authInstance.post(`/media`, formData);
this.fileToUpload = null;
this.uploadMediaOpen = false;
mediaStore.getMedia();
return {
id: response.data.id,
filename: filename,
media_name: media_name,
media_type: type,
};
} catch (error) {
console.log(error);
throw error;
}
};
createLinkWithArticle = async (media: {
id: string;
filename: string;
media_name?: string;
media_type: number;
}) => {
await authInstance.post(`/article/${this.sight.left_article}/media`, {
media_id: media.id,
media_order: 1,
});
this.sight.ru.left.media.unshift({
id: media.id,
media_type: media.media_type,
filename: media.filename,
});
this.sight.en.left.media.unshift({
id: media.id,
media_type: media.media_type,
filename: media.filename,
});
this.sight.zh.left.media.unshift({
id: media.id,
media_type: media.media_type,
filename: media.filename,
});
};
}
export const createSightStore = new CreateSightStore();

View File

@@ -1,26 +1,31 @@
// @shared/stores/editSightStore.ts
import { authInstance, Language } from "@shared";
import { authInstance, Language, languageInstance, mediaStore } from "@shared";
import { makeAutoObservable, runInAction } from "mobx";
export type SightLanguageInfo = {
id: number;
name: string;
address: string;
left: { heading: string; body: string };
left: {
heading: string;
body: string;
media: { id: string; media_type: number; filename: string }[];
};
right: { heading: string; body: string }[];
};
export type SightCommonInfo = {
id: number;
city_id: number;
city: string;
latitude: number;
longitude: number;
thumbnail: string;
watermark_lu: string;
watermark_rd: string;
thumbnail: string | null;
watermark_lu: string | null;
watermark_rd: string | null;
left_article: number;
preview_media: string;
video_preview: string;
preview_media: string | null;
video_preview: string | null;
};
export type SightBaseInfo = {
@@ -31,36 +36,37 @@ export type SightBaseInfo = {
class EditSightStore {
sight: SightBaseInfo = {
common: {
id: 0,
city_id: 0,
city: "",
latitude: 0,
longitude: 0,
thumbnail: "",
watermark_lu: "",
watermark_rd: "",
thumbnail: null,
watermark_lu: null,
watermark_rd: null,
left_article: 0,
preview_media: "",
video_preview: "",
preview_media: null,
video_preview: null,
},
ru: {
id: 0,
name: "",
address: "",
left: { heading: "", body: "" },
left: { heading: "", body: "", media: [] },
right: [],
},
en: {
id: 0,
name: "",
address: "",
left: { heading: "", body: "" },
left: { heading: "", body: "", media: [] },
right: [],
},
zh: {
id: 0,
name: "",
address: "",
left: { heading: "", body: "" },
left: { heading: "", body: "", media: [] },
right: [],
},
};
@@ -77,6 +83,9 @@ class EditSightStore {
const response = await authInstance.get(`/sight/${id}`);
const data = response.data;
if (data.left_article != 0) {
await this.getLeftArticle(data.left_article);
}
runInAction(() => {
// Обновляем языковую часть
@@ -101,25 +110,62 @@ class EditSightStore {
this.sight[language].left.body = body;
};
getRightArticles = async (id: number) => {
const responseRu = await languageInstance("ru").get(`/sight/${id}/article`);
const responseEn = await languageInstance("en").get(`/sight/${id}/article`);
const responseZh = await languageInstance("zh").get(`/sight/${id}/article`);
const data = {
ru: {
right: responseRu.data,
},
en: {
right: responseEn.data,
},
zh: {
right: responseZh.data,
},
};
runInAction(() => {
this.sight = {
...this.sight,
ru: {
...this.sight.ru,
right: data.ru.right,
},
en: {
...this.sight.en,
right: data.en.right,
},
zh: {
...this.sight.zh,
right: data.zh.right,
},
};
});
};
clearSightInfo = () => {
this.sight = {
common: {
id: 0,
city_id: 0,
city: "",
latitude: 0,
longitude: 0,
thumbnail: "",
watermark_lu: "",
watermark_rd: "",
thumbnail: null,
watermark_lu: null,
watermark_rd: null,
left_article: 0,
preview_media: "",
video_preview: "",
preview_media: null,
video_preview: null,
},
ru: {
id: 0,
name: "",
address: "",
left: { heading: "", body: "" },
left: { heading: "", body: "", media: [] },
right: [],
},
@@ -127,7 +173,7 @@ class EditSightStore {
id: 0,
name: "",
address: "",
left: { heading: "", body: "" },
left: { heading: "", body: "", media: [] },
right: [],
},
@@ -135,7 +181,7 @@ class EditSightStore {
id: 0,
name: "",
address: "",
left: { heading: "", body: "" },
left: { heading: "", body: "", media: [] },
right: [],
},
};
@@ -158,6 +204,244 @@ class EditSightStore {
};
}
};
unlinkLeftArticle = async () => {
this.sight.common.left_article = 0;
this.sight.ru.left.heading = "";
this.sight.en.left.heading = "";
this.sight.zh.left.heading = "";
this.sight.ru.left.body = "";
this.sight.en.left.body = "";
this.sight.zh.left.body = "";
};
updateSight = async () => {
let createdLeftArticleId = this.sight.common.left_article;
if (this.sight.common.left_article == 10000000) {
const response = await languageInstance("ru").post(`/article`, {
heading: this.sight.ru.left.heading,
body: this.sight.ru.left.body,
});
createdLeftArticleId = response.data.id;
await languageInstance("en").patch(`/article/${createdLeftArticleId}`, {
heading: this.sight.en.left.heading,
body: this.sight.en.left.body,
});
await languageInstance("zh").patch(`/article/${createdLeftArticleId}`, {
heading: this.sight.zh.left.heading,
body: this.sight.zh.left.body,
});
this.sight.common.left_article = createdLeftArticleId;
} else if (this.sight.common.left_article != 0) {
await languageInstance("ru").patch(
`/article/${this.sight.common.left_article}`,
{
heading: this.sight.ru.left.heading,
body: this.sight.ru.left.body,
}
);
await languageInstance("en").patch(
`/article/${this.sight.common.left_article}`,
{
heading: this.sight.en.left.heading,
body: this.sight.en.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}`, {
...this.sight.common,
name: this.sight.ru.name,
address: this.sight.ru.address,
left_article: createdLeftArticleId,
});
await languageInstance("en").patch(`/sight/${this.sight.common.id}`, {
...this.sight.common,
name: this.sight.en.name,
address: this.sight.en.address,
left_article: createdLeftArticleId,
});
await languageInstance("zh").patch(`/sight/${this.sight.common.id}`, {
...this.sight.common,
name: this.sight.zh.name,
address: this.sight.zh.address,
left_article: createdLeftArticleId,
});
if (this.sight.common.left_article == 0) {
return;
}
// await languageInstance("ru").patch(
// `/sight/${this.sight.common.left_article}/article`,
// {
// heading: this.sight.ru.left.heading,
// body: this.sight.ru.left.body,
// }
// );
// await languageInstance("en").patch(
// `/sight/${this.sight.common.left_article}/article`,
// {
// heading: this.sight.en.left.heading,
// body: this.sight.en.left.body,
// }
// );
// await languageInstance("zh").patch(
// `/sight/${this.sight.common.left_article}/article`,
// {
// heading: this.sight.zh.left.heading,
// body: this.sight.zh.left.body,
// }
// );
};
getLeftArticle = async (id: number) => {
const response = await languageInstance("ru").get(`/article/${id}`);
const responseEn = await languageInstance("en").get(`/article/${id}`);
const responseZh = await languageInstance("zh").get(`/article/${id}`);
const mediaIds = await authInstance.get(`/article/${id}/media`);
runInAction(() => {
this.sight.ru.left = {
heading: response.data.heading,
body: response.data.body,
media: mediaIds.data,
};
this.sight.en.left = {
heading: responseEn.data.heading,
body: responseEn.data.body,
media: mediaIds.data,
};
this.sight.zh.left = {
heading: responseZh.data.heading,
body: responseZh.data.body,
media: mediaIds.data,
};
});
};
deleteLeftArticle = async (id: number) => {
await authInstance.delete(`/article/${id}`);
this.sight.common.left_article = 0;
this.sight.ru.left.heading = "";
this.sight.en.left.heading = "";
this.sight.zh.left.heading = "";
this.sight.ru.left.body = "";
this.sight.en.left.body = "";
this.sight.zh.left.body = "";
};
createLeftArticle = async () => {
const response = await languageInstance("ru").post(`/article`, {
heading: "",
body: "",
});
this.sight.common.left_article = response.data.id;
this.sight.ru.left.heading = "";
this.sight.en.left.heading = "";
this.sight.zh.left.heading = "";
this.sight.ru.left.body = "";
this.sight.en.left.body = "";
this.sight.zh.left.body = "";
};
deleteMedia = async (article_id: number, media_id: string) => {
await authInstance.delete(`/article/${article_id}/media`, {
data: {
media_id: media_id,
},
});
this.sight.ru.left.media = this.sight.ru.left.media.filter(
(media) => media.id !== media_id
);
this.sight.en.left.media = this.sight.en.left.media.filter(
(media) => media.id !== media_id
);
this.sight.zh.left.media = this.sight.zh.left.media.filter(
(media) => media.id !== media_id
);
};
uploadMediaOpen = false;
setUploadMediaOpen = (open: boolean) => {
this.uploadMediaOpen = open;
};
fileToUpload: File | null = null;
setFileToUpload = (file: File | null) => {
this.fileToUpload = file;
};
uploadMedia = async (
filename: string,
type: number,
file: File,
media_name?: string
) => {
const formData = new FormData();
formData.append("file", file);
formData.append("filename", filename);
if (media_name) {
formData.append("media_name", media_name);
}
formData.append("type", type.toString());
try {
const response = await authInstance.post(`/media`, formData);
this.fileToUpload = null;
this.uploadMediaOpen = false;
mediaStore.getMedia();
return {
id: response.data.id,
filename: filename,
media_name: media_name,
media_type: type,
};
} catch (error) {
console.log(error);
}
};
createLinkWithArticle = async (media: {
id: string;
filename: string;
media_name?: string;
media_type: number;
}) => {
await authInstance.post(
`/article/${this.sight.common.left_article}/media`,
{
media_id: media.id,
media_order: 1,
}
);
this.sight.ru.left.media.unshift({
id: media.id,
media_type: media.media_type,
filename: media.filename,
});
this.sight.en.left.media.unshift({
id: media.id,
media_type: media.media_type,
filename: media.filename,
});
this.sight.zh.left.media.unshift({
id: media.id,
media_type: media.media_type,
filename: media.filename,
});
};
}
export const editSightStore = new EditSightStore();

View File

@@ -8,3 +8,4 @@ export * from "./CityStore";
export * from "./ArticlesStore";
export * from "./EditSightStore";
export * from "./MediaStore";
export * from "./CreateSightStore";