Files
website/client/src/routes/public/Portal.jsx
Claude 0318d6fe9f Make hero the full landing page; move quick links into hero editor
Remove the two-card destination row and the below-hero quick-links nav
from the portal so the hero fills the viewport with nothing rendered
after it. The 5 quick links (News, Screenshots, Five on Friday,
Monthly Newsletter, About) move into the hero editor as a third
buttons element in defaultLayout(), reusing the existing buttons
element type so they stay fully editable with no schema changes.

Also drop overflow:hidden on the hero section: on mobile, 100vh can
compute smaller than window.innerHeight, and with overflow hidden the
wrapped quick-links text was getting clipped at the bottom edge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 21:49:45 -05:00

71 lines
2.7 KiB
JavaScript

import { useEffect, useMemo, useState } from 'react'
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'
// 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 (
<PublicLayout header={false}>
<main style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
{PREVIEW && draft && (
<div
className="sans"
style={{ background: 'var(--accent)', color: 'var(--bg-deep)', textAlign: 'center', padding: '6px 12px', fontSize: '0.8rem', fontWeight: 700, letterSpacing: '0.04em' }}
>
Preview showing unpublished draft
</div>
)}
<section
style={{
position: 'relative',
flex: 1,
minHeight: '100vh',
...bgStyle,
}}
>
<div style={{ position: 'absolute', inset: 0, padding: '0 max(18px,calc((100% - 1080px)/2))' }}>
{elements.map((el) => (
<HeroElement key={el.id} element={el} />
))}
</div>
</section>
</main>
</PublicLayout>
)
}