feat(admin-web): add Taste Skill spike route
This commit is contained in:
parent
11f609eb3d
commit
ea3f9565bb
224
dashboards/admin-web/src/app/taste-spike/page.tsx
Normal file
224
dashboards/admin-web/src/app/taste-spike/page.tsx
Normal file
@ -0,0 +1,224 @@
|
||||
import Link from 'next/link';
|
||||
import {
|
||||
Activity,
|
||||
ArrowRight,
|
||||
CheckCircle2,
|
||||
Gauge,
|
||||
Layers3,
|
||||
ShieldCheck,
|
||||
Sparkles,
|
||||
TimerReset,
|
||||
} from 'lucide-react';
|
||||
|
||||
type Mode = 'before' | 'after';
|
||||
|
||||
const genericCards = [
|
||||
['AI insights', 'Powerful dashboards with smart automation for every team.'],
|
||||
['Fast workflows', 'Streamline productivity with an intuitive modern interface.'],
|
||||
['Better analytics', 'Unlock value from your data with beautiful charts.'],
|
||||
];
|
||||
|
||||
const healthSignals = [
|
||||
{ label: 'Tenant isolation', value: '98', tone: 'bg-emerald-500' },
|
||||
{ label: 'Replay safety', value: '91', tone: 'bg-cyan-500' },
|
||||
{ label: 'Queue pressure', value: '23', tone: 'bg-amber-500' },
|
||||
];
|
||||
|
||||
const incidents = [
|
||||
['07:42', 'Digest queue recovered after backoff', 'Auto-remediated'],
|
||||
['06:10', 'One webhook endpoint has 429 drift', 'Watch'],
|
||||
['02:55', 'Nightly tenant export completed', 'Verified'],
|
||||
];
|
||||
|
||||
function BeforeSpike() {
|
||||
return (
|
||||
<main className="min-h-screen bg-gradient-to-br from-violet-950 via-slate-950 to-blue-950 px-6 py-10 text-white">
|
||||
<section className="mx-auto flex max-w-6xl flex-col items-center justify-center gap-8 py-20 text-center">
|
||||
<div className="rounded-full border border-white/20 bg-white/10 px-4 py-1 text-sm backdrop-blur">
|
||||
AI-powered dashboard
|
||||
</div>
|
||||
<div className="space-y-5">
|
||||
<h1 className="mx-auto max-w-4xl text-5xl font-bold tracking-tight md:text-7xl">
|
||||
Transform your business with intelligent insights
|
||||
</h1>
|
||||
<p className="mx-auto max-w-2xl text-lg text-white/70">
|
||||
A generic centered hero, purple-blue gradient, and three equal cards. This is the
|
||||
baseline slop pattern the redesign skill flags.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap justify-center gap-3">
|
||||
<Link
|
||||
href="/taste-spike?mode=after"
|
||||
className="rounded-full bg-white px-6 py-3 font-semibold text-slate-950"
|
||||
>
|
||||
View refined version
|
||||
</Link>
|
||||
<button className="rounded-full border border-white/25 px-6 py-3 text-white/80">
|
||||
Learn more
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mx-auto grid max-w-6xl gap-4 md:grid-cols-3">
|
||||
{genericCards.map(([title, description]) => (
|
||||
<article
|
||||
key={title}
|
||||
className="rounded-3xl border border-white/10 bg-white/10 p-8 shadow-2xl"
|
||||
>
|
||||
<Sparkles className="mb-6 h-8 w-8 text-violet-200" aria-hidden />
|
||||
<h2 className="text-2xl font-bold">{title}</h2>
|
||||
<p className="mt-3 text-sm leading-6 text-white/65">{description}</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function AfterSpike() {
|
||||
return (
|
||||
<main className="min-h-screen overflow-hidden bg-[#f4f1ea] text-[#17201b]">
|
||||
<section className="relative mx-auto grid max-w-7xl gap-10 px-6 py-8 md:grid-cols-[0.92fr_1.08fr] md:px-10 lg:px-12">
|
||||
<div className="absolute inset-x-0 top-0 -z-10 h-80 bg-[radial-gradient(circle_at_20%_20%,rgba(58,93,80,0.22),transparent_32%),radial-gradient(circle_at_82%_8%,rgba(182,122,65,0.2),transparent_28%)]" />
|
||||
|
||||
<div className="flex min-h-[720px] flex-col justify-between rounded-[2rem] border border-[#17201b]/10 bg-[#fcfaf5]/85 p-8 shadow-[0_24px_80px_rgba(23,32,27,0.12)] backdrop-blur md:p-10">
|
||||
<div className="space-y-9">
|
||||
<div className="flex items-center justify-between gap-4 text-xs uppercase tracking-[0.28em] text-[#6e756c]">
|
||||
<span>ByteLyst ops taste spike</span>
|
||||
<span className="font-mono">VAR 5 / MOT 3 / DEN 7</span>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
<h1 className="max-w-3xl text-balance text-5xl font-semibold leading-[0.92] tracking-[-0.055em] md:text-7xl">
|
||||
Product health, but with an operator's pulse.
|
||||
</h1>
|
||||
<p className="max-w-xl text-pretty text-lg leading-8 text-[#556057]">
|
||||
Reading this as a ByteLyst admin-web dashboard slice for a solo operator, with a
|
||||
trust-first command-center language, leaning toward Tailwind utilities, warm
|
||||
surfaces, tabular numerics, and restrained motion.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 text-sm sm:grid-cols-3">
|
||||
{healthSignals.map(signal => (
|
||||
<div
|
||||
key={signal.label}
|
||||
className="rounded-2xl border border-[#17201b]/10 bg-white/70 p-4"
|
||||
>
|
||||
<div className="mb-4 h-1.5 overflow-hidden rounded-full bg-[#17201b]/10">
|
||||
<div
|
||||
className={`h-full rounded-full ${signal.tone}`}
|
||||
style={{ width: `${signal.value}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="font-mono text-3xl font-semibold tabular-nums">{signal.value}</div>
|
||||
<div className="mt-1 text-[#667066]">{signal.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-5 pt-10 md:pt-28">
|
||||
<div className="grid gap-5 lg:grid-cols-[0.72fr_1fr]">
|
||||
<article className="rounded-[1.7rem] border border-[#17201b]/10 bg-[#16211b] p-6 text-[#f4f1ea] shadow-[0_20px_60px_rgba(22,33,27,0.24)]">
|
||||
<div className="flex items-center justify-between">
|
||||
<Gauge className="h-6 w-6 text-[#b9d8c5]" aria-hidden />
|
||||
<span className="rounded-full bg-[#f4f1ea]/10 px-3 py-1 font-mono text-xs text-[#cbd5cd]">
|
||||
live
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-16 font-mono text-7xl font-semibold tracking-[-0.08em] tabular-nums">
|
||||
86
|
||||
</div>
|
||||
<p className="mt-3 max-w-48 text-sm leading-6 text-[#cbd5cd]">
|
||||
Aggregate health score, weighted toward retention and runtime stability.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article className="rounded-[1.7rem] border border-[#17201b]/10 bg-white/80 p-6 shadow-[0_20px_60px_rgba(23,32,27,0.08)]">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-[#6e756c]">Today's operator brief</p>
|
||||
<h2 className="mt-2 text-3xl font-semibold tracking-[-0.04em]">
|
||||
Three signals need eyes.
|
||||
</h2>
|
||||
</div>
|
||||
<Activity className="h-6 w-6 text-[#3f6b5a]" aria-hidden />
|
||||
</div>
|
||||
<div className="mt-8 space-y-3">
|
||||
{incidents.map(([time, item, state]) => (
|
||||
<div
|
||||
key={item}
|
||||
className="grid grid-cols-[4rem_1fr_auto] items-center gap-3 rounded-2xl bg-[#f4f1ea] px-4 py-3 text-sm"
|
||||
>
|
||||
<span className="font-mono text-[#6e756c]">{time}</span>
|
||||
<span className="font-medium">{item}</span>
|
||||
<span className="rounded-full bg-[#17201b]/5 px-2.5 py-1 text-xs text-[#556057]">
|
||||
{state}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5 lg:grid-cols-3">
|
||||
{[
|
||||
[
|
||||
ShieldCheck,
|
||||
'Trust surface',
|
||||
'No neon AI gradient; warm neutrals keep admin tasks calm.',
|
||||
],
|
||||
[
|
||||
Layers3,
|
||||
'Density tuned',
|
||||
'Cards carry more information without collapsing into a spreadsheet.',
|
||||
],
|
||||
[
|
||||
TimerReset,
|
||||
'Motion-safe',
|
||||
'Only hover/entrance affordances; reduced-motion users keep a static view.',
|
||||
],
|
||||
].map(([Icon, title, copy]) => (
|
||||
<article
|
||||
key={title as string}
|
||||
className="group rounded-[1.4rem] border border-[#17201b]/10 bg-[#fcfaf5]/80 p-5 transition-transform motion-safe:hover:-translate-y-1"
|
||||
>
|
||||
<Icon className="h-5 w-5 text-[#3f6b5a]" aria-hidden />
|
||||
<h3 className="mt-8 text-lg font-semibold tracking-[-0.02em]">{title as string}</h3>
|
||||
<p className="mt-2 text-sm leading-6 text-[#667066]">{copy as string}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center justify-between gap-4 rounded-[1.7rem] border border-[#17201b]/10 bg-[#dfe8dc] p-5">
|
||||
<div className="flex items-center gap-3">
|
||||
<CheckCircle2 className="h-5 w-5 text-[#3f6b5a]" aria-hidden />
|
||||
<p className="font-medium">
|
||||
Recommendation: keep as an optional redesign prompt, not a default dashboard
|
||||
generator.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/taste-spike?mode=before"
|
||||
className="inline-flex items-center gap-2 rounded-full bg-[#17201b] px-4 py-2 text-sm font-semibold text-[#f4f1ea]"
|
||||
>
|
||||
Compare baseline <ArrowRight className="h-4 w-4" aria-hidden />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function TasteSpikePage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams?: Promise<{ mode?: string }>;
|
||||
}) {
|
||||
const params = await searchParams;
|
||||
const mode: Mode = params?.mode === 'before' ? 'before' : 'after';
|
||||
|
||||
return mode === 'before' ? <BeforeSpike /> : <AfterSpike />;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user