72 lines
4.8 KiB
JavaScript
72 lines
4.8 KiB
JavaScript
import React from 'react'
|
|
import { router, usePage } from '@inertiajs/react'
|
|
import StudioLayout from '../../Layouts/StudioLayout'
|
|
import WorldStatusBadge from '../../components/worlds/WorldStatusBadge'
|
|
import WorldAnalyticsPortfolioPanel from '../../components/worlds/editor/analytics/WorldAnalyticsPortfolioPanel'
|
|
import NovaSelect from '../../components/ui/NovaSelect'
|
|
|
|
export default function StudioWorldsIndex() {
|
|
const { props } = usePage()
|
|
const listing = props.listing || {}
|
|
const items = Array.isArray(listing.items) ? listing.items : []
|
|
const filters = listing.filters || {}
|
|
|
|
const updateFilter = (name, value) => {
|
|
router.get('/studio/worlds', { ...filters, [name]: value }, { preserveState: true, replace: true })
|
|
}
|
|
|
|
return (
|
|
<StudioLayout title={props.title} subtitle={props.description}>
|
|
<div className="grid gap-6">
|
|
<WorldAnalyticsPortfolioPanel analytics={props.analytics} />
|
|
|
|
<section className="rounded-[28px] border border-white/10 bg-white/[0.03] p-5">
|
|
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_12rem_12rem_auto] lg:items-end">
|
|
<label className="grid gap-2 text-sm text-slate-300">
|
|
<span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Search</span>
|
|
<input value={filters.q || ''} onChange={(event) => updateFilter('q', event.target.value)} placeholder="Search title, slug, or summary" className="rounded-2xl border border-white/10 bg-black/20 px-4 py-3 text-white outline-none" />
|
|
</label>
|
|
<div className="grid gap-2 text-sm text-slate-300">
|
|
<span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Status</span>
|
|
<NovaSelect value={filters.status || ''} onChange={(val) => updateFilter('status', val)} options={[{ value: '', label: 'All statuses' }, ...(props.statusOptions || [])]} searchable={false} />
|
|
</div>
|
|
<div className="grid gap-2 text-sm text-slate-300">
|
|
<span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Type</span>
|
|
<NovaSelect value={filters.type || ''} onChange={(val) => updateFilter('type', val)} options={[{ value: '', label: 'All types' }, ...(props.typeOptions || [])]} searchable={false} />
|
|
</div>
|
|
<a href={props.createUrl} className="inline-flex items-center justify-center gap-2 rounded-2xl border border-sky-300/20 bg-sky-400/10 px-5 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15"><i className="fa-solid fa-plus" />New world</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="grid gap-4 xl:grid-cols-2">
|
|
{items.length > 0 ? items.map((world) => (
|
|
<a key={world.id} href={world.edit_url} className="rounded-[28px] border border-white/10 bg-white/[0.03] p-5 transition hover:-translate-y-1 hover:border-white/20 hover:bg-white/[0.05]">
|
|
<div className="flex flex-wrap items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">
|
|
<WorldStatusBadge badge={{ label: world.status, tone: 'slate' }} />
|
|
<WorldStatusBadge badge={{ label: world.type, tone: 'slate' }} />
|
|
{(Array.isArray(world.status_badges) ? world.status_badges : []).map((badge) => <WorldStatusBadge key={`${world.id}-${badge.label}`} badge={badge} />)}
|
|
</div>
|
|
<h2 className="mt-4 text-2xl font-semibold tracking-[-0.03em] text-white">{world.title}</h2>
|
|
<div className="mt-2 text-sm text-slate-500">/{world.slug}</div>
|
|
{world.summary ? <p className="mt-4 text-sm leading-6 text-slate-300">{world.summary}</p> : null}
|
|
<div className="mt-5 flex flex-wrap gap-4 text-sm text-slate-400">
|
|
{world.timeframe_label ? <span>{world.timeframe_label}</span> : null}
|
|
{world.promotion_window_label ? <span>{world.promotion_window_label}</span> : null}
|
|
<span>{world.relation_count} relations</span>
|
|
{world.live_submission_count > 0 ? <span>{world.live_submission_count} live submissions</span> : null}
|
|
{world.theme_key ? <span>{world.theme_key}</span> : null}
|
|
</div>
|
|
<div className="mt-5 flex flex-wrap gap-3 text-sm font-semibold">
|
|
<span className="text-sky-100">Edit</span>
|
|
<span className="text-slate-500">Preview</span>
|
|
{world.public_url ? <span className="text-slate-500">Public</span> : null}
|
|
</div>
|
|
</a>
|
|
)) : (
|
|
<div className="rounded-[28px] border border-dashed border-white/10 bg-white/[0.02] p-6 text-sm text-slate-400">No worlds match this filter yet.</div>
|
|
)}
|
|
</section>
|
|
</div>
|
|
</StudioLayout>
|
|
)
|
|
} |