Add correct edit right widget
This commit is contained in:
		| @@ -6,7 +6,6 @@ const authInstance = axios.create({ | ||||
| }); | ||||
|  | ||||
| authInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => { | ||||
|   console.log(config); | ||||
|   config.headers.Authorization = `Bearer ${localStorage.getItem("token")}`; | ||||
|   config.headers["X-Language"] = languageStore.language ?? "ru"; | ||||
|   return config; | ||||
|   | ||||
| @@ -21,12 +21,13 @@ import { MediaViewer } from "@widgets"; | ||||
| interface SelectMediaDialogProps { | ||||
|   open: boolean; // Corrected prop name | ||||
|   onClose: () => void; | ||||
|   onSelectMedia: (media: { | ||||
|   onSelectMedia?: (media: { | ||||
|     id: string; | ||||
|     filename: string; | ||||
|     media_name?: string; | ||||
|     media_type: number; | ||||
|   }) => void; // Renamed from onSelectArticle | ||||
|   onSelectForSightMedia?: (mediaId: string) => void; | ||||
|   linkedMediaIds?: string[]; // Renamed from linkedArticleIds, assuming it refers to media already in use | ||||
| } | ||||
|  | ||||
| @@ -35,6 +36,7 @@ export const SelectMediaDialog = observer( | ||||
|     open, // Corrected prop name | ||||
|     onClose, | ||||
|     onSelectMedia, // Renamed prop | ||||
|     onSelectForSightMedia, | ||||
|     linkedMediaIds = [], // Default to empty array if not provided, renamed | ||||
|   }: SelectMediaDialogProps) => { | ||||
|     const { media, getMedia } = mediaStore; | ||||
| @@ -55,7 +57,11 @@ export const SelectMediaDialog = observer( | ||||
|           if (hoveredMediaId) { | ||||
|             const mediaItem = media.find((m) => m.id === hoveredMediaId); | ||||
|             if (mediaItem) { | ||||
|               onSelectMedia(mediaItem); | ||||
|               if (onSelectForSightMedia) { | ||||
|                 onSelectForSightMedia(mediaItem.id); | ||||
|               } else if (onSelectMedia) { | ||||
|                 onSelectMedia(mediaItem); | ||||
|               } | ||||
|             } | ||||
|             onClose(); | ||||
|           } | ||||
| @@ -114,7 +120,11 @@ export const SelectMediaDialog = observer( | ||||
|                       key={mediaItem.id} | ||||
|                       onClick={() => setHoveredMediaId(mediaItem.id)} // Call onSelectMedia | ||||
|                       onDoubleClick={() => { | ||||
|                         onSelectMedia(mediaItem); | ||||
|                         if (onSelectForSightMedia) { | ||||
|                           onSelectForSightMedia(mediaItem.id); | ||||
|                         } else if (onSelectMedia) { | ||||
|                           onSelectMedia(mediaItem); | ||||
|                         } | ||||
|                         onClose(); | ||||
|                       }} | ||||
|                       sx={{ | ||||
|   | ||||
| @@ -24,16 +24,22 @@ import { ModelViewer3D } from "@widgets"; | ||||
| interface UploadMediaDialogProps { | ||||
|   open: boolean; | ||||
|   onClose: () => void; | ||||
|   afterUpload: (media: { | ||||
|   afterUpload?: (media: { | ||||
|     id: string; | ||||
|     filename: string; | ||||
|     media_name?: string; | ||||
|     media_type: number; | ||||
|   }) => void; | ||||
|   afterUploadSight?: (id: string) => void; | ||||
| } | ||||
|  | ||||
| export const UploadMediaDialog = observer( | ||||
|   ({ open, onClose, afterUpload }: UploadMediaDialogProps) => { | ||||
|   ({ | ||||
|     open, | ||||
|     onClose, | ||||
|     afterUpload, | ||||
|     afterUploadSight, | ||||
|   }: UploadMediaDialogProps) => { | ||||
|     const [isLoading, setIsLoading] = useState(false); | ||||
|     const [error, setError] = useState<string | null>(null); | ||||
|     const [success, setSuccess] = useState(false); | ||||
| @@ -103,7 +109,11 @@ export const UploadMediaDialog = observer( | ||||
|           mediaName | ||||
|         ); | ||||
|         if (media) { | ||||
|           await afterUpload(media); | ||||
|           if (afterUploadSight) { | ||||
|             await afterUploadSight(media.id); | ||||
|           } else if (afterUpload) { | ||||
|             await afterUpload(media); | ||||
|           } | ||||
|         } | ||||
|         setSuccess(true); | ||||
|       } catch (err) { | ||||
|   | ||||
| @@ -22,7 +22,7 @@ type SightLanguageInfo = { | ||||
|       media_type: number; | ||||
|     }[]; | ||||
|   }; | ||||
|   right: { heading: string; body: string }[]; | ||||
|   right: { id: number; heading: string; body: string; media: [] }[]; | ||||
| }; | ||||
|  | ||||
| type SightCommonInfo = { | ||||
| @@ -90,19 +90,43 @@ class CreateSightStore { | ||||
|     makeAutoObservable(this); | ||||
|   } | ||||
|  | ||||
|   createNewRightArticle = () => { | ||||
|     this.sight.ru.right.push({ | ||||
|   createNewRightArticle = async () => { | ||||
|     const articleId = await languageInstance("ru").post("/article", { | ||||
|       heading: "Введите русский заголовок", | ||||
|       body: "Введите русский текст", | ||||
|     }); | ||||
|     this.sight.en.right.push({ | ||||
|     const { id } = articleId.data; | ||||
|     await languageInstance("en").patch(`/article/${id}`, { | ||||
|       heading: "Enter the English heading", | ||||
|       body: "Enter the English text", | ||||
|     }); | ||||
|     this.sight.zh.right.push({ | ||||
|     await languageInstance("zh").patch(`/article/${id}`, { | ||||
|       heading: "Введите китайский заголовок", | ||||
|       body: "Введите китайский текст", | ||||
|     }); | ||||
|     await authInstance.post(`/sight/${this.sight.id}/article`, { | ||||
|       article_id: id, | ||||
|       page_num: this.sight.ru.right.length + 1, | ||||
|     }); | ||||
|  | ||||
|     this.sight.ru.right.push({ | ||||
|       id: id, | ||||
|       heading: "Введите русский заголовок", | ||||
|       body: "Введите русский текст", | ||||
|       media: [], | ||||
|     }); | ||||
|     this.sight.en.right.push({ | ||||
|       id: id, | ||||
|       heading: "Enter the English heading", | ||||
|       body: "Enter the English text", | ||||
|       media: [], | ||||
|     }); | ||||
|     this.sight.zh.right.push({ | ||||
|       id: id, | ||||
|       heading: "Введите китайский заголовок", | ||||
|       body: "Введите китайский текст", | ||||
|       media: [], | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   updateLeftInfo = (language: Language, heading: string, body: string) => { | ||||
| @@ -444,6 +468,32 @@ class CreateSightStore { | ||||
|       filename: media.filename, | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   unlinkRightAritcle = async (id: number) => { | ||||
|     this.sight.ru.right = this.sight.ru.right.filter( | ||||
|       (article) => article.id !== id | ||||
|     ); | ||||
|     this.sight.en.right = this.sight.en.right.filter( | ||||
|       (article) => article.id !== id | ||||
|     ); | ||||
|     this.sight.zh.right = this.sight.zh.right.filter( | ||||
|       (article) => article.id !== id | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   deleteRightArticle = async (id: number) => { | ||||
|     await authInstance.delete(`/article/${id}`); | ||||
|  | ||||
|     this.sight.ru.right = this.sight.ru.right.filter( | ||||
|       (article) => article.id !== id | ||||
|     ); | ||||
|     this.sight.en.right = this.sight.en.right.filter( | ||||
|       (article) => article.id !== id | ||||
|     ); | ||||
|     this.sight.zh.right = this.sight.zh.right.filter( | ||||
|       (article) => article.id !== id | ||||
|     ); | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export const createSightStore = new CreateSightStore(); | ||||
|   | ||||
| @@ -11,7 +11,12 @@ export type SightLanguageInfo = { | ||||
|     body: string; | ||||
|     media: { id: string; media_type: number; filename: string }[]; | ||||
|   }; | ||||
|   right: { heading: string; body: string }[]; | ||||
|   right: { | ||||
|     id: number; | ||||
|     heading: string; | ||||
|     body: string; | ||||
|     media: { id: string; media_type: number; filename: string }[]; | ||||
|   }[]; | ||||
| }; | ||||
|  | ||||
| export type SightCommonInfo = { | ||||
| @@ -111,21 +116,42 @@ class EditSightStore { | ||||
|   }; | ||||
|  | ||||
|   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`); | ||||
|     let responseRu = await languageInstance("ru").get(`/sight/${id}/article`); | ||||
|     let responseEn = await languageInstance("en").get(`/sight/${id}/article`); | ||||
|     let responseZh = await languageInstance("zh").get(`/sight/${id}/article`); | ||||
|  | ||||
|     // Function to fetch media for a given set of articles | ||||
|     const fetchMediaForArticles = async (articles: any[]) => { | ||||
|       const articlesWithMedia = []; | ||||
|       for (const article of articles) { | ||||
|         const responseMedia = await authInstance.get( | ||||
|           `/article/${article.id}/media` | ||||
|         ); | ||||
|         articlesWithMedia.push({ | ||||
|           ...article, | ||||
|           media: responseMedia.data, | ||||
|         }); | ||||
|       } | ||||
|       return articlesWithMedia; | ||||
|     }; | ||||
|  | ||||
|     // Fetch media for articles in each language | ||||
|     const ruArticlesWithMedia = await fetchMediaForArticles(responseRu.data); | ||||
|     const enArticlesWithMedia = await fetchMediaForArticles(responseEn.data); | ||||
|     const zhArticlesWithMedia = await fetchMediaForArticles(responseZh.data); | ||||
|  | ||||
|     const data = { | ||||
|       ru: { | ||||
|         right: responseRu.data, | ||||
|         right: ruArticlesWithMedia, | ||||
|       }, | ||||
|       en: { | ||||
|         right: responseEn.data, | ||||
|         right: enArticlesWithMedia, | ||||
|       }, | ||||
|       zh: { | ||||
|         right: responseZh.data, | ||||
|         right: zhArticlesWithMedia, | ||||
|       }, | ||||
|     }; | ||||
|  | ||||
|     runInAction(() => { | ||||
|       this.sight = { | ||||
|         ...this.sight, | ||||
| @@ -137,7 +163,6 @@ class EditSightStore { | ||||
|           ...this.sight.en, | ||||
|           right: data.en.right, | ||||
|         }, | ||||
|  | ||||
|         zh: { | ||||
|           ...this.sight.zh, | ||||
|           right: data.zh.right, | ||||
| @@ -279,8 +304,13 @@ class EditSightStore { | ||||
|       left_article: createdLeftArticleId, | ||||
|     }); | ||||
|  | ||||
|     if (this.sight.common.left_article == 0) { | ||||
|       return; | ||||
|     for (const language of ["ru", "en", "zh"] as Language[]) { | ||||
|       for (const article of this.sight[language].right) { | ||||
|         await languageInstance(language).patch(`/article/${article.id}`, { | ||||
|           heading: article.heading, | ||||
|           body: article.body, | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // await languageInstance("ru").patch( | ||||
| @@ -375,6 +405,38 @@ class EditSightStore { | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   unlinkRightArticle = async (article_id: number) => { | ||||
|     await authInstance.delete(`/sight/${this.sight.common.id}/article`, { | ||||
|       data: { | ||||
|         article_id: article_id, | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     this.sight.ru.right = this.sight.ru.right.filter( | ||||
|       (article) => article.id !== article_id | ||||
|     ); | ||||
|     this.sight.en.right = this.sight.en.right.filter( | ||||
|       (article) => article.id !== article_id | ||||
|     ); | ||||
|     this.sight.zh.right = this.sight.zh.right.filter( | ||||
|       (article) => article.id !== article_id | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   deleteRightArticle = async (article_id: number) => { | ||||
|     this.sight.ru.right = this.sight.ru.right.filter( | ||||
|       (article) => article.id !== article_id | ||||
|     ); | ||||
|     this.sight.en.right = this.sight.en.right.filter( | ||||
|       (article) => article.id !== article_id | ||||
|     ); | ||||
|     this.sight.zh.right = this.sight.zh.right.filter( | ||||
|       (article) => article.id !== article_id | ||||
|     ); | ||||
|  | ||||
|     await authInstance.delete(`/article/${article_id}`); | ||||
|   }; | ||||
|  | ||||
|   uploadMediaOpen = false; | ||||
|   setUploadMediaOpen = (open: boolean) => { | ||||
|     this.uploadMediaOpen = open; | ||||
| @@ -442,6 +504,168 @@ class EditSightStore { | ||||
|       filename: media.filename, | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   unlinkPreviewMedia = async () => { | ||||
|     this.sight.common.preview_media = null; | ||||
|   }; | ||||
|  | ||||
|   linkPreviewMedia = async (mediaId: string) => { | ||||
|     this.sight.common.preview_media = mediaId; | ||||
|   }; | ||||
|  | ||||
|   linkArticle = async (article_id: number) => { | ||||
|     const response = await languageInstance("ru").get(`/article/${article_id}`); | ||||
|     const responseEn = await languageInstance("en").get( | ||||
|       `/article/${article_id}` | ||||
|     ); | ||||
|     const responseZh = await languageInstance("zh").get( | ||||
|       `/article/${article_id}` | ||||
|     ); | ||||
|     const mediaIds = await authInstance.get(`/article/${article_id}/media`); | ||||
|     runInAction(() => { | ||||
|       this.sight.ru.right.push({ | ||||
|         id: article_id, | ||||
|         heading: response.data.heading, | ||||
|         body: response.data.body, | ||||
|         media: mediaIds.data, | ||||
|       }); | ||||
|       this.sight.en.right.push({ | ||||
|         id: article_id, | ||||
|         heading: responseEn.data.heading, | ||||
|         body: responseEn.data.body, | ||||
|         media: mediaIds.data, | ||||
|       }); | ||||
|       this.sight.zh.right.push({ | ||||
|         id: article_id, | ||||
|         heading: responseZh.data.heading, | ||||
|         body: responseZh.data.body, | ||||
|         media: mediaIds.data, | ||||
|       }); | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   deleteRightArticleMedia = async (article_id: number, media_id: string) => { | ||||
|     await authInstance.delete(`/article/${article_id}/media`, { | ||||
|       data: { | ||||
|         media_id: media_id, | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     this.sight.ru.right = this.sight.ru.right.map((article) => { | ||||
|       if (article.id === article_id) { | ||||
|         article.media = article.media.filter((media) => media.id !== media_id); | ||||
|       } | ||||
|       return article; | ||||
|     }); | ||||
|  | ||||
|     this.sight.en.right = this.sight.en.right.map((article) => { | ||||
|       if (article.id === article_id) { | ||||
|         article.media = article.media.filter((media) => media.id !== media_id); | ||||
|       } | ||||
|       return article; | ||||
|     }); | ||||
|  | ||||
|     this.sight.zh.right = this.sight.zh.right.map((article) => { | ||||
|       if (article.id === article_id) { | ||||
|         article.media = article.media.filter((media) => media.id !== media_id); | ||||
|       } | ||||
|       return article; | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   createNewRightArticle = async () => { | ||||
|     const articleId = await languageInstance("ru").post("/article", { | ||||
|       heading: "Введите русский заголовок", | ||||
|       body: "Введите русский текст", | ||||
|     }); | ||||
|     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`, { | ||||
|       article_id: id, | ||||
|       page_num: this.sight.ru.right.length + 1, | ||||
|     }); | ||||
|  | ||||
|     this.sight.ru.right.push({ | ||||
|       id: id, | ||||
|       heading: "Введите русский заголовок", | ||||
|       body: "Введите русский текст", | ||||
|       media: [], | ||||
|     }); | ||||
|     this.sight.en.right.push({ | ||||
|       id: id, | ||||
|       heading: "Enter the English heading", | ||||
|       body: "Enter the English text", | ||||
|       media: [], | ||||
|     }); | ||||
|     this.sight.zh.right.push({ | ||||
|       id: id, | ||||
|       heading: "Введите китайский заголовок", | ||||
|       body: "Введите китайский текст", | ||||
|       media: [], | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   createLinkWithRightArticle = async ( | ||||
|     media: { | ||||
|       id: string; | ||||
|       filename: string; | ||||
|       media_name?: string; | ||||
|       media_type: number; | ||||
|     }, | ||||
|     article_id: number | ||||
|   ) => { | ||||
|     await authInstance.post(`/article/${article_id}/media`, { | ||||
|       media_id: media.id, | ||||
|       media_order: 1, | ||||
|     }); | ||||
|     this.sight.ru.right = this.sight.ru.right.map((article) => { | ||||
|       if (article.id === article_id) { | ||||
|         article.media.unshift({ | ||||
|           id: media.id, | ||||
|           media_type: media.media_type, | ||||
|           filename: media.filename, | ||||
|         }); | ||||
|       } | ||||
|       return article; | ||||
|     }); | ||||
|     this.sight.en.right = this.sight.en.right.map((article) => { | ||||
|       if (article.id === article_id) { | ||||
|         article.media.unshift({ | ||||
|           id: media.id, | ||||
|           media_type: media.media_type, | ||||
|           filename: media.filename, | ||||
|         }); | ||||
|       } | ||||
|       return article; | ||||
|     }); | ||||
|     this.sight.zh.right = this.sight.zh.right.map((article) => { | ||||
|       if (article.id === article_id) { | ||||
|         article.media.unshift({ | ||||
|           id: media.id, | ||||
|           media_type: media.media_type, | ||||
|           filename: media.filename, | ||||
|         }); | ||||
|       } | ||||
|       return article; | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
|   updateRightArticleInfo = ( | ||||
|     index: number, | ||||
|     language: Language, | ||||
|     heading: string, | ||||
|     body: string | ||||
|   ) => { | ||||
|     this.sight[language].right[index].heading = heading; | ||||
|     this.sight[language].right[index].body = body; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| export const editSightStore = new EditSightStore(); | ||||
|   | ||||
							
								
								
									
										107
									
								
								src/widgets/MediaAreaForSight/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/widgets/MediaAreaForSight/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| import { Box, Button } from "@mui/material"; | ||||
| import { editSightStore, SelectMediaDialog, UploadMediaDialog } from "@shared"; | ||||
| import { Upload } from "lucide-react"; | ||||
| import { observer } from "mobx-react-lite"; | ||||
| import { useState, DragEvent, useRef } from "react"; | ||||
|  | ||||
| export const MediaAreaForSight = observer( | ||||
|   ({ | ||||
|     onFilesDrop, // 👈 Проп для обработки загруженных файлов | ||||
|     onFinishUpload, | ||||
|   }: { | ||||
|     onFilesDrop?: (files: File[]) => void; | ||||
|     onFinishUpload?: (mediaId: string) => void; | ||||
|   }) => { | ||||
|     const [selectMediaDialogOpen, setSelectMediaDialogOpen] = useState(false); | ||||
|     const [uploadMediaDialogOpen, setUploadMediaDialogOpen] = useState(false); | ||||
|     const [isDragging, setIsDragging] = useState(false); | ||||
|     const fileInputRef = useRef<HTMLInputElement>(null); | ||||
|     const { setFileToUpload } = editSightStore; | ||||
|  | ||||
|     const handleDrop = (e: DragEvent<HTMLDivElement>) => { | ||||
|       e.preventDefault(); | ||||
|       e.stopPropagation(); | ||||
|       setIsDragging(false); | ||||
|  | ||||
|       const files = Array.from(e.dataTransfer.files); | ||||
|       if (files.length && onFilesDrop) { | ||||
|         setFileToUpload(files[0]); | ||||
|       } | ||||
|  | ||||
|       setUploadMediaDialogOpen(true); | ||||
|     }; | ||||
|  | ||||
|     const handleDragOver = (e: DragEvent<HTMLDivElement>) => { | ||||
|       e.preventDefault(); | ||||
|       setIsDragging(true); | ||||
|     }; | ||||
|  | ||||
|     const handleDragLeave = () => { | ||||
|       setIsDragging(false); | ||||
|     }; | ||||
|  | ||||
|     const handleClick = () => { | ||||
|       fileInputRef.current?.click(); | ||||
|     }; | ||||
|  | ||||
|     const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => { | ||||
|       const files = Array.from(event.target.files || []); | ||||
|       if (files.length && onFilesDrop) { | ||||
|         setFileToUpload(files[0]); | ||||
|         onFilesDrop(files); | ||||
|         setUploadMediaDialogOpen(true); | ||||
|       } | ||||
|  | ||||
|       // Сбрасываем значение input, чтобы можно было выбрать тот же файл снова | ||||
|       event.target.value = ""; | ||||
|     }; | ||||
|  | ||||
|     return ( | ||||
|       <> | ||||
|         <input | ||||
|           type="file" | ||||
|           ref={fileInputRef} | ||||
|           onChange={handleFileSelect} | ||||
|           accept="image/*,video/*,.glb,.gltf" | ||||
|           multiple | ||||
|           style={{ display: "none" }} | ||||
|         /> | ||||
|         <Box className="w-full flex flex-col items-center justify-center border rounded-md p-4"> | ||||
|           <div className="w-full flex flex-col items-center justify-center"> | ||||
|             <div | ||||
|               className={`w-full h-40 flex flex-col justify-center items-center text-gray-400 border-dashed border-2 rounded-md border-gray-400 p-4 cursor-pointer hover:bg-gray-50 ${ | ||||
|                 isDragging ? "bg-blue-100 border-blue-400" : "" | ||||
|               }`} | ||||
|               onDrop={handleDrop} | ||||
|               onDragOver={handleDragOver} | ||||
|               onDragLeave={handleDragLeave} | ||||
|               onClick={handleClick} | ||||
|             > | ||||
|               <Upload size={32} className="mb-2" /> | ||||
|               Перетащите медиа файлы сюда или нажмите для выбора | ||||
|             </div> | ||||
|             <div>или</div> | ||||
|             <Button | ||||
|               variant="contained" | ||||
|               color="primary" | ||||
|               onClick={() => setSelectMediaDialogOpen(true)} | ||||
|             > | ||||
|               Выбрать существующие медиа файлы | ||||
|             </Button> | ||||
|           </div> | ||||
|         </Box> | ||||
|  | ||||
|         <UploadMediaDialog | ||||
|           open={uploadMediaDialogOpen} | ||||
|           onClose={() => setUploadMediaDialogOpen(false)} | ||||
|           afterUploadSight={onFinishUpload} | ||||
|         /> | ||||
|         <SelectMediaDialog | ||||
|           open={selectMediaDialogOpen} | ||||
|           onClose={() => setSelectMediaDialogOpen(false)} | ||||
|           onSelectForSightMedia={onFinishUpload} | ||||
|         /> | ||||
|       </> | ||||
|     ); | ||||
|   } | ||||
| ); | ||||
| @@ -7,7 +7,13 @@ import { | ||||
|   MenuItem, | ||||
|   TextField, | ||||
| } from "@mui/material"; | ||||
| import { BackButton, createSightStore, languageStore, TabPanel } from "@shared"; | ||||
| import { | ||||
|   BackButton, | ||||
|   createSightStore, | ||||
|   languageStore, | ||||
|   SelectArticleModal, | ||||
|   TabPanel, | ||||
| } from "@shared"; | ||||
| import { | ||||
|   LanguageSwitcher, | ||||
|   ReactMarkdownComponent, | ||||
| @@ -16,6 +22,7 @@ import { | ||||
| import { ImagePlus, Plus } from "lucide-react"; | ||||
| import { observer } from "mobx-react-lite"; | ||||
| import { useState } from "react"; | ||||
| import { MediaViewer } from "../../MediaViewer/index"; | ||||
|  | ||||
| // --- RightWidgetTab (Parent) Component --- | ||||
| export const CreateRightTab = observer( | ||||
| @@ -24,10 +31,11 @@ export const CreateRightTab = observer( | ||||
|     const { sight, createNewRightArticle, updateRightArticleInfo } = | ||||
|       createSightStore; | ||||
|     const { language } = languageStore; | ||||
|  | ||||
|     const [articleDialogOpen, setArticleDialogOpen] = useState(false); | ||||
|     const [activeArticleIndex, setActiveArticleIndex] = useState<number | null>( | ||||
|       null | ||||
|     ); | ||||
|     const [type, setType] = useState<"article" | "media">("media"); | ||||
|     const open = Boolean(anchorEl); | ||||
|     const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { | ||||
|       setAnchorEl(event.currentTarget); | ||||
| @@ -66,9 +74,9 @@ export const CreateRightTab = observer( | ||||
|                   <Box className="flex flex-col gap-3 max-h-[60vh] overflow-y-auto"> | ||||
|                     <Box | ||||
|                       onClick={() => { | ||||
|                         // setMediaType("preview"); | ||||
|                         setType("media"); | ||||
|                       }} | ||||
|                       className="w-full  bg-gray-200 p-4 rounded-2xl cursor-pointer text-sm hover:bg-gray-300 transition-all duration-300" | ||||
|                       className="w-full  bg-green-200 p-4 rounded-2xl cursor-pointer text-sm hover:bg-gray-300 transition-all duration-300" | ||||
|                     > | ||||
|                       <Typography>Предпросмотр медиа</Typography> | ||||
|                     </Box> | ||||
| @@ -79,6 +87,7 @@ export const CreateRightTab = observer( | ||||
|                         className="w-full  bg-gray-200 p-4 rounded-2xl  text-sm cursor-pointer hover:bg-gray-300 transition-all duration-300" | ||||
|                         onClick={() => { | ||||
|                           handleSelectArticle(index); | ||||
|                           setType("article"); | ||||
|                         }} | ||||
|                       > | ||||
|                         <Typography>{article.heading}</Typography> | ||||
| @@ -113,6 +122,7 @@ export const CreateRightTab = observer( | ||||
|                     </MenuItem> | ||||
|                     <MenuItem | ||||
|                       onClick={() => { | ||||
|                         setArticleDialogOpen(true); | ||||
|                         handleClose(); | ||||
|                       }} | ||||
|                     > | ||||
| @@ -120,63 +130,65 @@ export const CreateRightTab = observer( | ||||
|                     </MenuItem> | ||||
|                   </Menu> | ||||
|                 </Box> | ||||
|                 <Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3"> | ||||
|                   {activeArticleIndex !== null && ( | ||||
|                     <> | ||||
|                       <Box className="flex justify-end gap-2 mb-3"> | ||||
|                         <Button variant="contained" color="primary"> | ||||
|                           Открепить | ||||
|                         </Button> | ||||
|                 {type === "article" && ( | ||||
|                   <Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3"> | ||||
|                     {activeArticleIndex !== null && ( | ||||
|                       <> | ||||
|                         <Box className="flex justify-end gap-2 mb-3"> | ||||
|                           <Button variant="contained" color="primary"> | ||||
|                             Открепить | ||||
|                           </Button> | ||||
|  | ||||
|                         <Button variant="contained" color="success"> | ||||
|                           Удалить | ||||
|                         </Button> | ||||
|                       </Box> | ||||
|                       <Box sx={{ display: "flex", gap: 3, flexGrow: 1 }}> | ||||
|                         {/* Левая колонка: Редактирование */} | ||||
|  | ||||
|                         <Box | ||||
|                           sx={{ | ||||
|                             flex: 2, | ||||
|                             display: "flex", | ||||
|                             flexDirection: "column", | ||||
|                             gap: 2, | ||||
|                           }} | ||||
|                         > | ||||
|                           <TextField | ||||
|                             label="Название информации" | ||||
|                             value={ | ||||
|                               sight[language].right[activeArticleIndex].heading | ||||
|                             } | ||||
|                             onChange={(e) => | ||||
|                               updateRightArticleInfo( | ||||
|                                 activeArticleIndex, | ||||
|                                 language, | ||||
|                                 e.target.value, | ||||
|                                 sight[language].right[activeArticleIndex].body | ||||
|                               ) | ||||
|                             } | ||||
|                             variant="outlined" | ||||
|                             fullWidth | ||||
|                           /> | ||||
|  | ||||
|                           <ReactMarkdownEditor | ||||
|                             value={ | ||||
|                               sight[language].right[activeArticleIndex].body | ||||
|                             } | ||||
|                             onChange={(value) => | ||||
|                               updateRightArticleInfo( | ||||
|                                 activeArticleIndex, | ||||
|                                 language, | ||||
|                                 sight[language].right[activeArticleIndex] | ||||
|                                   .heading, | ||||
|                                 value | ||||
|                               ) | ||||
|                             } | ||||
|                           /> | ||||
|                           <Button variant="contained" color="success"> | ||||
|                             Удалить | ||||
|                           </Button> | ||||
|                         </Box> | ||||
|                         {/* Блок МЕДИА для статьи */} | ||||
|                         {/* <Paper elevation={1} sx={{ padding: 2, mt: 1 }}> | ||||
|                         <Box sx={{ display: "flex", gap: 3, flexGrow: 1 }}> | ||||
|                           {/* Левая колонка: Редактирование */} | ||||
|  | ||||
|                           <Box | ||||
|                             sx={{ | ||||
|                               flex: 2, | ||||
|                               display: "flex", | ||||
|                               flexDirection: "column", | ||||
|                               gap: 2, | ||||
|                             }} | ||||
|                           > | ||||
|                             <TextField | ||||
|                               label="Название информации" | ||||
|                               value={ | ||||
|                                 sight[language].right[activeArticleIndex] | ||||
|                                   .heading | ||||
|                               } | ||||
|                               onChange={(e) => | ||||
|                                 updateRightArticleInfo( | ||||
|                                   activeArticleIndex, | ||||
|                                   language, | ||||
|                                   e.target.value, | ||||
|                                   sight[language].right[activeArticleIndex].body | ||||
|                                 ) | ||||
|                               } | ||||
|                               variant="outlined" | ||||
|                               fullWidth | ||||
|                             /> | ||||
|  | ||||
|                             <ReactMarkdownEditor | ||||
|                               value={ | ||||
|                                 sight[language].right[activeArticleIndex].body | ||||
|                               } | ||||
|                               onChange={(value) => | ||||
|                                 updateRightArticleInfo( | ||||
|                                   activeArticleIndex, | ||||
|                                   language, | ||||
|                                   sight[language].right[activeArticleIndex] | ||||
|                                     .heading, | ||||
|                                   value | ||||
|                                 ) | ||||
|                               } | ||||
|                             /> | ||||
|                           </Box> | ||||
|                           {/* Блок МЕДИА для статьи */} | ||||
|                           {/* <Paper elevation={1} sx={{ padding: 2, mt: 1 }}> | ||||
|                     <Typography variant="h6" gutterBottom> | ||||
|                       МЕДИА | ||||
|                     </Typography> | ||||
| @@ -241,10 +253,22 @@ export const CreateRightTab = observer( | ||||
|                       </Button> | ||||
|                     )} | ||||
|                   </Paper> */} | ||||
|                       </Box> | ||||
|                     </> | ||||
|                   )} | ||||
|                 </Box> | ||||
|                         </Box> | ||||
|                       </> | ||||
|                     )} | ||||
|                   </Box> | ||||
|                 )} | ||||
|                 {type === "media" && ( | ||||
|                   <Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3 flex justify-center items-center"> | ||||
|                     <MediaViewer | ||||
|                       media={{ | ||||
|                         id: sight.preview_media || "", | ||||
|                         media_type: 1, | ||||
|                         filename: sight.preview_media || "", | ||||
|                       }} | ||||
|                     /> | ||||
|                   </Box> | ||||
|                 )} | ||||
|               </Box> | ||||
|             </Box> | ||||
|             <Box className="w-[25%] mr-10"> | ||||
| @@ -264,7 +288,7 @@ export const CreateRightTab = observer( | ||||
|                       flexDirection: "column", | ||||
|                     }} | ||||
|                   > | ||||
|                     {false ? ( | ||||
|                     {type === "media" ? ( | ||||
|                       <Box | ||||
|                         sx={{ | ||||
|                           width: "100%", | ||||
| @@ -368,6 +392,12 @@ export const CreateRightTab = observer( | ||||
|           onSelectArticle={handleSelectArticle} | ||||
|           linkedArticleIds={linkedArticleIds} | ||||
|         /> */} | ||||
|         <SelectArticleModal | ||||
|           open={articleDialogOpen} | ||||
|           onClose={() => setArticleDialogOpen(false)} | ||||
|           onSelectArticle={handleSelectArticle} | ||||
|           linkedArticleIds={sight[language].right.map((article) => article.id)} | ||||
|         /> | ||||
|       </TabPanel> | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -313,6 +313,7 @@ export const InformationTab = observer( | ||||
|                         /> | ||||
|                       </Tooltip> | ||||
|                     </Box> | ||||
|  | ||||
|                     <Box | ||||
|                       sx={{ | ||||
|                         position: "relative", | ||||
|   | ||||
| @@ -13,37 +13,63 @@ import { | ||||
|   editSightStore, | ||||
|   languageStore, | ||||
|   SelectArticleModal, | ||||
|   SelectMediaDialog, | ||||
|   TabPanel, | ||||
|   UploadMediaDialog, | ||||
| } from "@shared"; | ||||
| import { | ||||
|   LanguageSwitcher, | ||||
|   MediaArea, | ||||
|   MediaAreaForSight, | ||||
|   ReactMarkdownComponent, | ||||
|   ReactMarkdownEditor, | ||||
| } from "@widgets"; | ||||
| import { ImagePlus, Plus } from "lucide-react"; | ||||
| import { ImagePlus, Plus, X } from "lucide-react"; | ||||
| import { observer } from "mobx-react-lite"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import { toast } from "react-toastify"; | ||||
| import { MediaViewer } from "../../MediaViewer/index"; | ||||
|  | ||||
| export const RightWidgetTab = observer( | ||||
|   ({ value, index }: { value: number; index: number }) => { | ||||
|     const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); | ||||
|     const { createNewRightArticle, updateRightArticleInfo } = createSightStore; | ||||
|     const { sight, getRightArticles, updateSight } = editSightStore; | ||||
|     const { language } = languageStore; | ||||
|  | ||||
|     const { | ||||
|       sight, | ||||
|       updateRightArticleInfo, | ||||
|       getRightArticles, | ||||
|       updateSight, | ||||
|       unlinkPreviewMedia, | ||||
|       linkPreviewMedia, | ||||
|       unlinkRightArticle, | ||||
|       deleteRightArticle, | ||||
|       linkArticle, | ||||
|       deleteRightArticleMedia, | ||||
|       createLinkWithRightArticle, | ||||
|       setFileToUpload, | ||||
|       createNewRightArticle, | ||||
|     } = editSightStore; | ||||
|  | ||||
|     const [uploadMediaOpen, setUploadMediaOpen] = useState(false); | ||||
|     const { language } = languageStore; | ||||
|     const [type, setType] = useState<"article" | "media">("media"); | ||||
|     useEffect(() => { | ||||
|       if (sight.common.id) { | ||||
|         getRightArticles(sight.common.id); | ||||
|       } | ||||
|       const fetchData = async () => { | ||||
|         if (sight.common.id) { | ||||
|           await getRightArticles(sight.common.id); | ||||
|         } | ||||
|       }; | ||||
|       fetchData(); | ||||
|       console.log(sight[language].right); | ||||
|     }, [sight.common.id]); | ||||
|  | ||||
|     const [activeArticleIndex, setActiveArticleIndex] = useState<number | null>( | ||||
|       null | ||||
|     ); | ||||
|     const [isSelectModalOpen, setIsSelectModalOpen] = useState(false); | ||||
|  | ||||
|     const [isSelectMediaModalOpen, setIsSelectMediaModalOpen] = useState(false); | ||||
|     const open = Boolean(anchorEl); | ||||
|  | ||||
|     const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { | ||||
|       setAnchorEl(event.currentTarget); | ||||
|     }; | ||||
| @@ -69,16 +95,32 @@ export const RightWidgetTab = observer( | ||||
|       setIsSelectModalOpen(false); | ||||
|     }; | ||||
|  | ||||
|     const handleArticleSelect = () => { | ||||
|       // TODO: Implement article selection logic | ||||
|     const handleArticleSelect = (id: number) => { | ||||
|       linkArticle(id); | ||||
|       handleCloseSelectModal(); | ||||
|     }; | ||||
|  | ||||
|     const handleMediaSelected = async (media: { | ||||
|       id: string; | ||||
|       filename: string; | ||||
|       media_name?: string; | ||||
|       media_type: number; | ||||
|     }) => { | ||||
|       await createLinkWithRightArticle( | ||||
|         media, | ||||
|         sight[language].right[activeArticleIndex || 0].id | ||||
|       ); | ||||
|     }; | ||||
|  | ||||
|     const handleSave = async () => { | ||||
|       await updateSight(); | ||||
|       toast.success("Достопримечательность сохранена"); | ||||
|     }; | ||||
|  | ||||
|     useEffect(() => { | ||||
|       console.log(sight[language].right); | ||||
|     }, [sight[language].right]); | ||||
|  | ||||
|     return ( | ||||
|       <TabPanel value={value} index={index}> | ||||
|         <LanguageSwitcher /> | ||||
| @@ -99,23 +141,26 @@ export const RightWidgetTab = observer( | ||||
|             <Box className="flex flex-col w-[75%] gap-2"> | ||||
|               <Box className="w-full flex gap-2"> | ||||
|                 <Box className="relative w-[20%] h-[70vh] flex flex-col rounded-2xl overflow-y-auto gap-3 border border-gray-300 p-3"> | ||||
|                   <Box className="flex flex-col gap-3 max-h-[60vh] overflow-y-auto"> | ||||
|                   <Box className="flex flex-col gap-3 max-h-[60vh] overflow-y-auto "> | ||||
|                     <Box | ||||
|                       // onClick={() => setMediaType("preview")} | ||||
|                       className="w-full bg-gray-200 p-4 rounded-2xl cursor-pointer text-sm hover:bg-gray-300 transition-all duration-300" | ||||
|                       onClick={() => setType("media")} | ||||
|                       className="w-full  bg-green-200 p-4 rounded-2xl cursor-pointer text-sm hover:bg-gray-300 transition-all duration-300" | ||||
|                     > | ||||
|                       <Typography>Предпросмотр медиа</Typography> | ||||
|                     </Box> | ||||
|  | ||||
|                     {sight[language].right.map((article, index) => ( | ||||
|                       <Box | ||||
|                         key={index} | ||||
|                         className="w-full bg-gray-200 p-4 rounded-2xl text-sm cursor-pointer hover:bg-gray-300 transition-all duration-300" | ||||
|                         onClick={() => handleSelectArticle(index)} | ||||
|                       > | ||||
|                         <Typography>{article.heading}</Typography> | ||||
|                       </Box> | ||||
|                     ))} | ||||
|                     {sight[language].right.length > 0 && | ||||
|                       sight[language].right.map((article, index) => ( | ||||
|                         <Box | ||||
|                           key={index} | ||||
|                           className="w-full bg-gray-200 p-4 rounded-2xl text-sm cursor-pointer hover:bg-gray-300 transition-all duration-300" | ||||
|                           onClick={() => { | ||||
|                             handleSelectArticle(index); | ||||
|                             setType("article"); | ||||
|                           }} | ||||
|                         > | ||||
|                           <Typography>{article.heading}</Typography> | ||||
|                         </Box> | ||||
|                       ))} | ||||
|                   </Box> | ||||
|                   <button | ||||
|                     className="w-10 h-10 bg-blue-500 rounded-full absolute bottom-5 left-5 flex items-center justify-center" | ||||
| @@ -142,147 +187,225 @@ export const RightWidgetTab = observer( | ||||
|                   </Menu> | ||||
|                 </Box> | ||||
|  | ||||
|                 <Box className="w-[80%] border border-gray-300 rounded-2xl p-3"> | ||||
|                   {activeArticleIndex !== null && ( | ||||
|                     <> | ||||
|                       <Box className="flex justify-end gap-2 mb-3"> | ||||
|                         <Button variant="contained" color="primary"> | ||||
|                           Открепить | ||||
|                         </Button> | ||||
|                         <Button variant="contained" color="success"> | ||||
|                           Удалить | ||||
|                         </Button> | ||||
|                       </Box> | ||||
|                       <Box sx={{ display: "flex", gap: 3, flexGrow: 1 }}> | ||||
|                         <Box | ||||
|                           sx={{ | ||||
|                             flex: 2, | ||||
|                             display: "flex", | ||||
|                             flexDirection: "column", | ||||
|                             gap: 2, | ||||
|                             maxHeight: "70%", | ||||
|                           }} | ||||
|                         > | ||||
|                           <TextField | ||||
|                             label="Название информации" | ||||
|                             value={ | ||||
|                               sight[language].right[activeArticleIndex].heading | ||||
|                             } | ||||
|                             onChange={(e) => | ||||
|                               updateRightArticleInfo( | ||||
|                                 activeArticleIndex, | ||||
|                                 language, | ||||
|                                 e.target.value, | ||||
|                                 sight[language].right[activeArticleIndex].body | ||||
|                               ) | ||||
|                             } | ||||
|                             variant="outlined" | ||||
|                             fullWidth | ||||
|                           /> | ||||
|  | ||||
|                           <ReactMarkdownEditor | ||||
|                             value={ | ||||
|                               sight[language].right[activeArticleIndex].body | ||||
|                             } | ||||
|                             onChange={(value) => | ||||
|                               updateRightArticleInfo( | ||||
|                                 activeArticleIndex, | ||||
|                                 language, | ||||
|                                 sight[language].right[activeArticleIndex] | ||||
|                                   .heading, | ||||
|                                 value | ||||
|                               ) | ||||
|                             } | ||||
|                           /> | ||||
|                           {/* <MediaArea | ||||
|                             articleId={1} | ||||
|                             mediaIds={[]} | ||||
|                             deleteMedia={() => {}} | ||||
|                           /> */} | ||||
|                 {type === "article" && ( | ||||
|                   <Box className="w-[80%] border border-gray-300 rounded-2xl p-3"> | ||||
|                     {activeArticleIndex !== null && ( | ||||
|                       <> | ||||
|                         <Box className="flex justify-end gap-2 mb-3"> | ||||
|                           <Button | ||||
|                             variant="contained" | ||||
|                             color="primary" | ||||
|                             onClick={() => { | ||||
|                               unlinkRightArticle( | ||||
|                                 sight[language].right[activeArticleIndex].id | ||||
|                               ); | ||||
|                               setActiveArticleIndex(null); | ||||
|                             }} | ||||
|                           > | ||||
|                             Открепить | ||||
|                           </Button> | ||||
|                           <Button | ||||
|                             variant="contained" | ||||
|                             color="success" | ||||
|                             onClick={() => { | ||||
|                               deleteRightArticle( | ||||
|                                 sight[language].right[activeArticleIndex].id | ||||
|                               ); | ||||
|                               setActiveArticleIndex(null); | ||||
|                             }} | ||||
|                           > | ||||
|                             Удалить | ||||
|                           </Button> | ||||
|                         </Box> | ||||
|                       </Box> | ||||
|                     </> | ||||
|                   )} | ||||
|                 </Box> | ||||
|                         <Box sx={{ display: "flex", gap: 3, flexGrow: 1 }}> | ||||
|                           <Box | ||||
|                             sx={{ | ||||
|                               flex: 2, | ||||
|                               display: "flex", | ||||
|                               flexDirection: "column", | ||||
|                               gap: 2, | ||||
|                               maxHeight: "70%", | ||||
|                             }} | ||||
|                           > | ||||
|                             <TextField | ||||
|                               label="Название информации" | ||||
|                               value={ | ||||
|                                 sight[language].right[activeArticleIndex] | ||||
|                                   .heading | ||||
|                               } | ||||
|                               onChange={(e) => | ||||
|                                 updateRightArticleInfo( | ||||
|                                   activeArticleIndex, | ||||
|                                   language, | ||||
|                                   e.target.value, | ||||
|                                   sight[language].right[activeArticleIndex].body | ||||
|                                 ) | ||||
|                               } | ||||
|                               variant="outlined" | ||||
|                               fullWidth | ||||
|                             /> | ||||
|  | ||||
|                             <ReactMarkdownEditor | ||||
|                               value={ | ||||
|                                 sight[language].right[activeArticleIndex].body | ||||
|                               } | ||||
|                               onChange={(value) => | ||||
|                                 updateRightArticleInfo( | ||||
|                                   activeArticleIndex, | ||||
|                                   language, | ||||
|                                   sight[language].right[activeArticleIndex] | ||||
|                                     .heading, | ||||
|                                   value | ||||
|                                 ) | ||||
|                               } | ||||
|                             /> | ||||
|  | ||||
|                             <MediaArea | ||||
|                               articleId={ | ||||
|                                 sight[language].right[activeArticleIndex].id | ||||
|                               } | ||||
|                               mediaIds={ | ||||
|                                 sight[language].right[activeArticleIndex].media | ||||
|                               } | ||||
|                               onFilesDrop={(files) => { | ||||
|                                 setFileToUpload(files[0]); | ||||
|                                 setUploadMediaOpen(true); | ||||
|                               }} | ||||
|                               deleteMedia={deleteRightArticleMedia} | ||||
|                               setSelectMediaDialogOpen={() => { | ||||
|                                 setIsSelectMediaModalOpen(true); | ||||
|                               }} | ||||
|                             /> | ||||
|                           </Box> | ||||
|                         </Box> | ||||
|                       </> | ||||
|                     )} | ||||
|                   </Box> | ||||
|                 )} | ||||
|                 {type === "media" && ( | ||||
|                   <Box className="w-[80%] border border-gray-300 rounded-2xl relative"> | ||||
|                     {sight.common.preview_media && ( | ||||
|                       <> | ||||
|                         <Box className="absolute top-4 right-4"> | ||||
|                           <button | ||||
|                             className="w-10 h-10 flex items-center justify-center" | ||||
|                             onClick={unlinkPreviewMedia} | ||||
|                           > | ||||
|                             <X size={20} color="red" /> | ||||
|                           </button> | ||||
|                         </Box> | ||||
|  | ||||
|                         <MediaViewer | ||||
|                           media={{ | ||||
|                             id: sight.common.preview_media || "", | ||||
|                             media_type: 1, | ||||
|                             filename: sight.common.preview_media || "", | ||||
|                           }} | ||||
|                         /> | ||||
|                       </> | ||||
|                     )} | ||||
|                     {!sight.common.preview_media && ( | ||||
|                       <MediaAreaForSight | ||||
|                         onFinishUpload={(mediaId) => { | ||||
|                           linkPreviewMedia(mediaId); | ||||
|                         }} | ||||
|                         onFilesDrop={() => {}} | ||||
|                       /> | ||||
|                     )} | ||||
|                   </Box> | ||||
|                 )} | ||||
|               </Box> | ||||
|             </Box> | ||||
|  | ||||
|             <Box className="w-[25%] mr-10"> | ||||
|               {activeArticleIndex !== null && ( | ||||
|                 <Paper | ||||
|                   className="flex-1 flex flex-col rounded-2xl" | ||||
|                   elevation={2} | ||||
|                 > | ||||
|                   <Box | ||||
|                     className="rounded-2xl overflow-hidden" | ||||
|                     sx={{ | ||||
|                       width: "100%", | ||||
|                       height: "75vh", | ||||
|                       background: "#877361", | ||||
|                       borderColor: "grey.300", | ||||
|                       display: "flex", | ||||
|                       flexDirection: "column", | ||||
|                     }} | ||||
|             {type === "article" && ( | ||||
|               <Box className="w-[25%] mr-10"> | ||||
|                 {activeArticleIndex !== null && ( | ||||
|                   <Paper | ||||
|                     className="flex-1 flex flex-col rounded-2xl" | ||||
|                     elevation={2} | ||||
|                   > | ||||
|                     <Box | ||||
|                       className="rounded-2xl overflow-hidden" | ||||
|                       sx={{ | ||||
|                         width: "100%", | ||||
|                         height: 200, | ||||
|                         flexShrink: 0, | ||||
|                         backgroundColor: "rgba(0,0,0,0.1)", | ||||
|                         display: "flex", | ||||
|                         alignItems: "center", | ||||
|                         justifyContent: "center", | ||||
|                       }} | ||||
|                     > | ||||
|                       <ImagePlus size={48} color="white" /> | ||||
|                     </Box> | ||||
|  | ||||
|                     <Box | ||||
|                       sx={{ | ||||
|                         width: "100%", | ||||
|                         minHeight: "70px", | ||||
|                         height: "75vh", | ||||
|                         background: "#877361", | ||||
|                         borderColor: "grey.300", | ||||
|                         display: "flex", | ||||
|                         flexShrink: 0, | ||||
|                         alignItems: "center", | ||||
|                         borderBottom: "1px solid rgba(255,255,255,0.1)", | ||||
|                         px: 2, | ||||
|                         flexDirection: "column", | ||||
|                       }} | ||||
|                     > | ||||
|                       <Typography variant="h6" color="white"> | ||||
|                         {sight[language].right[activeArticleIndex].heading || | ||||
|                           "Выберите статью"} | ||||
|                       </Typography> | ||||
|                     </Box> | ||||
|  | ||||
|                     <Box | ||||
|                       sx={{ | ||||
|                         px: 2, | ||||
|                         flexGrow: 1, | ||||
|                         overflowY: "auto", | ||||
|                         backgroundColor: "#877361", | ||||
|                         color: "white", | ||||
|                         py: 1, | ||||
|                       }} | ||||
|                     > | ||||
|                       {sight[language].right[activeArticleIndex].body ? ( | ||||
|                         <ReactMarkdownComponent | ||||
|                           value={sight[language].right[activeArticleIndex].body} | ||||
|                       {sight[language].right[activeArticleIndex].media.length > | ||||
|                       0 ? ( | ||||
|                         <MediaViewer | ||||
|                           media={ | ||||
|                             sight[language].right[activeArticleIndex].media[0] | ||||
|                           } | ||||
|                         /> | ||||
|                       ) : ( | ||||
|                         <Typography | ||||
|                           color="rgba(255,255,255,0.7)" | ||||
|                           sx={{ textAlign: "center", mt: 4 }} | ||||
|                         <Box | ||||
|                           sx={{ | ||||
|                             width: "100%", | ||||
|                             height: 200, | ||||
|                             flexShrink: 0, | ||||
|                             backgroundColor: "rgba(0,0,0,0.1)", | ||||
|                             display: "flex", | ||||
|                             alignItems: "center", | ||||
|                             justifyContent: "center", | ||||
|                           }} | ||||
|                         > | ||||
|                           Предпросмотр статьи появится здесь | ||||
|                         </Typography> | ||||
|                           <ImagePlus size={48} color="white" /> | ||||
|                         </Box> | ||||
|                       )} | ||||
|  | ||||
|                       <Box | ||||
|                         sx={{ | ||||
|                           width: "100%", | ||||
|                           minHeight: "70px", | ||||
|                           background: "#877361", | ||||
|                           display: "flex", | ||||
|                           flexShrink: 0, | ||||
|                           alignItems: "center", | ||||
|                           borderBottom: "1px solid rgba(255,255,255,0.1)", | ||||
|                           px: 2, | ||||
|                         }} | ||||
|                       > | ||||
|                         <Typography variant="h6" color="white"> | ||||
|                           {sight[language].right[activeArticleIndex].heading || | ||||
|                             "Выберите статью"} | ||||
|                         </Typography> | ||||
|                       </Box> | ||||
|  | ||||
|                       <Box | ||||
|                         sx={{ | ||||
|                           px: 2, | ||||
|                           flexGrow: 1, | ||||
|                           overflowY: "auto", | ||||
|                           backgroundColor: "#877361", | ||||
|                           color: "white", | ||||
|                           py: 1, | ||||
|                         }} | ||||
|                       > | ||||
|                         {sight[language].right[activeArticleIndex].body ? ( | ||||
|                           <ReactMarkdownComponent | ||||
|                             value={ | ||||
|                               sight[language].right[activeArticleIndex].body | ||||
|                             } | ||||
|                           /> | ||||
|                         ) : ( | ||||
|                           <Typography | ||||
|                             color="rgba(255,255,255,0.7)" | ||||
|                             sx={{ textAlign: "center", mt: 4 }} | ||||
|                           > | ||||
|                             Предпросмотр статьи появится здесь | ||||
|                           </Typography> | ||||
|                         )} | ||||
|                       </Box> | ||||
|                     </Box> | ||||
|                   </Box> | ||||
|                 </Paper> | ||||
|               )} | ||||
|             </Box> | ||||
|                   </Paper> | ||||
|                 )} | ||||
|               </Box> | ||||
|             )} | ||||
|           </Box> | ||||
|  | ||||
|           <Box | ||||
| @@ -303,11 +426,29 @@ export const RightWidgetTab = observer( | ||||
|           </Box> | ||||
|         </Box> | ||||
|  | ||||
|         <UploadMediaDialog | ||||
|           open={uploadMediaOpen} | ||||
|           onClose={() => setUploadMediaOpen(false)} | ||||
|           afterUpload={async (media) => { | ||||
|             setUploadMediaOpen(false); | ||||
|             setFileToUpload(null); | ||||
|             await createLinkWithRightArticle( | ||||
|               media, | ||||
|               sight[language].right[activeArticleIndex || 0].id | ||||
|             ); | ||||
|           }} | ||||
|         /> | ||||
|  | ||||
|         <SelectArticleModal | ||||
|           open={isSelectModalOpen} | ||||
|           onClose={handleCloseSelectModal} | ||||
|           onSelectArticle={handleArticleSelect} | ||||
|         /> | ||||
|         <SelectMediaDialog | ||||
|           open={isSelectMediaModalOpen} | ||||
|           onClose={() => setIsSelectMediaModalOpen(false)} | ||||
|           onSelectMedia={handleMediaSelected} | ||||
|         /> | ||||
|       </TabPanel> | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -10,3 +10,4 @@ export * from "./SightsTable"; | ||||
| export * from "./MediaViewer"; | ||||
| export * from "./MediaArea"; | ||||
| export * from "./ModelViewer3D"; | ||||
| export * from "./MediaAreaForSight"; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user