import { articlesStore, Language, authInstance, languageInstance, mediaStore, selectedCityStore, } from "@shared"; import { makeAutoObservable, runInAction } from "mobx"; type MediaItem = { id: string; filename: string; media_name?: string; media_type: number; }; type SightLanguageInfo = { name: string; short_name: string; address: string; left: { heading: string; body: string; media: MediaItem[]; }; right: { id: number; heading: string; body: string; media: MediaItem[] }[]; }; type SightCommonInfo = { city_id: number; city: string; latitude: number; longitude: number; is_default_icon: boolean; thumbnail: string | null; icon: string | null; alt_icon: string | null; watermark_lu: string | null; watermark_rd: string | null; left_article: number; preview_media: string | null; video_preview: string | null; preview_font_size?: number; }; type SightBaseInfo = SightCommonInfo & { [key in Language]: SightLanguageInfo; }; const initialSightState: SightBaseInfo = { city_id: 0, city: "", latitude: 0, longitude: 0, is_default_icon: false, thumbnail: null, icon: null, alt_icon: null, watermark_lu: null, watermark_rd: null, left_article: 0, preview_media: null, video_preview: null, ru: { name: "", short_name: "", address: "", left: { heading: "", body: "", media: [] }, right: [], }, en: { name: "", short_name: "", address: "", left: { heading: "", body: "", media: [] }, right: [], }, zh: { name: "", short_name: "", address: "", left: { heading: "", body: "", media: [] }, right: [], }, }; class CreateSightStore { sight: SightBaseInfo = JSON.parse(JSON.stringify(initialSightState)); uploadMediaOpen = false; setUploadMediaOpen = (open: boolean) => { this.uploadMediaOpen = open; }; fileToUpload: File | null = null; setFileToUpload = (file: File | null) => { this.fileToUpload = file; }; constructor() { makeAutoObservable(this); } createNewRightArticle = async () => { const articleRuData = { heading: "Новый заголовок (RU)", body: "Новый текст (RU)", }; const articleEnData = { heading: "New Heading (EN)", body: "New Text (EN)", }; const articleZhData = { heading: "Новый заголовок (ZH)", body: "Новый текст (ZH)", }; try { this.needLeaveAgree = true; const articleRes = await authInstance.post("/article", { translations: { heading: { ru: articleRuData.heading, en: articleEnData.heading, zh: articleZhData.heading, }, body: { ru: articleRuData.body, en: articleEnData.body, zh: articleZhData.body, }, }, ...(selectedCityStore.selectedCityId ? { city_id: selectedCityStore.selectedCityId } : {}), }); const { id } = articleRes.data; runInAction(() => { const newArticleEntry = { id, media: [] }; this.sight.ru.right.push({ ...newArticleEntry, ...articleRuData }); this.sight.en.right.push({ ...newArticleEntry, ...articleEnData }); this.sight.zh.right.push({ ...newArticleEntry, ...articleZhData }); }); return id; } catch (error) { console.error("Error creating new right article:", error); throw error; } }; linkExistingRightArticle = async (articleId: number) => { try { const ruData = await languageInstance("ru").get(`/article/${articleId}`); const enData = await languageInstance("en").get(`/article/${articleId}`); const zhData = await languageInstance("zh").get(`/article/${articleId}`); const mediaRes = await authInstance.get(`/article/${articleId}/media`); const mediaData: MediaItem[] = mediaRes.data || []; runInAction(() => { this.sight.ru.right.push({ id: articleId, heading: ruData.data.heading, body: ruData.data.body, media: mediaData, }); this.sight.en.right.push({ id: articleId, heading: enData.data.heading, body: enData.data.body, media: mediaData, }); this.sight.zh.right.push({ id: articleId, heading: zhData.data.heading, body: zhData.data.body, media: mediaData, }); }); return articleId; } catch (error) { console.error("Error linking existing right article:", error); throw error; } }; updateRightArticleInfo = ( index: number, language: Language, heading: string, body: string, ) => { if (this.sight[language].right[index]) { this.sight[language].right[index].heading = heading; this.sight[language].right[index].body = body; } }; unlinkRightAritcle = (articleId: number) => { runInAction(() => { this.sight.ru.right = this.sight.ru.right.filter( (article) => article.id !== articleId, ); this.sight.en.right = this.sight.en.right.filter( (article) => article.id !== articleId, ); this.sight.zh.right = this.sight.zh.right.filter( (article) => article.id !== articleId, ); }); }; deleteRightArticle = async (articleId: number) => { try { await authInstance.delete(`/article/${articleId}`); runInAction(() => { this.sight.ru.right = this.sight.ru.right.filter( (article) => article.id !== articleId, ); this.sight.en.right = this.sight.en.right.filter( (article) => article.id !== articleId, ); this.sight.zh.right = this.sight.zh.right.filter( (article) => article.id !== articleId, ); }); } catch (error) { console.error("Error deleting right article:", error); throw error; } }; createLinkWithRightArticle = async (media: MediaItem, articleId: number) => { try { await authInstance.post(`/article/${articleId}/media`, { media_id: media.id, media_order: 1, }); runInAction(() => { (["ru", "en", "zh"] as Language[]).forEach((lang) => { const article = this.sight[lang].right.find( (a) => a.id === articleId, ); if (article) { if (!article.media) article.media = []; article.media.unshift(media); } }); }); } catch (error) { console.error("Error linking media to right article:", error); throw error; } }; deleteRightArticleMedia = async (articleId: number, mediaId: string) => { try { await authInstance.delete(`/article/${articleId}/media`, { data: { media_id: mediaId }, }); runInAction(() => { (["ru", "en", "zh"] as Language[]).forEach((lang) => { const article = this.sight[lang].right.find( (a) => a.id === articleId, ); if (article && article.media) { article.media = article.media.filter((m) => m.id !== mediaId); } }); }); } catch (error) { console.error("Error deleting media from right article:", error); throw error; } }; updateLeftInfo = (language: Language, heading: string, body: string) => { this.sight[language].left.heading = heading; this.sight[language].left.body = body; }; unlinkLeftArticle = () => { /* ... your existing logic ... */ this.sight.left_article = 0; this.sight.ru.left = { heading: "", body: "", media: [] }; this.sight.en.left = { heading: "", body: "", media: [] }; this.sight.zh.left = { heading: "", body: "", media: [] }; }; updateLeftArticle = async (articleId: number) => { /* ... your existing logic ... */ this.sight.left_article = articleId; if (articleId) { const [ruArticleData, enArticleData, zhArticleData, mediaData] = await Promise.all([ languageInstance("ru").get(`/article/${articleId}`), languageInstance("en").get(`/article/${articleId}`), languageInstance("zh").get(`/article/${articleId}`), authInstance.get(`/article/${articleId}/media`), ]); runInAction(() => { this.sight.ru.left = { heading: ruArticleData.data.heading, body: ruArticleData.data.body, media: mediaData.data || [], }; this.sight.en.left = { heading: enArticleData.data.heading, body: enArticleData.data.body, media: mediaData.data || [], }; this.sight.zh.left = { heading: zhArticleData.data.heading, body: zhArticleData.data.body, media: mediaData.data || [], }; }); } else { this.unlinkLeftArticle(); } }; deleteLeftArticle = async (articleId: number) => { /* ... your existing logic ... */ await authInstance.delete(`/article/${articleId}`); runInAction(() => { articlesStore.articles.ru = articlesStore.articles.ru.filter( (article) => article.id !== articleId, ); articlesStore.articles.en = articlesStore.articles.en.filter( (article) => article.id !== articleId, ); articlesStore.articles.zh = articlesStore.articles.zh.filter( (article) => article.id !== articleId, ); }); this.unlinkLeftArticle(); }; createLeftArticle = async () => { /* ... your existing logic to create a new left article (placeholder or DB) ... */ const ruName = (this.sight.ru.name || "").trim(); const enName = (this.sight.en.name || "").trim(); const zhName = (this.sight.zh.name || "").trim(); const hasAnyName = !!(ruName || enName || zhName); const response = await languageInstance("ru").post("/article", { heading: hasAnyName ? ruName : "", body: "", ...(selectedCityStore.selectedCityId ? { city_id: selectedCityStore.selectedCityId } : {}), }); const newLeftArticleId = response.data.id; await languageInstance("en").patch(`/article/${newLeftArticleId}`, { heading: hasAnyName ? enName : "", body: "", }); await languageInstance("zh").patch(`/article/${newLeftArticleId}`, { heading: hasAnyName ? zhName : "", body: "", }); runInAction(() => { this.sight.left_article = newLeftArticleId; this.sight.ru.left = { heading: hasAnyName ? ruName : "", body: "", media: [], }; this.sight.en.left = { heading: hasAnyName ? enName : "", body: "", media: [], }; this.sight.zh.left = { heading: hasAnyName ? zhName : "", body: "", media: [], }; articlesStore.articles.ru.push({ id: newLeftArticleId, heading: hasAnyName ? ruName : "", body: "", service_name: hasAnyName ? ruName : "", }); articlesStore.articles.en.push({ id: newLeftArticleId, heading: hasAnyName ? enName : "", body: "", service_name: hasAnyName ? enName : "", }); articlesStore.articles.zh.push({ id: newLeftArticleId, heading: hasAnyName ? zhName : "", body: "", service_name: hasAnyName ? zhName : "", }); }); return newLeftArticleId; }; setNewLeftArticlePlaceholder = () => { this.sight.left_article = 10000000; this.sight.ru.left = { heading: "Новая левая статья", body: "Заполните контентом", media: [], }; this.sight.en.left = { heading: "New Left Article", body: "Fill with content", media: [], }; this.sight.zh.left = { heading: "新的左侧文章", body: "填写内容", media: [], }; }; linkPreviewMedia = (mediaId: string) => { this.sight.preview_media = mediaId; }; unlinkPreviewMedia = () => { this.sight.preview_media = null; }; clearCreateSight = () => { this.needLeaveAgree = false; this.sight = JSON.parse(JSON.stringify(initialSightState)); }; updateSightInfo = ( content: Partial, language?: Language, ) => { this.needLeaveAgree = true; if (language) { this.sight[language] = { ...this.sight[language], ...content }; } else { this.sight = { ...this.sight, ...(content as Partial) }; } }; createSight = async (primaryLanguage: Language) => { let finalLeftArticleId = this.sight.left_article; if (this.sight.left_article === 10000000) { const res = await languageInstance("ru").post("/article", { heading: this.sight.ru.left.heading, body: this.sight.ru.left.body, ...(selectedCityStore.selectedCityId ? { city_id: selectedCityStore.selectedCityId } : {}), }); finalLeftArticleId = res.data.id; await languageInstance("en").patch(`/article/${finalLeftArticleId}`, { heading: this.sight.en.left.heading, body: this.sight.en.left.body, }); await languageInstance("zh").patch(`/article/${finalLeftArticleId}`, { heading: this.sight.zh.left.heading, body: this.sight.zh.left.body, }); } else if ( this.sight.left_article !== 0 && this.sight.left_article !== null ) { await languageInstance("ru").patch( `/article/${this.sight.left_article}`, { heading: this.sight.ru.left.heading, body: this.sight.ru.left.body }, ); await languageInstance("en").patch( `/article/${this.sight.left_article}`, { heading: this.sight.en.left.heading, body: this.sight.en.left.body }, ); await languageInstance("zh").patch( `/article/${this.sight.left_article}`, { heading: this.sight.zh.left.heading, body: this.sight.zh.left.body }, ); } for (const lang of ["ru", "en", "zh"] as Language[]) { for (const article of this.sight[lang].right) { if (article.id == 0 || article.id == null) { continue; } await languageInstance(lang).patch(`/article/${article.id}`, { heading: article.heading, body: article.body, }); } } const rightArticleIdsForLink = this.sight[primaryLanguage].right.map( (a) => a.id, ); const sightPayload = { city_id: this.sight.city_id, city: this.sight.city, latitude: this.sight.latitude, longitude: this.sight.longitude, is_default_icon: this.sight.is_default_icon, name: (this.sight[primaryLanguage].name || "").trim(), short_name: (this.sight[primaryLanguage].short_name || "").trim(), address: this.sight[primaryLanguage].address, thumbnail: this.sight.thumbnail, icon: this.sight.icon, alt_icon: this.sight.alt_icon, watermark_lu: this.sight.watermark_lu, watermark_rd: this.sight.watermark_rd, left_article: finalLeftArticleId === 0 ? null : finalLeftArticleId, preview_media: this.sight.preview_media, video_preview: this.sight.video_preview, preview_font_size: this.sight.preview_font_size, }; const response = await languageInstance(primaryLanguage).post( "/sight", sightPayload, ); const newSightId = response.data.id; const otherLanguages = (["ru", "en", "zh"] as Language[]).filter( (l) => l !== primaryLanguage, ); for (const lang of otherLanguages) { await languageInstance(lang).patch(`/sight/${newSightId}`, { city_id: this.sight.city_id, city: this.sight.city, latitude: this.sight.latitude, longitude: this.sight.longitude, is_default_icon: this.sight.is_default_icon, name: (this.sight[lang].name || "").trim(), short_name: (this.sight[lang].short_name || "").trim(), address: this.sight[lang].address, thumbnail: this.sight.thumbnail, icon: this.sight.icon, alt_icon: this.sight.alt_icon, watermark_lu: this.sight.watermark_lu, watermark_rd: this.sight.watermark_rd, left_article: finalLeftArticleId === 0 ? null : finalLeftArticleId, preview_media: this.sight.preview_media, video_preview: this.sight.video_preview, }); } for (let i = 0; i < rightArticleIdsForLink.length; i++) { await authInstance.post(`/sight/${newSightId}/article`, { article_id: rightArticleIdsForLink[i], page_num: i + 1, }); } runInAction(() => { this.needLeaveAgree = false; }); return newSightId; }; uploadMedia = async ( filename: string, type: number, file: File, media_name?: string, ): Promise => { const formData = new FormData(); formData.append("file", file); formData.append("filename", filename); if (media_name) formData.append("media_name", media_name); formData.append("type", type.toString()); if (selectedCityStore.selectedCityId) { formData.append("city_id", selectedCityStore.selectedCityId.toString()); } try { const response = await authInstance.post(`/media`, formData); runInAction(() => { this.fileToUpload = null; this.uploadMediaOpen = false; }); mediaStore.getMedia(); return { id: response.data.id, filename: filename, media_name: media_name, media_type: type, }; } catch (error) { console.error("Error uploading media:", error); throw error; } }; createLinkWithLeftArticle = async (media: MediaItem) => { if (!this.sight.left_article || this.sight.left_article === 10000000) { console.warn( "Left article not selected or is a placeholder. Cannot link media yet.", ); return; } try { await authInstance.post(`/article/${this.sight.left_article}/media`, { media_id: media.id, media_order: (this.sight.ru.left.media?.length || 0) + 1, }); runInAction(() => { (["ru", "en", "zh"] as Language[]).forEach((lang) => { if (!this.sight[lang].left.media) this.sight[lang].left.media = []; this.sight[lang].left.media.unshift(media); }); }); } catch (error) { console.error("Error linking media to left article:", error); throw error; } }; deleteLeftArticleMedia = async (mediaId: string) => { if (!this.sight.left_article || this.sight.left_article === 10000000) return; try { await authInstance.delete(`/article/${this.sight.left_article}/media`, { data: { media_id: mediaId }, }); runInAction(() => { (["ru", "en", "zh"] as Language[]).forEach((lang) => { if (this.sight[lang].left.media) { this.sight[lang].left.media = this.sight[lang].left.media.filter( (m) => m.id !== mediaId, ); } }); }); } catch (error) { console.error("Error deleting media from left article:", error); throw error; } }; updateRightArticles = async (articles: any[]) => { const articlesIds = articles.map((article) => article.id); const sortArticles = (existing: any[]) => { const articleMap = new Map( existing.map((article) => [article.id, article]), ); return articlesIds .map((id) => articleMap.get(id)) .filter( (article): article is (typeof existing)[number] => article !== undefined, ); }; this.sight.ru.right = sortArticles(this.sight.ru.right); this.sight.en.right = sortArticles(this.sight.en.right); this.sight.zh.right = sortArticles(this.sight.zh.right); this.needLeaveAgree = true; }; needLeaveAgree = false; setNeedLeaveAgree = (need: boolean) => { this.needLeaveAgree = need; }; } export const createSightStore = new CreateSightStore();