import React from 'react' import { Head, Link, router, usePage } from '@inertiajs/react' function getCsrfToken() { if (typeof document === 'undefined') return '' return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '' } async function requestJson(url, { method = 'POST', body } = {}) { const response = await fetch(url, { method, credentials: 'same-origin', headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken(), 'X-Requested-With': 'XMLHttpRequest', }, body: body ? JSON.stringify(body) : undefined, }) const payload = await response.json().catch(() => ({})) if (!response.ok) { throw new Error(payload?.message || payload?.errors?.story?.[0] || 'Request failed.') } return payload } function replacePattern(pattern, value) { return String(pattern || '').replace('__STORY__', String(value)).replace('__WORLD__', String(value)) } function StatusBadge({ story }) { const tone = story.status === 'published' ? 'border-emerald-300/20 bg-emerald-400/12 text-emerald-100' : story.status === 'archived' ? 'border-amber-300/20 bg-amber-400/12 text-amber-100' : 'border-white/10 bg-white/[0.06] text-slate-200' return {story.status} } export default function WorldWebStoriesIndex() { const { props } = usePage() const stories = props.stories || { data: [] } const endpoints = props.endpoints || {} const worldOptions = props.worldOptions || [] const [filters, setFilters] = React.useState(props.filters || { q: '', status: 'all' }) const [notice, setNotice] = React.useState('') const [error, setError] = React.useState('') const [busyKey, setBusyKey] = React.useState('') const [generator, setGenerator] = React.useState({ world_id: worldOptions[0]?.value || '', pages: 7, force: false, publish: false }) React.useEffect(() => { setFilters(props.filters || { q: '', status: 'all' }) }, [props.filters]) function applyFilters(event) { event.preventDefault() router.get(endpoints.index, filters, { preserveState: true, replace: true, preserveScroll: true }) } async function performAction(key, url, method = 'POST', body = null) { setBusyKey(key) setNotice('') setError('') try { const payload = await requestJson(url, { method, body }) setNotice(payload.message || 'Action completed.') router.reload({ only: ['stories', 'stats', 'filters'], preserveScroll: true }) } catch (requestError) { setError(requestError.message || 'Action failed.') } finally { setBusyKey('') } } async function generateDraft(event) { event.preventDefault() if (!generator.world_id) return setBusyKey('generate') setNotice('') setError('') try { const payload = await requestJson(replacePattern(endpoints.generatePattern, generator.world_id), { body: { pages: Number(generator.pages || 7), force: Boolean(generator.force), publish: Boolean(generator.publish), }, }) setNotice(payload.message || 'Web story generated.') if (payload.story?.edit_url) { router.visit(payload.story.edit_url) return } router.reload({ only: ['stories', 'stats'], preserveScroll: true }) } catch (requestError) { setError(requestError.message || 'Generation failed.') } finally { setBusyKey('') } } return (

Moderation surface

World Web Stories

Create standalone AMP Web Stories for Skinbase Worlds, keep them self-canonical, and publish only when the story is complete and visible.

New story
{[ ['Total stories', props.stats?.total || 0], ['Published', props.stats?.published || 0], ['Drafts', props.stats?.draft || 0], ['Hidden', props.stats?.hidden || 0], ].map(([label, value]) => (
{label}
{Number(value).toLocaleString()}
))}
setFilters((current) => ({ ...current, q: event.target.value }))} placeholder="Search by title, slug, or world" className="rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" />
Generate from World
setGenerator((current) => ({ ...current, pages: event.target.value }))} className="rounded-2xl border border-white/10 bg-slate-950/70 px-4 py-3 text-sm text-white outline-none" />
{notice ?
{notice}
: null} {error ?
{error}
: null}
{(stories.data || []).map((story) => (
{story.poster_portrait_url ? {story.title} :
}
{!story.active ? inactive : null} {story.noindex ? noindex : null}

{story.title}

/{story.slug}{story.world ? ` • ${story.world.title}` : ''}

{story.excerpt ?

{story.excerpt}

: null}
{story.page_count} pages {story.published_at ? {new Date(story.published_at).toLocaleDateString()} : null}
Edit Open {story.status === 'published' ? ( ) : ( )}
))}
) }