Save workspace changes
This commit is contained in:
115
resources/js/components/worlds/editor/WorldSummaryCard.jsx
Normal file
115
resources/js/components/worlds/editor/WorldSummaryCard.jsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import React from 'react'
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) return 'Not set'
|
||||
const date = new Date(value)
|
||||
if (Number.isNaN(date.getTime())) return 'Not set'
|
||||
return new Intl.DateTimeFormat('en', { dateStyle: 'medium', timeStyle: 'short' }).format(date)
|
||||
}
|
||||
|
||||
function typeLabel(value) {
|
||||
const labels = {
|
||||
seasonal: 'Seasonal',
|
||||
event: 'Event',
|
||||
campaign: 'Campaign',
|
||||
tribute: 'Tribute',
|
||||
}
|
||||
|
||||
return labels[value] || value || 'Seasonal'
|
||||
}
|
||||
|
||||
function promotionState(world, state) {
|
||||
if (!world?.is_featured) {
|
||||
return {
|
||||
label: 'Public page only',
|
||||
message: 'This world will live at its own URL, but it is not currently marked for homepage or Worlds spotlight placement.',
|
||||
tone: 'slate',
|
||||
}
|
||||
}
|
||||
|
||||
if (state.label === 'Live') {
|
||||
return {
|
||||
label: 'Active seasonal promotion',
|
||||
message: 'Featured promotion is enabled and the world is live, so it is ready for homepage spotlight and promoted Worlds surfaces.',
|
||||
tone: 'emerald',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
label: 'Homepage spotlight eligible',
|
||||
message: 'Featured promotion is enabled. Once the world is live, it becomes eligible for homepage and Worlds spotlight treatment.',
|
||||
tone: 'sky',
|
||||
}
|
||||
}
|
||||
|
||||
function workflowState(world) {
|
||||
const now = Date.now()
|
||||
const publishedAt = world?.published_at ? new Date(world.published_at).getTime() : null
|
||||
const startsAt = world?.starts_at ? new Date(world.starts_at).getTime() : null
|
||||
const endsAt = world?.ends_at ? new Date(world.ends_at).getTime() : null
|
||||
|
||||
if (world?.status === 'archived') {
|
||||
return { label: 'Archived', message: 'This world has ended and is no longer part of the active campaign cycle.', tone: 'amber' }
|
||||
}
|
||||
|
||||
if (world?.status !== 'published') {
|
||||
return { label: 'Draft', message: 'Editors can keep refining this world before it becomes publicly visible.', tone: 'slate' }
|
||||
}
|
||||
|
||||
if (publishedAt && publishedAt > now) {
|
||||
return { label: 'Scheduled', message: `This world will publish automatically on ${formatDateTime(world.published_at)}.`, tone: 'sky' }
|
||||
}
|
||||
|
||||
if (startsAt && startsAt > now) {
|
||||
return { label: 'Scheduled', message: `This world is published and will go live automatically on ${formatDateTime(world.starts_at)}.`, tone: 'sky' }
|
||||
}
|
||||
|
||||
if (endsAt && endsAt < now) {
|
||||
return { label: 'Ended', message: 'The campaign window has passed. Archive it or create a new edition to continue the lineage.', tone: 'amber' }
|
||||
}
|
||||
|
||||
return { label: 'Live', message: 'This world is currently active on public surfaces.', tone: 'emerald' }
|
||||
}
|
||||
|
||||
const tones = {
|
||||
slate: 'border-white/10 bg-white/[0.04] text-slate-100',
|
||||
sky: 'border-sky-300/20 bg-sky-400/10 text-sky-100',
|
||||
emerald: 'border-emerald-300/20 bg-emerald-400/10 text-emerald-100',
|
||||
amber: 'border-amber-300/20 bg-amber-400/10 text-amber-100',
|
||||
}
|
||||
|
||||
export default function WorldSummaryCard({ world, themeLabel, relationCount, enabledSectionsCount }) {
|
||||
const state = workflowState(world)
|
||||
const promotion = promotionState(world, state)
|
||||
|
||||
return (
|
||||
<div className="rounded-[28px] border border-white/10 bg-white/[0.03] p-5">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-white">Campaign summary</h2>
|
||||
<p className="mt-1 text-sm leading-6 text-slate-400">See the world lifecycle, promotion state, and editorial readiness without parsing the whole form.</p>
|
||||
</div>
|
||||
<div className={`rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] ${tones[state.tone]}`}>{state.label}</div>
|
||||
</div>
|
||||
|
||||
<div className={`mt-4 rounded-[24px] border px-4 py-4 text-sm leading-6 ${tones[state.tone]}`}>
|
||||
{state.message}
|
||||
</div>
|
||||
|
||||
<div className={`mt-3 rounded-[24px] border px-4 py-4 text-sm leading-6 ${tones[promotion.tone]}`}>
|
||||
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] opacity-80">Promotion scope</div>
|
||||
<div className="mt-1 font-semibold">{promotion.label}</div>
|
||||
<div className="mt-1">{promotion.message}</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 grid gap-3 sm:grid-cols-2">
|
||||
<div className="rounded-2xl border border-white/10 bg-black/20 p-4"><div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Type</div><div className="mt-2 text-sm font-semibold text-white">{typeLabel(world?.type)}</div></div>
|
||||
<div className="rounded-2xl border border-white/10 bg-black/20 p-4"><div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Theme preset</div><div className="mt-2 text-sm font-semibold text-white">{themeLabel || 'No preset'}</div></div>
|
||||
<div className="rounded-2xl border border-white/10 bg-black/20 p-4"><div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Campaign window</div><div className="mt-2 text-sm font-semibold text-white">{world?.starts_at || world?.ends_at ? `${formatDateTime(world?.starts_at)} to ${formatDateTime(world?.ends_at)}` : 'Open ended'}</div></div>
|
||||
<div className="rounded-2xl border border-white/10 bg-black/20 p-4"><div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Publish at</div><div className="mt-2 text-sm font-semibold text-white">{formatDateTime(world?.published_at)}</div></div>
|
||||
<div className="rounded-2xl border border-white/10 bg-black/20 p-4"><div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Recurrence</div><div className="mt-2 text-sm font-semibold text-white">{world?.is_recurring ? `${world?.recurrence_key || 'recurring'} ${world?.edition_year || ''}`.trim() : 'One-off world'}</div></div>
|
||||
<div className="rounded-2xl border border-white/10 bg-black/20 p-4"><div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">Editorial setup</div><div className="mt-2 text-sm font-semibold text-white">{relationCount} relations · {enabledSectionsCount} enabled sections</div></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user