import { FC, useEffect, useState } from 'react'; import { FaLanguage, FaSave, FaSpinner, FaTimes, FaTrash } from 'react-icons/fa'; import { CatalogType, LocalizeText } from '../../../../api'; import { useCatalogData, useCatalogUiState, useTranslationActions, useTranslationState } from '../../../../hooks'; import { IPageEditData, useCatalogAdmin } from '../../CatalogAdminContext'; const LAYOUT_OPTIONS = [ 'default_3x3', 'frontpage4', 'pets', 'pets2', 'pets3', 'spaces_new', 'soundmachine', 'trophies', 'roomads', 'guild_frontpage', 'guild_forum', 'guild_custom_furni', 'vip_buy', 'builders_club_frontpage', 'builders_club_addons', 'builders_club_loyalty', 'marketplace', 'marketplace_own_items', 'recycler', 'recycler_info', 'recycler_prizes', 'info_loyalty', 'badge_display', 'bots', 'single_bundle', 'color_grouping', 'recent_purchases', 'custom_prefix' ]; const MODE_OPTIONS = [ { value: 'NORMAL', label: 'Normal' }, { value: 'BUILDER', label: 'Builder' }, { value: 'BOTH', label: 'Both' } ]; export const CatalogAdminPageEditView: FC<{}> = () => { const { currentPage = null, rootNode = null } = useCatalogData(); const { activeNodes = [], currentType = CatalogType.NORMAL } = useCatalogUiState(); const catalogAdmin = useCatalogAdmin(); const editingPageData = catalogAdmin?.editingPageData ?? false; const editingRootPage = catalogAdmin?.editingRootPage ?? false; const editingPageNode = catalogAdmin?.editingPageNode ?? null; const editingPageDetails = catalogAdmin?.editingPageDetails ?? null; const requestPageDetails = catalogAdmin?.requestPageDetails; const loading = catalogAdmin?.loading ?? false; const [ caption, setCaption ] = useState(''); const [ captionSave, setCaptionSave ] = useState(''); const [ catalogMode, setCatalogMode ] = useState('NORMAL'); const [ pageLayout, setPageLayout ] = useState('default_3x3'); const [ iconImage, setIconImage ] = useState(0); const [ minRank, setMinRank ] = useState(1); const [ visible, setVisible ] = useState('1'); const [ enabled, setEnabled ] = useState('1'); const [ orderNum, setOrderNum ] = useState(0); const [ parentId, setParentId ] = useState(-1); const [ pageText1, setPageText1 ] = useState(''); const [ showTranslate, setShowTranslate ] = useState(false); const [ translateTargetLanguage, setTranslateTargetLanguage ] = useState('en'); const [ isTranslating, setIsTranslating ] = useState(false); const [ translateError, setTranslateError ] = useState(null); const { supportedLanguages = [], languagesLoading = false } = useTranslationState(); const { translateText, ensureSupportedLanguagesLoaded } = useTranslationActions(); const targetNode = editingPageNode ? editingPageNode : editingRootPage ? rootNode : (activeNodes.length > 0 ? activeNodes[activeNodes.length - 1] : null); const targetPageId = targetNode?.pageId ?? currentPage?.pageId; const isRoot = editingRootPage; const closeForm = () => { catalogAdmin?.setEditingPageData(false); catalogAdmin?.setEditingRootPage(false); catalogAdmin?.setEditingPageNode(null); }; useEffect(() => { if(!editingPageData || !targetNode) return; // Don't read the decorated caption out of the catalog index - // the gameserver appends " (id)" when ACC_CATALOG_IDS is on and // we don't want that round-tripping back into the DB. Wait for // the admin page-details event to land instead; it carries the // raw caption / caption_save / min_rank / order_num / enabled. setCaption(''); setCaptionSave(''); setMinRank(1); setOrderNum(0); setEnabled('1'); setCatalogMode(currentType === CatalogType.BUILDER ? 'BUILDER' : 'NORMAL'); setPageLayout(currentPage?.layoutCode || 'default_3x3'); setIconImage(targetNode.iconId ?? 0); setVisible(targetNode.isVisible ? '1' : '0'); const matchesLoadedPage = currentPage && targetPageId === currentPage.pageId; const existingText1 = matchesLoadedPage && currentPage.localization ? currentPage.localization.getText(0) : ''; setPageText1(existingText1 || ''); setShowTranslate(false); setIsTranslating(false); setTranslateError(null); const wireParentId = targetNode.parentId; setParentId(typeof wireParentId === 'number' && wireParentId !== -1 ? wireParentId : (targetNode.parent ? targetNode.parent.pageId : -1)); if(targetPageId != null && targetPageId >= 0) requestPageDetails?.(targetPageId); }, [ editingPageData, targetNode, currentPage, currentType, targetPageId, requestPageDetails ]); useEffect(() => { if(!editingPageDetails) return; if(targetPageId != null && editingPageDetails.pageId !== targetPageId) return; setCaption(editingPageDetails.caption); setCaptionSave(editingPageDetails.captionSave); setMinRank(editingPageDetails.minRank); setOrderNum(editingPageDetails.orderNum); setVisible(editingPageDetails.visible ? '1' : '0'); setEnabled(editingPageDetails.enabled ? '1' : '0'); }, [ editingPageDetails, targetPageId ]); if(!editingPageData || !targetNode) return null; const inputClass = 'text-[11px] border-2 border-card-grid-item-border rounded px-2 py-1 bg-white focus:outline-none focus:border-primary transition-colors'; const handleSave = async () => { if(!catalogAdmin?.savePage) return; const data: IPageEditData = { pageId: targetPageId, caption, captionSave, catalogMode, pageLayout, iconImage, minRank, visible, enabled, orderNum, parentId, pageText1, }; catalogAdmin.savePage(data); closeForm(); }; const openTranslate = () => { const next = !showTranslate; setShowTranslate(next); setTranslateError(null); if(next) ensureSupportedLanguagesLoaded(); }; const runTranslate = async () => { if(!pageText1.trim().length) { setTranslateError('Nothing to translate yet.'); return; } if(!translateTargetLanguage) { setTranslateError('Pick a language first.'); return; } setIsTranslating(true); setTranslateError(null); try { const result = await translateText(pageText1, translateTargetLanguage); setPageText1(result?.translatedText || pageText1); setShowTranslate(false); } catch(error) { setTranslateError((error as Error)?.message || 'Translation failed.'); } finally { setIsTranslating(false); } }; const handleDelete = async () => { if(!catalogAdmin?.deletePage || isRoot) return; if(!confirm(LocalizeText('catalog.admin.delete.page.confirm', [ 'name' ], [ editingPageDetails?.caption ?? '' ]))) return; catalogAdmin.deletePage(targetPageId); closeForm(); }; return (
{ isRoot ? LocalizeText('catalog.admin.edit.root') : `${ LocalizeText('catalog.admin.edit') } ${ editingPageDetails?.caption ?? '' }` }
setCaption(e.target.value) } />
setMinRank(parseInt(e.target.value) || 1) } />
setCaptionSave(e.target.value) } />
setIconImage(parseInt(e.target.value) || 0) } />
setOrderNum(parseInt(e.target.value) || 0) } />
setParentId(parseInt(e.target.value) || -1) } />
{ showTranslate &&
} { translateError && { translateError } }