import { useEffect, useMemo, useState } from 'react' import { Link } from 'react-router-dom' import PublicLayout from '../../components/PublicLayout.jsx' import HeroElement from '../../components/HeroElement.jsx' import { useSite } from '../../contexts/SiteContext.jsx' import { api } from '../../api/client.js' import { defaultLayout, parseLayout, heroBackground } from '../../lib/heroLayout.js' const QUICK = [ { label: 'News', to: '/site/news' }, { label: 'Screenshots', to: '/site/screenshots' }, { label: 'Five on Friday', to: '/site/five-on-friday' }, { label: 'Monthly Newsletter', to: '/site/newsletter' }, { label: 'About', to: '/site/about' }, ] const DESTINATIONS = [ { kicker: 'Public portal', title: 'Mysticmoon Website', body: 'Updates, screenshots, newsletters, and weekly community posts from the shard.', to: '/site', }, { kicker: 'Knowledge base', title: 'Mysticmoon Wiki', body: 'Guides, maps, systems, items, monsters, crafting, lore, and rules.', to: '/wiki', }, ] // Admin "Preview" opens the portal with ?preview=1 to render the unpublished draft. const PREVIEW = typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('preview') === '1' export default function Portal() { const { settings } = useSite() const teaser = settings.homepage_teaser || 'Mysticmoon is still being shaped beneath a midnight sky — a quiet preview for the news, screenshots, guides, and community notes to come as the world wakes.' // Published layout (public). Falls back to the pre-populated default if missing, // malformed, the wrong version, or empty — so the hero is never blank. const published = useMemo(() => parseLayout(settings.hero_layout), [settings.hero_layout]) // In preview mode, pull the draft via the admin endpoint (requires a logged-in // admin cookie); falls back silently to the published/default layout otherwise. const [draft, setDraft] = useState(null) useEffect(() => { if (!PREVIEW) return let active = true api.admin .getSettings() .then((s) => active && setDraft(parseLayout(s.hero_layout_draft))) .catch(() => {}) return () => { active = false } }, []) const active = (PREVIEW && draft) || (published && published.elements.length ? published : null) const layout = active || defaultLayout(teaser) const isDefault = !active const bgStyle = heroBackground(layout, { isDefault }) const elements = [...layout.elements].sort((a, b) => (a.z || 0) - (b.z || 0)) return (
{PREVIEW && draft && (
Preview — showing unpublished draft
)}
{elements.map((el) => ( ))}
) }