185 lines
6.8 KiB
TypeScript
185 lines
6.8 KiB
TypeScript
import {
|
|
dashboardKpis,
|
|
pipelineHealth,
|
|
reviewQueue,
|
|
} from "@/lib/dashboard-model";
|
|
import { LeadFunnelBoard } from "@/components/lead-funnel-board";
|
|
import {
|
|
ArrowRight,
|
|
CheckCircle2,
|
|
ClipboardCheck,
|
|
FileSearch,
|
|
ShieldCheck,
|
|
} from "lucide-react";
|
|
|
|
const reviewSignals = [
|
|
{
|
|
label: "Evidence",
|
|
value: "belegt",
|
|
detail: "Audit-Aussagen brauchen Quellen, Screenshots oder geprüfte Seiten.",
|
|
icon: FileSearch,
|
|
surface: "evidence-surface",
|
|
},
|
|
{
|
|
label: "Approval",
|
|
value: "manuell",
|
|
detail: "Public Audit, E-Mail und finaler Versand bleiben getrennte Schritte.",
|
|
icon: ClipboardCheck,
|
|
surface: "review-surface",
|
|
},
|
|
{
|
|
label: "Reputation",
|
|
value: "geschützt",
|
|
detail: "Do-not-contact, Sperrliste und Limits sind Teil der Produktqualität.",
|
|
icon: ShieldCheck,
|
|
surface: "safe-surface",
|
|
},
|
|
];
|
|
|
|
export default function DashboardPage() {
|
|
return (
|
|
<main className="px-4 py-5 sm:px-6 lg:px-8">
|
|
<div className="mx-auto flex w-full max-w-[92rem] flex-col gap-6">
|
|
<header className="agency-panel overflow-hidden">
|
|
<div className="grid gap-5 p-5 lg:grid-cols-[minmax(0,1fr)_minmax(22rem,0.42fr)] lg:p-6">
|
|
<div className="min-w-0">
|
|
<p className="agency-kicker">SaaS Workspace · Agency Evidence Desk</p>
|
|
<h1 className="mt-3 max-w-4xl font-heading text-4xl font-semibold tracking-normal text-foreground">
|
|
Review, Evidence und Outreach-Sicherheit in einem Arbeitsfluss.
|
|
</h1>
|
|
<p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
|
|
WebDev Pipeline hilft Agenturen, lokale Chancen zu finden,
|
|
konkrete Belege zu sammeln und Outreach erst nach sauberer
|
|
Freigabe zu starten.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid gap-2">
|
|
{reviewSignals.map((signal) => {
|
|
const Icon = signal.icon;
|
|
|
|
return (
|
|
<article
|
|
className="rounded-md border border-border/70 bg-background/55 p-3"
|
|
key={signal.label}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<span
|
|
className={`flex size-9 shrink-0 items-center justify-center rounded-md ${signal.surface}`}
|
|
>
|
|
<Icon className="size-4" />
|
|
</span>
|
|
<div className="min-w-0">
|
|
<p className="text-xs font-bold uppercase text-muted-foreground">
|
|
{signal.label}
|
|
</p>
|
|
<p className="text-sm font-semibold">{signal.value}</p>
|
|
<p className="mt-1 text-xs leading-5 text-muted-foreground">
|
|
{signal.detail}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section className="grid gap-3 lg:grid-cols-[minmax(0,0.85fr)_minmax(0,1.15fr)]">
|
|
<div className="agency-panel p-4">
|
|
<div className="flex items-center justify-between gap-3">
|
|
<div>
|
|
<p className="agency-kicker">Heute prüfen</p>
|
|
<h2 className="mt-1 font-heading text-xl font-semibold">
|
|
Approval Queue
|
|
</h2>
|
|
</div>
|
|
<span className="rounded-md bg-[var(--surface-review)] px-2.5 py-1 text-sm font-bold text-secondary-foreground">
|
|
{reviewQueue.length} offen
|
|
</span>
|
|
</div>
|
|
<div className="mt-4 grid gap-2">
|
|
{reviewQueue.map((item) => (
|
|
<article
|
|
className="rounded-md border border-border/75 bg-background/60 p-3"
|
|
key={`${item.title}-${item.company}`}
|
|
>
|
|
<div className="flex items-start justify-between gap-3">
|
|
<div className="min-w-0">
|
|
<h3 className="text-sm font-semibold">{item.title}</h3>
|
|
<p className="mt-1 text-sm text-muted-foreground">
|
|
{item.company}
|
|
</p>
|
|
</div>
|
|
<ArrowRight className="mt-1 size-4 shrink-0 text-primary" />
|
|
</div>
|
|
<p className="mt-2 text-sm leading-5 text-muted-foreground">
|
|
{item.detail}
|
|
</p>
|
|
</article>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<section
|
|
className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4"
|
|
aria-label="Pipeline-Kennzahlen"
|
|
>
|
|
{dashboardKpis.map((kpi) => (
|
|
<article className="agency-rail p-4" key={kpi.label}>
|
|
<p className="text-xs font-bold uppercase text-muted-foreground">
|
|
{kpi.label}
|
|
</p>
|
|
<p className="mt-3 font-heading text-3xl font-semibold tracking-normal">
|
|
{kpi.value}
|
|
</p>
|
|
<p className="mt-2 text-sm leading-5 text-muted-foreground">
|
|
{kpi.detail}
|
|
</p>
|
|
</article>
|
|
))}
|
|
</section>
|
|
</section>
|
|
|
|
<LeadFunnelBoard />
|
|
|
|
<section className="agency-panel p-4">
|
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
|
|
<div>
|
|
<p className="agency-kicker">Workspace Health</p>
|
|
<h2 className="mt-1 font-heading text-xl font-semibold">
|
|
Safety Ledger
|
|
</h2>
|
|
</div>
|
|
<p className="inline-flex items-center gap-2 text-sm font-semibold text-muted-foreground">
|
|
<CheckCircle2 className="size-4 text-primary" />
|
|
Kundenreputation bleibt geschützt
|
|
</p>
|
|
</div>
|
|
<div className="mt-4 grid gap-3 md:grid-cols-3">
|
|
{pipelineHealth.map((item) => {
|
|
const Icon = item.icon;
|
|
|
|
return (
|
|
<div
|
|
className="rounded-md border border-border/75 bg-background/60 p-3"
|
|
key={item.label}
|
|
>
|
|
<span className="inline-flex items-center gap-2 text-sm font-semibold">
|
|
<Icon className="size-4 text-primary" />
|
|
{item.label}
|
|
</span>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
{item.value}
|
|
</p>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|