134 lines
7.7 KiB
JavaScript
134 lines
7.7 KiB
JavaScript
import React from 'react'
|
|
import { Head, Link, usePage } from '@inertiajs/react'
|
|
import StudioLayout from '../../Layouts/StudioLayout'
|
|
import NovaCardCanvasPreview from '../../components/nova-cards/NovaCardCanvasPreview'
|
|
|
|
function requestJson(url, { method = 'POST' } = {}) {
|
|
return fetch(url, {
|
|
method,
|
|
credentials: 'same-origin',
|
|
headers: {
|
|
Accept: 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
},
|
|
}).then(async (response) => {
|
|
const payload = await response.json().catch(() => ({}))
|
|
if (!response.ok) throw new Error(payload?.message || 'Request failed')
|
|
return payload
|
|
})
|
|
}
|
|
|
|
function StatCard({ label, value, icon }) {
|
|
return (
|
|
<div className="rounded-[24px] border border-white/10 bg-white/[0.04] p-5 shadow-[0_20px_50px_rgba(2,6,23,0.18)]">
|
|
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500">{label}</div>
|
|
<div className="mt-3 flex items-center gap-3">
|
|
<span className="inline-flex h-12 w-12 items-center justify-center rounded-2xl border border-sky-300/20 bg-sky-400/10 text-sky-100">
|
|
<i className={`fa-solid ${icon}`} />
|
|
</span>
|
|
<span className="text-3xl font-semibold tracking-[-0.04em] text-white">{value}</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function StudioCardsIndex() {
|
|
const { props } = usePage()
|
|
const cards = props.cards?.data || []
|
|
const stats = props.stats || {}
|
|
const endpoints = props.endpoints || {}
|
|
|
|
async function duplicateCard(cardId) {
|
|
const url = (endpoints.duplicatePattern || '').replace('__CARD__', String(cardId))
|
|
if (!url) return
|
|
|
|
const payload = await requestJson(url)
|
|
if (payload?.data?.id) {
|
|
window.location.assign((endpoints.editPattern || '/studio/cards/__CARD__/edit').replace('__CARD__', String(payload.data.id)))
|
|
}
|
|
}
|
|
|
|
return (
|
|
<StudioLayout title="Nova Cards">
|
|
<Head title="Nova Cards Studio" />
|
|
|
|
<section className="rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.15),transparent_38%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.88))] p-6 shadow-[0_24px_70px_rgba(2,6,23,0.32)]">
|
|
<div className="flex flex-col gap-5 lg:flex-row lg:items-end lg:justify-between">
|
|
<div className="max-w-3xl">
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.28em] text-sky-200/75">Creation surface</p>
|
|
<h2 className="mt-3 text-3xl font-semibold tracking-[-0.04em] text-white">Build quote cards, mood cards, and visual text art.</h2>
|
|
<p className="mt-3 text-sm leading-7 text-slate-300">Drafts autosave, templates stay structured, and every published card gets a public preview image ready for discovery and sharing.</p>
|
|
</div>
|
|
<div className="flex flex-wrap gap-3">
|
|
<Link href={endpoints.create || '/studio/cards/create'} className="inline-flex items-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 card
|
|
</Link>
|
|
<a href="/cards" className="inline-flex items-center gap-2 rounded-2xl border border-white/10 bg-white/[0.05] px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/[0.08]">
|
|
<i className="fa-solid fa-compass" />
|
|
Browse public cards
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
<StatCard label="All cards" value={stats.all || 0} icon="fa-layer-group" />
|
|
<StatCard label="Drafts" value={stats.drafts || 0} icon="fa-file-lines" />
|
|
<StatCard label="Processing" value={stats.processing || 0} icon="fa-wand-magic-sparkles" />
|
|
<StatCard label="Published" value={stats.published || 0} icon="fa-earth-americas" />
|
|
</section>
|
|
|
|
<section className="mt-8">
|
|
<div className="mb-4 flex items-center justify-between gap-4">
|
|
<div>
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-500">Latest work</p>
|
|
<h3 className="mt-1 text-2xl font-semibold text-white">Your card library</h3>
|
|
</div>
|
|
</div>
|
|
|
|
{cards.length === 0 ? (
|
|
<div className="rounded-[28px] border border-dashed border-white/12 bg-white/[0.03] px-6 py-16 text-center">
|
|
<div className="mx-auto flex h-20 w-20 items-center justify-center rounded-[24px] border border-white/12 bg-white/[0.05] text-slate-400">
|
|
<i className="fa-solid fa-rectangle-history-circle-user text-3xl" />
|
|
</div>
|
|
<h3 className="mt-5 text-2xl font-semibold text-white">No cards yet</h3>
|
|
<p className="mx-auto mt-3 max-w-xl text-sm leading-7 text-slate-300">Start with a square card or jump straight into a story-sized template. Your first draft will be created automatically in the editor.</p>
|
|
<Link href={endpoints.create || '/studio/cards/create'} className="mt-6 inline-flex items-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" />
|
|
Create your first card
|
|
</Link>
|
|
</div>
|
|
) : (
|
|
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
|
{cards.map((card) => (
|
|
<div key={card.id} className="group rounded-[28px] border border-white/10 bg-white/[0.04] p-4 shadow-[0_22px_60px_rgba(2,6,23,0.22)] transition hover:-translate-y-1 hover:border-sky-300/30 hover:bg-white/[0.06]">
|
|
<a href={(endpoints.editPattern || '/studio/cards/__CARD__/edit').replace('__CARD__', String(card.id))}>
|
|
<NovaCardCanvasPreview card={card} className="w-full" />
|
|
<div className="mt-4 flex items-start justify-between gap-4">
|
|
<div className="min-w-0">
|
|
<div className="truncate text-lg font-semibold tracking-[-0.03em] text-white">{card.title}</div>
|
|
<div className="mt-1 line-clamp-2 text-sm leading-6 text-slate-300">{card.quote_text}</div>
|
|
</div>
|
|
<span className={`rounded-full border px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] ${card.status === 'published' ? 'border-emerald-300/25 bg-emerald-400/10 text-emerald-100' : card.status === 'processing' ? 'border-amber-300/25 bg-amber-400/10 text-amber-100' : 'border-white/10 bg-white/[0.05] text-slate-200'}`}>
|
|
{card.status}
|
|
</span>
|
|
</div>
|
|
<div className="mt-4 flex items-center justify-between text-xs text-slate-400">
|
|
<span>{card.category?.name || 'Uncategorized'}</span>
|
|
<span>{card.format}</span>
|
|
</div>
|
|
</a>
|
|
<div className="mt-4 flex gap-3">
|
|
<a href={(endpoints.editPattern || '/studio/cards/__CARD__/edit').replace('__CARD__', String(card.id))} className="flex-1 rounded-2xl border border-white/10 bg-white/[0.05] px-4 py-3 text-center text-sm font-semibold text-white transition hover:bg-white/[0.08]">Edit</a>
|
|
<button type="button" onClick={() => duplicateCard(card.id)} className="rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15">Duplicate</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
</StudioLayout>
|
|
)
|
|
} |