Add correct edit right widget
This commit is contained in:
parent
5814e65953
commit
f0e03c3a1d
@ -6,7 +6,6 @@ const authInstance = axios.create({
|
|||||||
});
|
});
|
||||||
|
|
||||||
authInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
authInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||||
console.log(config);
|
|
||||||
config.headers.Authorization = `Bearer ${localStorage.getItem("token")}`;
|
config.headers.Authorization = `Bearer ${localStorage.getItem("token")}`;
|
||||||
config.headers["X-Language"] = languageStore.language ?? "ru";
|
config.headers["X-Language"] = languageStore.language ?? "ru";
|
||||||
return config;
|
return config;
|
||||||
|
@ -21,12 +21,13 @@ import { MediaViewer } from "@widgets";
|
|||||||
interface SelectMediaDialogProps {
|
interface SelectMediaDialogProps {
|
||||||
open: boolean; // Corrected prop name
|
open: boolean; // Corrected prop name
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSelectMedia: (media: {
|
onSelectMedia?: (media: {
|
||||||
id: string;
|
id: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
media_name?: string;
|
media_name?: string;
|
||||||
media_type: number;
|
media_type: number;
|
||||||
}) => void; // Renamed from onSelectArticle
|
}) => void; // Renamed from onSelectArticle
|
||||||
|
onSelectForSightMedia?: (mediaId: string) => void;
|
||||||
linkedMediaIds?: string[]; // Renamed from linkedArticleIds, assuming it refers to media already in use
|
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
|
open, // Corrected prop name
|
||||||
onClose,
|
onClose,
|
||||||
onSelectMedia, // Renamed prop
|
onSelectMedia, // Renamed prop
|
||||||
|
onSelectForSightMedia,
|
||||||
linkedMediaIds = [], // Default to empty array if not provided, renamed
|
linkedMediaIds = [], // Default to empty array if not provided, renamed
|
||||||
}: SelectMediaDialogProps) => {
|
}: SelectMediaDialogProps) => {
|
||||||
const { media, getMedia } = mediaStore;
|
const { media, getMedia } = mediaStore;
|
||||||
@ -55,8 +57,12 @@ export const SelectMediaDialog = observer(
|
|||||||
if (hoveredMediaId) {
|
if (hoveredMediaId) {
|
||||||
const mediaItem = media.find((m) => m.id === hoveredMediaId);
|
const mediaItem = media.find((m) => m.id === hoveredMediaId);
|
||||||
if (mediaItem) {
|
if (mediaItem) {
|
||||||
|
if (onSelectForSightMedia) {
|
||||||
|
onSelectForSightMedia(mediaItem.id);
|
||||||
|
} else if (onSelectMedia) {
|
||||||
onSelectMedia(mediaItem);
|
onSelectMedia(mediaItem);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,7 +120,11 @@ export const SelectMediaDialog = observer(
|
|||||||
key={mediaItem.id}
|
key={mediaItem.id}
|
||||||
onClick={() => setHoveredMediaId(mediaItem.id)} // Call onSelectMedia
|
onClick={() => setHoveredMediaId(mediaItem.id)} // Call onSelectMedia
|
||||||
onDoubleClick={() => {
|
onDoubleClick={() => {
|
||||||
|
if (onSelectForSightMedia) {
|
||||||
|
onSelectForSightMedia(mediaItem.id);
|
||||||
|
} else if (onSelectMedia) {
|
||||||
onSelectMedia(mediaItem);
|
onSelectMedia(mediaItem);
|
||||||
|
}
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -24,16 +24,22 @@ import { ModelViewer3D } from "@widgets";
|
|||||||
interface UploadMediaDialogProps {
|
interface UploadMediaDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
afterUpload: (media: {
|
afterUpload?: (media: {
|
||||||
id: string;
|
id: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
media_name?: string;
|
media_name?: string;
|
||||||
media_type: number;
|
media_type: number;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
afterUploadSight?: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UploadMediaDialog = observer(
|
export const UploadMediaDialog = observer(
|
||||||
({ open, onClose, afterUpload }: UploadMediaDialogProps) => {
|
({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
afterUpload,
|
||||||
|
afterUploadSight,
|
||||||
|
}: UploadMediaDialogProps) => {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [success, setSuccess] = useState(false);
|
const [success, setSuccess] = useState(false);
|
||||||
@ -103,8 +109,12 @@ export const UploadMediaDialog = observer(
|
|||||||
mediaName
|
mediaName
|
||||||
);
|
);
|
||||||
if (media) {
|
if (media) {
|
||||||
|
if (afterUploadSight) {
|
||||||
|
await afterUploadSight(media.id);
|
||||||
|
} else if (afterUpload) {
|
||||||
await afterUpload(media);
|
await afterUpload(media);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to save media");
|
setError(err instanceof Error ? err.message : "Failed to save media");
|
||||||
|
@ -22,7 +22,7 @@ type SightLanguageInfo = {
|
|||||||
media_type: number;
|
media_type: number;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
right: { heading: string; body: string }[];
|
right: { id: number; heading: string; body: string; media: [] }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type SightCommonInfo = {
|
type SightCommonInfo = {
|
||||||
@ -90,19 +90,43 @@ class CreateSightStore {
|
|||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
createNewRightArticle = () => {
|
createNewRightArticle = async () => {
|
||||||
this.sight.ru.right.push({
|
const articleId = await languageInstance("ru").post("/article", {
|
||||||
heading: "Введите русский заголовок",
|
heading: "Введите русский заголовок",
|
||||||
body: "Введите русский текст",
|
body: "Введите русский текст",
|
||||||
});
|
});
|
||||||
this.sight.en.right.push({
|
const { id } = articleId.data;
|
||||||
|
await languageInstance("en").patch(`/article/${id}`, {
|
||||||
heading: "Enter the English heading",
|
heading: "Enter the English heading",
|
||||||
body: "Enter the English text",
|
body: "Enter the English text",
|
||||||
});
|
});
|
||||||
this.sight.zh.right.push({
|
await languageInstance("zh").patch(`/article/${id}`, {
|
||||||
heading: "Введите китайский заголовок",
|
heading: "Введите китайский заголовок",
|
||||||
body: "Введите китайский текст",
|
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) => {
|
updateLeftInfo = (language: Language, heading: string, body: string) => {
|
||||||
@ -444,6 +468,32 @@ class CreateSightStore {
|
|||||||
filename: media.filename,
|
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();
|
export const createSightStore = new CreateSightStore();
|
||||||
|
@ -11,7 +11,12 @@ export type SightLanguageInfo = {
|
|||||||
body: string;
|
body: string;
|
||||||
media: { id: string; media_type: number; filename: 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 = {
|
export type SightCommonInfo = {
|
||||||
@ -111,21 +116,42 @@ class EditSightStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getRightArticles = async (id: number) => {
|
getRightArticles = async (id: number) => {
|
||||||
const responseRu = await languageInstance("ru").get(`/sight/${id}/article`);
|
let responseRu = await languageInstance("ru").get(`/sight/${id}/article`);
|
||||||
const responseEn = await languageInstance("en").get(`/sight/${id}/article`);
|
let responseEn = await languageInstance("en").get(`/sight/${id}/article`);
|
||||||
const responseZh = await languageInstance("zh").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 = {
|
const data = {
|
||||||
ru: {
|
ru: {
|
||||||
right: responseRu.data,
|
right: ruArticlesWithMedia,
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
right: responseEn.data,
|
right: enArticlesWithMedia,
|
||||||
},
|
},
|
||||||
zh: {
|
zh: {
|
||||||
right: responseZh.data,
|
right: zhArticlesWithMedia,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.sight = {
|
this.sight = {
|
||||||
...this.sight,
|
...this.sight,
|
||||||
@ -137,7 +163,6 @@ class EditSightStore {
|
|||||||
...this.sight.en,
|
...this.sight.en,
|
||||||
right: data.en.right,
|
right: data.en.right,
|
||||||
},
|
},
|
||||||
|
|
||||||
zh: {
|
zh: {
|
||||||
...this.sight.zh,
|
...this.sight.zh,
|
||||||
right: data.zh.right,
|
right: data.zh.right,
|
||||||
@ -279,8 +304,13 @@ class EditSightStore {
|
|||||||
left_article: createdLeftArticleId,
|
left_article: createdLeftArticleId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.sight.common.left_article == 0) {
|
for (const language of ["ru", "en", "zh"] as Language[]) {
|
||||||
return;
|
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(
|
// 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;
|
uploadMediaOpen = false;
|
||||||
setUploadMediaOpen = (open: boolean) => {
|
setUploadMediaOpen = (open: boolean) => {
|
||||||
this.uploadMediaOpen = open;
|
this.uploadMediaOpen = open;
|
||||||
@ -442,6 +504,168 @@ class EditSightStore {
|
|||||||
filename: media.filename,
|
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();
|
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,
|
MenuItem,
|
||||||
TextField,
|
TextField,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { BackButton, createSightStore, languageStore, TabPanel } from "@shared";
|
import {
|
||||||
|
BackButton,
|
||||||
|
createSightStore,
|
||||||
|
languageStore,
|
||||||
|
SelectArticleModal,
|
||||||
|
TabPanel,
|
||||||
|
} from "@shared";
|
||||||
import {
|
import {
|
||||||
LanguageSwitcher,
|
LanguageSwitcher,
|
||||||
ReactMarkdownComponent,
|
ReactMarkdownComponent,
|
||||||
@ -16,6 +22,7 @@ import {
|
|||||||
import { ImagePlus, Plus } from "lucide-react";
|
import { ImagePlus, Plus } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { MediaViewer } from "../../MediaViewer/index";
|
||||||
|
|
||||||
// --- RightWidgetTab (Parent) Component ---
|
// --- RightWidgetTab (Parent) Component ---
|
||||||
export const CreateRightTab = observer(
|
export const CreateRightTab = observer(
|
||||||
@ -24,10 +31,11 @@ export const CreateRightTab = observer(
|
|||||||
const { sight, createNewRightArticle, updateRightArticleInfo } =
|
const { sight, createNewRightArticle, updateRightArticleInfo } =
|
||||||
createSightStore;
|
createSightStore;
|
||||||
const { language } = languageStore;
|
const { language } = languageStore;
|
||||||
|
const [articleDialogOpen, setArticleDialogOpen] = useState(false);
|
||||||
const [activeArticleIndex, setActiveArticleIndex] = useState<number | null>(
|
const [activeArticleIndex, setActiveArticleIndex] = useState<number | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
const [type, setType] = useState<"article" | "media">("media");
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
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 className="flex flex-col gap-3 max-h-[60vh] overflow-y-auto">
|
||||||
<Box
|
<Box
|
||||||
onClick={() => {
|
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>
|
<Typography>Предпросмотр медиа</Typography>
|
||||||
</Box>
|
</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"
|
className="w-full bg-gray-200 p-4 rounded-2xl text-sm cursor-pointer hover:bg-gray-300 transition-all duration-300"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSelectArticle(index);
|
handleSelectArticle(index);
|
||||||
|
setType("article");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{article.heading}</Typography>
|
<Typography>{article.heading}</Typography>
|
||||||
@ -113,6 +122,7 @@ export const CreateRightTab = observer(
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
setArticleDialogOpen(true);
|
||||||
handleClose();
|
handleClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -120,6 +130,7 @@ export const CreateRightTab = observer(
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Box>
|
</Box>
|
||||||
|
{type === "article" && (
|
||||||
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3">
|
<Box className="w-[80%] h-[70vh] border border-gray-300 rounded-2xl p-3">
|
||||||
{activeArticleIndex !== null && (
|
{activeArticleIndex !== null && (
|
||||||
<>
|
<>
|
||||||
@ -146,7 +157,8 @@ export const CreateRightTab = observer(
|
|||||||
<TextField
|
<TextField
|
||||||
label="Название информации"
|
label="Название информации"
|
||||||
value={
|
value={
|
||||||
sight[language].right[activeArticleIndex].heading
|
sight[language].right[activeArticleIndex]
|
||||||
|
.heading
|
||||||
}
|
}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateRightArticleInfo(
|
updateRightArticleInfo(
|
||||||
@ -245,6 +257,18 @@ export const CreateRightTab = observer(
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</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>
|
</Box>
|
||||||
<Box className="w-[25%] mr-10">
|
<Box className="w-[25%] mr-10">
|
||||||
@ -264,7 +288,7 @@ export const CreateRightTab = observer(
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{false ? (
|
{type === "media" ? (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -368,6 +392,12 @@ export const CreateRightTab = observer(
|
|||||||
onSelectArticle={handleSelectArticle}
|
onSelectArticle={handleSelectArticle}
|
||||||
linkedArticleIds={linkedArticleIds}
|
linkedArticleIds={linkedArticleIds}
|
||||||
/> */}
|
/> */}
|
||||||
|
<SelectArticleModal
|
||||||
|
open={articleDialogOpen}
|
||||||
|
onClose={() => setArticleDialogOpen(false)}
|
||||||
|
onSelectArticle={handleSelectArticle}
|
||||||
|
linkedArticleIds={sight[language].right.map((article) => article.id)}
|
||||||
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -313,6 +313,7 @@ export const InformationTab = observer(
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
@ -13,37 +13,63 @@ import {
|
|||||||
editSightStore,
|
editSightStore,
|
||||||
languageStore,
|
languageStore,
|
||||||
SelectArticleModal,
|
SelectArticleModal,
|
||||||
|
SelectMediaDialog,
|
||||||
TabPanel,
|
TabPanel,
|
||||||
|
UploadMediaDialog,
|
||||||
} from "@shared";
|
} from "@shared";
|
||||||
import {
|
import {
|
||||||
LanguageSwitcher,
|
LanguageSwitcher,
|
||||||
|
MediaArea,
|
||||||
|
MediaAreaForSight,
|
||||||
ReactMarkdownComponent,
|
ReactMarkdownComponent,
|
||||||
ReactMarkdownEditor,
|
ReactMarkdownEditor,
|
||||||
} from "@widgets";
|
} from "@widgets";
|
||||||
import { ImagePlus, Plus } from "lucide-react";
|
import { ImagePlus, Plus, X } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
import { MediaViewer } from "../../MediaViewer/index";
|
||||||
|
|
||||||
export const RightWidgetTab = observer(
|
export const RightWidgetTab = observer(
|
||||||
({ value, index }: { value: number; index: number }) => {
|
({ value, index }: { value: number; index: number }) => {
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
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(() => {
|
useEffect(() => {
|
||||||
|
const fetchData = async () => {
|
||||||
if (sight.common.id) {
|
if (sight.common.id) {
|
||||||
getRightArticles(sight.common.id);
|
await getRightArticles(sight.common.id);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
fetchData();
|
||||||
|
console.log(sight[language].right);
|
||||||
}, [sight.common.id]);
|
}, [sight.common.id]);
|
||||||
|
|
||||||
const [activeArticleIndex, setActiveArticleIndex] = useState<number | null>(
|
const [activeArticleIndex, setActiveArticleIndex] = useState<number | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [isSelectModalOpen, setIsSelectModalOpen] = useState(false);
|
const [isSelectModalOpen, setIsSelectModalOpen] = useState(false);
|
||||||
|
const [isSelectMediaModalOpen, setIsSelectMediaModalOpen] = useState(false);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
@ -69,16 +95,32 @@ export const RightWidgetTab = observer(
|
|||||||
setIsSelectModalOpen(false);
|
setIsSelectModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleArticleSelect = () => {
|
const handleArticleSelect = (id: number) => {
|
||||||
// TODO: Implement article selection logic
|
linkArticle(id);
|
||||||
handleCloseSelectModal();
|
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 () => {
|
const handleSave = async () => {
|
||||||
await updateSight();
|
await updateSight();
|
||||||
toast.success("Достопримечательность сохранена");
|
toast.success("Достопримечательность сохранена");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(sight[language].right);
|
||||||
|
}, [sight[language].right]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TabPanel value={value} index={index}>
|
<TabPanel value={value} index={index}>
|
||||||
<LanguageSwitcher />
|
<LanguageSwitcher />
|
||||||
@ -101,17 +143,20 @@ export const RightWidgetTab = observer(
|
|||||||
<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="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
|
<Box
|
||||||
// onClick={() => setMediaType("preview")}
|
onClick={() => 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>
|
<Typography>Предпросмотр медиа</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
{sight[language].right.length > 0 &&
|
||||||
{sight[language].right.map((article, index) => (
|
sight[language].right.map((article, index) => (
|
||||||
<Box
|
<Box
|
||||||
key={index}
|
key={index}
|
||||||
className="w-full bg-gray-200 p-4 rounded-2xl text-sm cursor-pointer hover:bg-gray-300 transition-all duration-300"
|
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)}
|
onClick={() => {
|
||||||
|
handleSelectArticle(index);
|
||||||
|
setType("article");
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{article.heading}</Typography>
|
<Typography>{article.heading}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -142,14 +187,33 @@ export const RightWidgetTab = observer(
|
|||||||
</Menu>
|
</Menu>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{type === "article" && (
|
||||||
<Box className="w-[80%] border border-gray-300 rounded-2xl p-3">
|
<Box className="w-[80%] border border-gray-300 rounded-2xl p-3">
|
||||||
{activeArticleIndex !== null && (
|
{activeArticleIndex !== null && (
|
||||||
<>
|
<>
|
||||||
<Box className="flex justify-end gap-2 mb-3">
|
<Box className="flex justify-end gap-2 mb-3">
|
||||||
<Button variant="contained" color="primary">
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
unlinkRightArticle(
|
||||||
|
sight[language].right[activeArticleIndex].id
|
||||||
|
);
|
||||||
|
setActiveArticleIndex(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
Открепить
|
Открепить
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" color="success">
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="success"
|
||||||
|
onClick={() => {
|
||||||
|
deleteRightArticle(
|
||||||
|
sight[language].right[activeArticleIndex].id
|
||||||
|
);
|
||||||
|
setActiveArticleIndex(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
Удалить
|
Удалить
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
@ -166,7 +230,8 @@ export const RightWidgetTab = observer(
|
|||||||
<TextField
|
<TextField
|
||||||
label="Название информации"
|
label="Название информации"
|
||||||
value={
|
value={
|
||||||
sight[language].right[activeArticleIndex].heading
|
sight[language].right[activeArticleIndex]
|
||||||
|
.heading
|
||||||
}
|
}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
updateRightArticleInfo(
|
updateRightArticleInfo(
|
||||||
@ -194,19 +259,65 @@ export const RightWidgetTab = observer(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{/* <MediaArea
|
|
||||||
articleId={1}
|
<MediaArea
|
||||||
mediaIds={[]}
|
articleId={
|
||||||
deleteMedia={() => {}}
|
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>
|
</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>
|
</Box>
|
||||||
|
|
||||||
|
{type === "article" && (
|
||||||
<Box className="w-[25%] mr-10">
|
<Box className="w-[25%] mr-10">
|
||||||
{activeArticleIndex !== null && (
|
{activeArticleIndex !== null && (
|
||||||
<Paper
|
<Paper
|
||||||
@ -224,6 +335,14 @@ export const RightWidgetTab = observer(
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{sight[language].right[activeArticleIndex].media.length >
|
||||||
|
0 ? (
|
||||||
|
<MediaViewer
|
||||||
|
media={
|
||||||
|
sight[language].right[activeArticleIndex].media[0]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -237,6 +356,7 @@ export const RightWidgetTab = observer(
|
|||||||
>
|
>
|
||||||
<ImagePlus size={48} color="white" />
|
<ImagePlus size={48} color="white" />
|
||||||
</Box>
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -268,7 +388,9 @@ export const RightWidgetTab = observer(
|
|||||||
>
|
>
|
||||||
{sight[language].right[activeArticleIndex].body ? (
|
{sight[language].right[activeArticleIndex].body ? (
|
||||||
<ReactMarkdownComponent
|
<ReactMarkdownComponent
|
||||||
value={sight[language].right[activeArticleIndex].body}
|
value={
|
||||||
|
sight[language].right[activeArticleIndex].body
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Typography
|
<Typography
|
||||||
@ -283,6 +405,7 @@ export const RightWidgetTab = observer(
|
|||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
@ -303,11 +426,29 @@ export const RightWidgetTab = observer(
|
|||||||
</Box>
|
</Box>
|
||||||
</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
|
<SelectArticleModal
|
||||||
open={isSelectModalOpen}
|
open={isSelectModalOpen}
|
||||||
onClose={handleCloseSelectModal}
|
onClose={handleCloseSelectModal}
|
||||||
onSelectArticle={handleArticleSelect}
|
onSelectArticle={handleArticleSelect}
|
||||||
/>
|
/>
|
||||||
|
<SelectMediaDialog
|
||||||
|
open={isSelectMediaModalOpen}
|
||||||
|
onClose={() => setIsSelectMediaModalOpen(false)}
|
||||||
|
onSelectMedia={handleMediaSelected}
|
||||||
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,3 +10,4 @@ export * from "./SightsTable";
|
|||||||
export * from "./MediaViewer";
|
export * from "./MediaViewer";
|
||||||
export * from "./MediaArea";
|
export * from "./MediaArea";
|
||||||
export * from "./ModelViewer3D";
|
export * from "./ModelViewer3D";
|
||||||
|
export * from "./MediaAreaForSight";
|
||||||
|
Loading…
Reference in New Issue
Block a user