Implement academy analytics, billing, and web stories updates

This commit is contained in:
2026-05-26 07:27:29 +02:00
parent 456c3d6bb0
commit 0b33a1b074
177 changed files with 27360 additions and 2685 deletions

View File

@@ -143,6 +143,40 @@ function RewardedContributors({ section, world }) {
)
}
function WebStoryCard({ story, worldTitle }) {
if (!story) {
return null
}
return (
<section className="mt-10 rounded-[28px] border border-sky-300/18 bg-[linear-gradient(135deg,rgba(14,165,233,0.12),rgba(15,23,42,0.85))] p-5">
<div className="grid gap-5 lg:grid-cols-[220px,1fr] lg:items-center">
<div className="overflow-hidden rounded-[24px] border border-white/10 bg-black/20 aspect-[3/4] max-w-[220px]">
{story.poster_portrait_url ? (
<img src={story.poster_portrait_url} alt={story.title} className="h-full w-full object-cover" />
) : (
<div className="flex h-full items-center justify-center text-slate-500">
<i className="fa-solid fa-book-open-reader text-4xl" />
</div>
)}
</div>
<div>
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-sky-100/80">Web Story</div>
<h2 className="mt-3 text-2xl font-semibold tracking-[-0.03em] text-white">Experience this World as a Web Story</h2>
<p className="mt-2 max-w-3xl text-sm leading-6 text-slate-200">Swipe through a cinematic visual preview of {worldTitle}.</p>
{story.excerpt ? <p className="mt-3 max-w-3xl text-sm leading-6 text-slate-300">{story.excerpt}</p> : null}
<div className="mt-5">
<a href={story.url} className="inline-flex items-center gap-2 rounded-full border border-white/12 bg-white/[0.08] px-5 py-2.5 text-xs font-semibold uppercase tracking-[0.16em] text-white transition hover:bg-white/[0.14]">
View Web Story
<i className="fa-solid fa-arrow-right" />
</a>
</div>
</div>
</div>
</section>
)
}
export default function WorldShow() {
const { props } = usePage()
const world = props.world
@@ -160,6 +194,7 @@ export default function WorldShow() {
const currentEdition = props.currentEdition || null
const previousEdition = props.previousEdition || null
const nextEdition = props.nextEdition || null
const webStory = props.webStory || world?.published_web_story || null
const archiveTitle = currentEdition ? 'Previous Editions' : 'Archive Editions'
const archiveDescription = currentEdition
? 'Earlier editions remain public so the recurring family keeps its full history accessible.'
@@ -265,6 +300,8 @@ export default function WorldShow() {
</section>
) : null}
<WebStoryCard story={webStory} worldTitle={world?.title || 'this World'} />
{sections.length > 0 ? sections.map((section) => <WorldSection key={section.key} section={section} />) : null}
<WorldChallengeEntriesRail section={linkedChallengeEntries} challengeId={linkedChallenge?.id || null} />