119 lines
7.1 KiB
JavaScript
119 lines
7.1 KiB
JavaScript
import React, { useState } from 'react'
|
|
import { Link, router, usePage } from '@inertiajs/react'
|
|
import SeoHead from '../../components/seo/SeoHead'
|
|
|
|
function LockedPanel({ pricingUrl, label }) {
|
|
return (
|
|
<div className="rounded-[28px] border border-amber-300/20 bg-amber-300/10 p-6 text-amber-50">
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-amber-100/80">Premium content</p>
|
|
<h2 className="mt-3 text-2xl font-semibold tracking-[-0.04em]">Unlock the full {label}.</h2>
|
|
<p className="mt-3 text-sm leading-7 text-amber-50/90">This preview is visible, but the full Academy content stays server-side until your account has the required Creator or Pro access.</p>
|
|
<Link href={pricingUrl} className="mt-5 inline-flex rounded-full border border-amber-200/25 bg-white/10 px-5 py-3 text-sm font-semibold text-white">See Academy plans</Link>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function AcademyShow({ pageType, item, seo, pricingUrl, completeUrl, completed: initialCompleted, saveUrl, unsaveUrl, saved: initialSaved, submitUrl }) {
|
|
const flash = usePage().props.flash || {}
|
|
const [completed, setCompleted] = useState(Boolean(initialCompleted))
|
|
const [saved, setSaved] = useState(Boolean(initialSaved))
|
|
|
|
const markComplete = () => {
|
|
if (!completeUrl || completed) return
|
|
router.post(completeUrl, {}, {
|
|
preserveScroll: true,
|
|
onSuccess: () => setCompleted(true),
|
|
})
|
|
}
|
|
|
|
const toggleSave = () => {
|
|
const url = saved ? unsaveUrl : saveUrl
|
|
const method = saved ? router.delete : router.post
|
|
method(url, {}, {
|
|
preserveScroll: true,
|
|
onSuccess: () => setSaved(!saved),
|
|
})
|
|
}
|
|
|
|
return (
|
|
<main className="min-h-screen bg-[radial-gradient(circle_at_top_left,_rgba(56,189,248,0.15),_transparent_24%),radial-gradient(circle_at_bottom_right,_rgba(251,191,36,0.16),_transparent_24%),linear-gradient(180deg,_#0f172a_0%,_#111827_100%)] px-4 py-8 sm:px-6 lg:px-8">
|
|
<SeoHead seo={seo || {}} title={item?.title} description={item?.excerpt || item?.description} />
|
|
|
|
<div className="mx-auto max-w-[1200px] space-y-6">
|
|
<section className="rounded-[38px] border border-white/10 bg-black/20 p-8 md:p-10">
|
|
<div className="flex flex-wrap items-start justify-between gap-5">
|
|
<div className="max-w-3xl">
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/80">Skinbase AI Academy</p>
|
|
<h1 className="mt-3 text-4xl font-semibold tracking-[-0.05em] text-white md:text-5xl">{item.title}</h1>
|
|
<p className="mt-4 text-base leading-8 text-slate-300">{item.excerpt || item.description || item.prompt_preview || item.content_preview}</p>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-3">
|
|
{completeUrl ? <button type="button" onClick={markComplete} className="rounded-full border border-emerald-300/25 bg-emerald-300/12 px-5 py-3 text-sm font-semibold text-emerald-100">{completed ? 'Completed' : 'Mark complete'}</button> : null}
|
|
{saveUrl ? <button type="button" onClick={toggleSave} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100">{saved ? 'Saved' : 'Save prompt'}</button> : null}
|
|
{submitUrl ? <Link href={submitUrl} className="rounded-full border border-sky-300/25 bg-sky-300/12 px-5 py-3 text-sm font-semibold text-sky-100">Submit artwork</Link> : null}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{flash.success ? <div className="rounded-2xl border border-emerald-300/20 bg-emerald-300/10 px-4 py-3 text-sm text-emerald-100">{flash.success}</div> : null}
|
|
{flash.error ? <div className="rounded-2xl border border-rose-300/20 bg-rose-300/10 px-4 py-3 text-sm text-rose-100">{flash.error}</div> : null}
|
|
|
|
{item.locked ? <LockedPanel pricingUrl={pricingUrl} label={pageType} /> : null}
|
|
|
|
<section className="rounded-[30px] border border-white/10 bg-white/[0.04] p-6 text-slate-200">
|
|
{pageType === 'lesson' ? <div className="whitespace-pre-wrap text-sm leading-8 text-slate-200">{item.content || item.content_preview}</div> : null}
|
|
{pageType === 'prompt' ? (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Prompt</p>
|
|
<pre className="mt-3 whitespace-pre-wrap rounded-2xl border border-white/10 bg-black/20 p-4 text-sm leading-7 text-slate-200">{item.prompt || item.prompt_preview}</pre>
|
|
</div>
|
|
{item.negative_prompt ? <div><p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Negative prompt</p><pre className="mt-3 whitespace-pre-wrap rounded-2xl border border-white/10 bg-black/20 p-4 text-sm leading-7 text-slate-200">{item.negative_prompt}</pre></div> : null}
|
|
</div>
|
|
) : null}
|
|
{pageType === 'pack' ? (
|
|
<div className="space-y-5">
|
|
<p className="text-sm leading-8 text-slate-200">{item.description}</p>
|
|
<div className="grid gap-4 md:grid-cols-2">
|
|
{(item.prompts || []).map((prompt) => (
|
|
<div key={prompt.id} className="rounded-2xl border border-white/10 bg-black/20 p-4">
|
|
<h3 className="text-lg font-semibold text-white">{prompt.title}</h3>
|
|
<p className="mt-2 text-sm leading-7 text-slate-300">{prompt.excerpt || prompt.prompt_preview}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
{pageType === 'challenge' ? (
|
|
<div className="space-y-6">
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
<div>
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Brief</p>
|
|
<div className="mt-3 whitespace-pre-wrap text-sm leading-8 text-slate-200">{item.brief || item.description}</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Rules</p>
|
|
<div className="mt-3 whitespace-pre-wrap text-sm leading-8 text-slate-200">{item.rules || 'No special rules posted yet.'}</div>
|
|
</div>
|
|
</div>
|
|
{(item.submissions || []).length ? (
|
|
<div>
|
|
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">Approved submissions</p>
|
|
<div className="mt-4 grid gap-4 md:grid-cols-2">
|
|
{item.submissions.map((submission) => (
|
|
<div key={submission.id} className="rounded-2xl border border-white/10 bg-black/20 p-4">
|
|
<h3 className="text-lg font-semibold text-white">{submission.artwork?.title || 'Submission'}</h3>
|
|
<p className="mt-2 text-sm text-slate-400">{submission.user?.name || 'Unknown creator'}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
</section>
|
|
</div>
|
|
</main>
|
|
)
|
|
} |