import { AddLinkEventTracker, ILinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer'; import { FC, useEffect, useState } from 'react'; import { FaCog, FaEdit, FaEye, FaEyeSlash, FaHeart, FaPlus, FaStar, FaTrash } from 'react-icons/fa'; import { CatalogType, LocalizeText } from '../../api'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../common'; import { useCatalogActions, useCatalogData, useCatalogFavorites, useCatalogUiState, useHasPermission } from '../../hooks'; import { CatalogAdminProvider, useCatalogAdmin } from './CatalogAdminContext'; import { CatalogAdminOfferEditView } from './views/admin/CatalogAdminOfferEditView'; import { CatalogAdminPageEditView } from './views/admin/CatalogAdminPageEditView'; import { CatalogBuildersClubStatusView } from './views/catalog-header/CatalogBuildersClubStatusView'; import { CatalogIconView } from './views/catalog-icon/CatalogIconView'; import { CatalogFavoritesView } from './views/favorites/CatalogFavoritesView'; import { CatalogGiftView } from './views/gift/CatalogGiftView'; import { CatalogNavigationView } from './views/navigation/CatalogNavigationView'; import { CatalogSearchView } from './views/page/common/CatalogSearchView'; import { GetCatalogLayout } from './views/page/layout/GetCatalogLayout'; import { MarketplacePostOfferView } from './views/page/layout/marketplace/MarketplacePostOfferView'; const CatalogModernViewInner: FC<{}> = () => { const { rootNode = null, currentPage = null, searchResult = null } = useCatalogData(); const { isVisible = false, setIsVisible = null, navigationHidden = false, setNavigationHidden = null, activeNodes = [], setSearchResult = null, currentType = CatalogType.NORMAL } = useCatalogUiState(); const { openPageByName = null, openPageByOfferId = null, activateNode = null, openCatalogByType = null, toggleCatalogByType = null } = useCatalogActions(); const catalogAdmin = useCatalogAdmin(); const adminMode = catalogAdmin?.adminMode ?? false; const setAdminMode = catalogAdmin?.setAdminMode ?? (() => {}); const hasPendingChanges = catalogAdmin?.hasPendingChanges ?? false; const publishCatalog = catalogAdmin?.publishCatalog ?? (() => {}); const loading = catalogAdmin?.loading ?? false; const { favoriteOfferIds, favoritePageIds } = useCatalogFavorites(); const [ showFavorites, setShowFavorites ] = useState(false); const isMod = useHasPermission('acc_catalogfurni'); const totalFavs = favoriteOfferIds.length + favoritePageIds.length; const buildersClubHeaderStyle = (currentType === CatalogType.BUILDER) ? { borderColor: '#d79d2e', borderBottomColor: '#000', background: 'linear-gradient(180deg, #d89f2d 0%, #c68515 100%)' } : undefined; // Desktop = fixed 780x520. On mobile the window clamps below the viewport so // it reads as a dialog (with margins) instead of filling the whole phone // screen — applies to both the normal catalog and the Builders Club. const catalogCardSize = 'w-[780px] h-[520px] max-w-[96vw] max-h-[72vh] sm:max-w-[100vw] sm:max-h-[92vh]'; useEffect(() => { const getCatalogTypeFromLink = (type?: string) => { switch((type || '').toLowerCase()) { case 'bc': case 'builder': case 'buildersclub': case 'builders_club': return CatalogType.BUILDER; default: return CatalogType.NORMAL; } }; const linkTracker: ILinkEventTracker = { linkReceived: (url: string) => { const parts = url.split('/'); if(parts.length < 2) return; switch(parts[1]) { case 'show': if(parts.length > 2) { openCatalogByType(getCatalogTypeFromLink(parts[2])); return; } setIsVisible(true); return; case 'hide': setIsVisible(false); return; case 'toggle': if(parts.length > 2) { toggleCatalogByType(getCatalogTypeFromLink(parts[2])); return; } setIsVisible(prevValue => !prevValue); return; case 'open': if(parts.length > 2) { if(parts.length === 4) { switch(parts[2]) { case 'offerId': openPageByOfferId(parseInt(parts[3])); return; } } else { openPageByName(parts[2]); } } else { setIsVisible(true); } return; } }, eventUrlPrefix: 'catalog/' }; AddLinkEventTracker(linkTracker); return () => RemoveLinkEventTracker(linkTracker); }, [ setIsVisible, openPageByOfferId, openPageByName, openCatalogByType, toggleCatalogByType ]); return ( <> { isVisible && setIsVisible(false) } style={ buildersClubHeaderStyle } /> { /* Admin banner */ } { adminMode &&
⚙ Admin Mode
}
{ /* === LEFT SIDEBAR === */ }
{ /* Favorites toggle */ }
setShowFavorites(!showFavorites) } >
0 ? 'text-danger' : 'text-muted' }` } /> { totalFavs > 0 && { totalFavs } }
{ LocalizeText('catalog.favorites') }
{ /* Admin: root page actions */ } { adminMode && rootNode &&
} { /* Category icons */ } { rootNode && rootNode.children.length > 0 && rootNode.children.map((child, index) => { if(!adminMode && !child.isVisible) return null; const isHidden = !child.isVisible; return (
{ if(searchResult) setSearchResult(null); if(showFavorites) setShowFavorites(false); activateNode(child); } } >
{ isHidden && }
{ child.localization } { /* Admin actions on each root category */ } { adminMode &&
{ e.stopPropagation(); catalogAdmin.setEditingPageNode(child); catalogAdmin.setEditingRootPage(false); catalogAdmin.setEditingPageData(true); } } >
{ e.stopPropagation(); catalogAdmin.togglePageVisible(child.pageId); } } > { isHidden ? : }
{ e.stopPropagation(); if(confirm(LocalizeText('catalog.admin.delete.category.confirm', [ 'name' ], [ child.localization ]))) { catalogAdmin.deletePage(child.pageId); } } } >
}
); }) }
{ /* === MAIN AREA === */ }
{ /* Toolbar: search + admin */ }
{ /* Breadcrumb */ }
{ activeNodes && activeNodes.length > 0 ? activeNodes.map((node, i) => ( { i > 0 && } activateNode(node) : undefined }> { node.localization } )) : { LocalizeText('catalog.title') } }
{ isMod && }
{ /* Content area */ }
{ showFavorites ?
setShowFavorites(false) } />
: <> { !navigationHidden && activeNodes && activeNodes.length > 0 &&
}
{ adminMode && } { GetCatalogLayout(currentPage, () => setNavigationHidden(true)) }
}
} ); }; export const CatalogModernView: FC<{}> = () => { return ( ); };