87 lines
5.4 KiB
JavaScript
87 lines
5.4 KiB
JavaScript
import React from 'react'
|
|
import WorldStatusBadge from './WorldStatusBadge'
|
|
import { trackWorldSourceClick, withWorldSource } from '../../lib/worldAnalytics'
|
|
|
|
function themeStyle(theme) {
|
|
return {
|
|
'--world-accent': theme?.accent_color || '#38bdf8',
|
|
'--world-accent-secondary': theme?.accent_color_secondary || '#0f172a',
|
|
}
|
|
}
|
|
|
|
export default function WorldFamilyCard({ family, sourceSurface = '', sourceDetail = '' }) {
|
|
if (!family) {
|
|
return null
|
|
}
|
|
|
|
const currentWorld = family.current_world || null
|
|
const editionCount = Number(family.edition_count || 0)
|
|
const archiveCount = Number(family.archive_count || 0)
|
|
const familyHref = sourceSurface ? withWorldSource(family.public_url, sourceSurface, sourceDetail || 'recurring_family') : family.public_url
|
|
|
|
return (
|
|
<article
|
|
className="group relative overflow-hidden rounded-[28px] border border-white/10 bg-slate-950/70 p-6"
|
|
style={themeStyle(family.theme)}
|
|
>
|
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top_right,_color-mix(in_srgb,var(--world-accent)_26%,transparent),_transparent_42%),linear-gradient(135deg,_color-mix(in_srgb,var(--world-accent-secondary)_94%,black),_rgba(2,6,23,0.94))] opacity-95" />
|
|
{family.cover_url ? <img src={family.cover_url} alt={family.title} className="absolute inset-0 h-full w-full object-cover opacity-15" /> : null}
|
|
<div className="absolute inset-0 bg-gradient-to-t from-slate-950 via-slate-950/88 to-slate-950/15" />
|
|
|
|
<div className="relative flex h-full min-h-[18rem] flex-col justify-between gap-6">
|
|
<div>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<WorldStatusBadge badge={{ label: 'Recurring family', tone: 'sky' }} />
|
|
{archiveCount > 0 ? <WorldStatusBadge badge={{ label: `${archiveCount} archived`, tone: 'amber' }} /> : null}
|
|
{currentWorld?.campaign_state_label ? <WorldStatusBadge badge={{ label: currentWorld.campaign_state_label, tone: currentWorld.campaign_state === 'live_now' ? 'emerald' : 'slate' }} /> : null}
|
|
</div>
|
|
|
|
<h3 className="mt-4 text-3xl font-semibold tracking-[-0.03em] text-white">{family.title}</h3>
|
|
{family.summary ? <p className="mt-4 max-w-2xl text-sm leading-6 text-slate-200/85">{family.summary}</p> : null}
|
|
|
|
<div className="mt-5 flex flex-wrap gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-300/75">
|
|
<span>{editionCount} editions</span>
|
|
{Array.isArray(family.years) && family.years.length > 0 ? <span>{family.years.join(' / ')}</span> : null}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid gap-4">
|
|
<div className="rounded-[22px] border border-white/10 bg-black/25 p-4">
|
|
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">Current edition</div>
|
|
{currentWorld ? (
|
|
<div className="mt-3">
|
|
<a href={sourceSurface ? withWorldSource(currentWorld.public_url, sourceSurface, 'recurring_family_current') : currentWorld.public_url} onClick={() => trackWorldSourceClick({ worldId: currentWorld.id, worldTitle: currentWorld.title, sourceSurface, sourceDetail: 'recurring_family_current' })} className="text-lg font-semibold text-white transition hover:text-sky-200">{currentWorld.title}</a>
|
|
{currentWorld.summary ? <p className="mt-2 text-sm leading-6 text-slate-300/85">{currentWorld.summary}</p> : null}
|
|
</div>
|
|
) : (
|
|
<p className="mt-3 text-sm leading-6 text-slate-300/75">No public edition is currently available.</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="rounded-[22px] border border-white/10 bg-black/25 p-4">
|
|
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">Archive</div>
|
|
{Array.isArray(family.previous_editions) && family.previous_editions.length > 0 ? (
|
|
<div className="mt-3 grid gap-2">
|
|
{family.previous_editions.map((edition) => (
|
|
<a key={edition.id} href={sourceSurface ? withWorldSource(edition.public_url, sourceSurface, 'recurring_family_archive') : edition.public_url} onClick={() => trackWorldSourceClick({ worldId: edition.id, worldTitle: edition.title, sourceSurface, sourceDetail: 'recurring_family_archive' })} className="inline-flex items-center justify-between gap-3 rounded-2xl border border-white/8 bg-white/[0.04] px-3 py-2 text-sm text-slate-200 transition hover:border-white/16 hover:bg-white/[0.07]">
|
|
<span>{edition.title}</span>
|
|
{edition.edition_year ? <span className="text-xs uppercase tracking-[0.14em] text-slate-400">{edition.edition_year}</span> : null}
|
|
</a>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="mt-3 text-sm leading-6 text-slate-300/75">The archive starts with the current edition.</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<a href={familyHref} onClick={() => trackWorldSourceClick({ worldId: family.current_world?.id || 0, worldTitle: family.title, sourceSurface, sourceDetail: sourceDetail || 'recurring_family' })} className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/10 px-4 py-2 text-sm font-semibold text-white transition group-hover:bg-white/15">
|
|
Open family
|
|
<i className="fa-solid fa-arrow-right" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
)
|
|
} |