amana / frontend /src /components /WelcomeModal.tsx
Misbahuddin's picture
feat(ui): first-run welcome modal + Light/Auto/Dark theme
61cdd31
Raw
History Blame Contribute Delete
4.88 kB
import { useEffect, useRef } from "react";
import { ArrowRight, X } from "lucide-react";
const STEPS: { n: number; title: string; body: string }[] = [
{
n: 1,
title: "Pick a campaign",
body: "Choose a submission from the review queue on the left.",
},
{
n: 2,
title: "Run AI triage",
body: "The agent investigates and recommends — citing the exact policy rules, surfacing risk signals, and showing the deterministic policy gate, which enforces safety rules in code and can only route a case to a human (never auto-approve or auto-reject).",
},
{
n: 3,
title: "You decide",
body: "Approve, Reject, or Request info. Overriding the AI requires a written reason — every decision is recorded against your name.",
},
];
// camp id -> one-line reason it's worth trying. Clicking selects the campaign and closes the modal.
const SHOWCASE: { id: string; note: string }[] = [
{ id: "camp-017", note: "approves clearing a debt's principal (hardship relief)" },
{ id: "camp-005", note: "rejects an interest-bearing investment — reads policy, not keywords" },
{ id: "camp-015", note: "prompt injection: flagged and escalated, never obeyed" },
];
export function WelcomeModal({
onClose,
onPick,
}: {
onClose: () => void;
onPick: (id: string) => void;
}) {
const startRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
startRef.current?.focus();
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [onClose]);
return (
<div
className="fixed inset-0 z-50 grid place-items-center bg-black/40 p-4 anim-fade"
onClick={onClose}
>
<div
role="dialog"
aria-modal="true"
aria-labelledby="welcome-title"
onClick={(e) => e.stopPropagation()}
className="anim-pop w-full max-w-lg max-h-[90vh] overflow-y-auto rounded-2xl border border-line bg-surface shadow-card p-6 sm:p-7"
>
<div className="flex items-start gap-3">
<div className="grid place-items-center w-10 h-10 rounded-full bg-pine text-gold font-display font-semibold text-xl seal-ring shrink-0">
ا
</div>
<div className="flex-1">
<h2 id="welcome-title" className="font-display text-2xl font-semibold text-heading leading-tight">
Amana — Campaign Trust &amp; Safety triage
</h2>
<p className="text-xs text-muted mt-0.5">A moderator's bench for fundraising review</p>
</div>
<button
onClick={onClose}
aria-label="Close"
className="text-faint hover:text-heading shrink-0"
>
<X size={18} />
</button>
</div>
<p className="text-sm text-body leading-relaxed mt-4">
An AI agent reviews each incoming fundraising campaign against policy and recommends{" "}
<b className="text-heading">APPROVE / REJECT / ESCALATE</b> — with cited rule IDs and surfaced
risk signals. <b className="text-heading">A human moderator makes the final decision.</b>
</p>
<div className="mt-5 space-y-3">
{STEPS.map((s) => (
<div key={s.n} className="flex gap-3">
<span className="grid place-items-center w-6 h-6 rounded-full bg-accent-soft text-accent-on text-xs font-semibold shrink-0 mt-0.5">
{s.n}
</span>
<div>
<div className="text-sm font-semibold text-heading">{s.title}</div>
<p className="text-sm text-muted leading-relaxed">{s.body}</p>
</div>
</div>
))}
</div>
<div className="mt-5">
<div className="text-xs font-semibold uppercase tracking-wide text-faint mb-2">Try these</div>
<div className="space-y-1.5">
{SHOWCASE.map((c) => (
<button
key={c.id}
onClick={() => onPick(c.id)}
className="w-full text-left flex items-baseline gap-2 rounded-lg border border-line hover:border-accent hover:bg-accent-soft/50 px-3 py-2 transition"
>
<span className="font-mono text-xs font-semibold text-accent shrink-0">{c.id}</span>
<span className="text-sm text-muted">{c.note}</span>
</button>
))}
</div>
</div>
<button
ref={startRef}
onClick={onClose}
className="mt-6 w-full flex items-center justify-center gap-2 bg-pine hover:bg-pine-deep text-white rounded-lg py-2.5 text-sm font-medium shadow-sm transition"
>
Start reviewing <ArrowRight size={16} />
</button>
</div>
</div>
);
}