Add DesignGuide page for component development reference
Showcases all UI primitives (Button, Badge, Input, Textarea, Card, Dialog, Tooltip, Tabs, Skeleton, etc.) with Paperclip's dark theme. Accessible at /design-guide; not linked in the main sidebar. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
973
ui/src/pages/DesignGuide.tsx
Normal file
973
ui/src/pages/DesignGuide.tsx
Normal file
@@ -0,0 +1,973 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Bot,
|
||||
CircleDot,
|
||||
DollarSign,
|
||||
Hexagon,
|
||||
History,
|
||||
Inbox,
|
||||
LayoutDashboard,
|
||||
ListTodo,
|
||||
Mail,
|
||||
Plus,
|
||||
Search,
|
||||
Settings,
|
||||
Target,
|
||||
Trash2,
|
||||
Upload,
|
||||
User,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipTrigger,
|
||||
TooltipContent,
|
||||
} from "@/components/ui/tooltip";
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarGroup,
|
||||
AvatarGroupCount,
|
||||
} from "@/components/ui/avatar";
|
||||
import { StatusBadge } from "@/components/StatusBadge";
|
||||
import { StatusIcon } from "@/components/StatusIcon";
|
||||
import { PriorityIcon } from "@/components/PriorityIcon";
|
||||
import { EntityRow } from "@/components/EntityRow";
|
||||
import { EmptyState } from "@/components/EmptyState";
|
||||
import { MetricCard } from "@/components/MetricCard";
|
||||
import { FilterBar, type FilterValue } from "@/components/FilterBar";
|
||||
import { InlineEditor } from "@/components/InlineEditor";
|
||||
import { PageSkeleton } from "@/components/PageSkeleton";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Section wrapper */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
||||
{title}
|
||||
</h3>
|
||||
<Separator />
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function SubSection({ title, children }: { title: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-medium">{title}</h4>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Color swatch */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
function Swatch({ name, cssVar }: { name: string; cssVar: string }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="h-8 w-8 rounded-md border border-border shrink-0"
|
||||
style={{ backgroundColor: `var(${cssVar})` }}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xs font-mono">{cssVar}</p>
|
||||
<p className="text-xs text-muted-foreground">{name}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Page */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export function DesignGuide() {
|
||||
const [status, setStatus] = useState("todo");
|
||||
const [priority, setPriority] = useState("medium");
|
||||
const [inlineText, setInlineText] = useState("Click to edit this text");
|
||||
const [filters, setFilters] = useState<FilterValue[]>([
|
||||
{ key: "status", label: "Status", value: "Active" },
|
||||
{ key: "priority", label: "Priority", value: "High" },
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="space-y-10 max-w-4xl">
|
||||
{/* Page header */}
|
||||
<div>
|
||||
<h2 className="text-xl font-bold">Design Guide</h2>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Every component, style, and pattern used across Paperclip.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* COLORS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Colors">
|
||||
<SubSection title="Core">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<Swatch name="Background" cssVar="--background" />
|
||||
<Swatch name="Foreground" cssVar="--foreground" />
|
||||
<Swatch name="Card" cssVar="--card" />
|
||||
<Swatch name="Primary" cssVar="--primary" />
|
||||
<Swatch name="Primary foreground" cssVar="--primary-foreground" />
|
||||
<Swatch name="Secondary" cssVar="--secondary" />
|
||||
<Swatch name="Muted" cssVar="--muted" />
|
||||
<Swatch name="Muted foreground" cssVar="--muted-foreground" />
|
||||
<Swatch name="Accent" cssVar="--accent" />
|
||||
<Swatch name="Destructive" cssVar="--destructive" />
|
||||
<Swatch name="Border" cssVar="--border" />
|
||||
<Swatch name="Ring" cssVar="--ring" />
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Sidebar">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<Swatch name="Sidebar" cssVar="--sidebar" />
|
||||
<Swatch name="Sidebar border" cssVar="--sidebar-border" />
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Chart">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<Swatch name="Chart 1" cssVar="--chart-1" />
|
||||
<Swatch name="Chart 2" cssVar="--chart-2" />
|
||||
<Swatch name="Chart 3" cssVar="--chart-3" />
|
||||
<Swatch name="Chart 4" cssVar="--chart-4" />
|
||||
<Swatch name="Chart 5" cssVar="--chart-5" />
|
||||
</div>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* TYPOGRAPHY */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Typography">
|
||||
<div className="space-y-3">
|
||||
<h2 className="text-xl font-bold">Page Title — text-xl font-bold</h2>
|
||||
<h2 className="text-lg font-semibold">Section Title — text-lg font-semibold</h2>
|
||||
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
||||
Section Heading — text-sm font-semibold uppercase tracking-wide
|
||||
</h3>
|
||||
<p className="text-sm font-medium">Card Title — text-sm font-medium</p>
|
||||
<p className="text-sm font-semibold">Card Title Alt — text-sm font-semibold</p>
|
||||
<p className="text-sm">Body text — text-sm</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Muted description — text-sm text-muted-foreground
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Tiny label — text-xs text-muted-foreground
|
||||
</p>
|
||||
<p className="text-xs font-mono text-muted-foreground">
|
||||
Mono identifier — text-xs font-mono text-muted-foreground
|
||||
</p>
|
||||
<p className="text-2xl font-bold">Large stat — text-2xl font-bold</p>
|
||||
<p className="font-mono text-xs">Log/code text — font-mono text-xs</p>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* SPACING & RADIUS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Radius">
|
||||
<div className="flex items-end gap-4 flex-wrap">
|
||||
{[
|
||||
["sm", "var(--radius-sm)"],
|
||||
["md", "var(--radius-md)"],
|
||||
["lg", "var(--radius-lg)"],
|
||||
["xl", "var(--radius-xl)"],
|
||||
["full", "9999px"],
|
||||
].map(([label, radius]) => (
|
||||
<div key={label} className="flex flex-col items-center gap-1">
|
||||
<div
|
||||
className="h-12 w-12 bg-primary"
|
||||
style={{ borderRadius: radius }}
|
||||
/>
|
||||
<span className="text-xs text-muted-foreground">{label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* BUTTONS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Buttons">
|
||||
<SubSection title="Variants">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Button variant="default">Default</Button>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="outline">Outline</Button>
|
||||
<Button variant="ghost">Ghost</Button>
|
||||
<Button variant="destructive">Destructive</Button>
|
||||
<Button variant="link">Link</Button>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Sizes">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Button size="xs">Extra Small</Button>
|
||||
<Button size="sm">Small</Button>
|
||||
<Button size="default">Default</Button>
|
||||
<Button size="lg">Large</Button>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Icon buttons">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Button variant="ghost" size="icon-xs"><Search /></Button>
|
||||
<Button variant="ghost" size="icon-sm"><Search /></Button>
|
||||
<Button variant="outline" size="icon"><Search /></Button>
|
||||
<Button variant="outline" size="icon-lg"><Search /></Button>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="With icons">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Button><Plus /> New Issue</Button>
|
||||
<Button variant="outline"><Upload /> Upload</Button>
|
||||
<Button variant="destructive"><Trash2 /> Delete</Button>
|
||||
<Button size="sm"><Plus /> Add</Button>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="States">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Button disabled>Disabled</Button>
|
||||
<Button variant="outline" disabled>Disabled Outline</Button>
|
||||
</div>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* BADGES */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Badges">
|
||||
<SubSection title="Variants">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Badge variant="default">Default</Badge>
|
||||
<Badge variant="secondary">Secondary</Badge>
|
||||
<Badge variant="outline">Outline</Badge>
|
||||
<Badge variant="destructive">Destructive</Badge>
|
||||
<Badge variant="ghost">Ghost</Badge>
|
||||
</div>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* STATUS BADGES & ICONS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Status System">
|
||||
<SubSection title="StatusBadge (all statuses)">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{[
|
||||
"active", "running", "paused", "idle", "archived", "planned",
|
||||
"achieved", "completed", "failed", "succeeded", "error", "backlog",
|
||||
"todo", "in_progress", "in_review", "blocked", "done",
|
||||
"cancelled", "pending", "approved", "rejected",
|
||||
].map((s) => (
|
||||
<StatusBadge key={s} status={s} />
|
||||
))}
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="StatusIcon (interactive)">
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
{["backlog", "todo", "in_progress", "in_review", "done", "cancelled", "blocked"].map(
|
||||
(s) => (
|
||||
<div key={s} className="flex items-center gap-1.5">
|
||||
<StatusIcon status={s} />
|
||||
<span className="text-xs text-muted-foreground">{s}</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<StatusIcon status={status} onChange={setStatus} />
|
||||
<span className="text-sm">Click the icon to change status (current: {status})</span>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="PriorityIcon (interactive)">
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
{["critical", "high", "medium", "low"].map((p) => (
|
||||
<div key={p} className="flex items-center gap-1.5">
|
||||
<PriorityIcon priority={p} />
|
||||
<span className="text-xs text-muted-foreground">{p}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<PriorityIcon priority={priority} onChange={setPriority} />
|
||||
<span className="text-sm">Click the icon to change (current: {priority})</span>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Agent status dots">
|
||||
<div className="flex items-center gap-4 flex-wrap">
|
||||
{[
|
||||
["running", "bg-cyan-400 animate-pulse"],
|
||||
["active", "bg-green-400"],
|
||||
["paused", "bg-yellow-400"],
|
||||
["error", "bg-red-400"],
|
||||
["offline", "bg-neutral-400"],
|
||||
].map(([label, color]) => (
|
||||
<div key={label} className="flex items-center gap-2">
|
||||
<span className="relative flex h-2.5 w-2.5">
|
||||
<span className={`inline-flex h-full w-full rounded-full ${color}`} />
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">{label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Run invocation badges">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{[
|
||||
["timer", "bg-blue-900/50 text-blue-300"],
|
||||
["assignment", "bg-violet-900/50 text-violet-300"],
|
||||
["on_demand", "bg-cyan-900/50 text-cyan-300"],
|
||||
["webhook", "bg-neutral-800 text-neutral-400"],
|
||||
].map(([label, cls]) => (
|
||||
<span key={label} className={`rounded-full px-1.5 py-0.5 text-[10px] font-medium ${cls}`}>
|
||||
{label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* FORM ELEMENTS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Form Elements">
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<SubSection title="Input">
|
||||
<Input placeholder="Default input" />
|
||||
<Input placeholder="Disabled input" disabled className="mt-2" />
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Textarea">
|
||||
<Textarea placeholder="Write something..." />
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Checkbox & Label">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="check1" defaultChecked />
|
||||
<Label htmlFor="check1">Checked item</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="check2" />
|
||||
<Label htmlFor="check2">Unchecked item</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox id="check3" disabled />
|
||||
<Label htmlFor="check3">Disabled item</Label>
|
||||
</div>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Inline Editor">
|
||||
<InlineEditor
|
||||
value={inlineText}
|
||||
onSave={setInlineText}
|
||||
as="p"
|
||||
className="text-sm"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Click the text above to edit inline.
|
||||
</p>
|
||||
</SubSection>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* CARDS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Cards">
|
||||
<SubSection title="Standard Card">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Card Title</CardTitle>
|
||||
<CardDescription>Card description with supporting text.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm">Card content goes here. This is the main body area.</p>
|
||||
</CardContent>
|
||||
<CardFooter className="gap-2">
|
||||
<Button size="sm">Action</Button>
|
||||
<Button variant="outline" size="sm">Cancel</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Metric Cards">
|
||||
<div className="grid md:grid-cols-2 xl:grid-cols-4 gap-4">
|
||||
<MetricCard icon={Bot} value={12} label="Active Agents" description="+3 this week" />
|
||||
<MetricCard icon={CircleDot} value={48} label="Open Issues" />
|
||||
<MetricCard icon={DollarSign} value="$1,234" label="Monthly Cost" description="Under budget" />
|
||||
<MetricCard icon={Zap} value="99.9%" label="Uptime" />
|
||||
</div>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* TABS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Tabs">
|
||||
<SubSection title="Default (pill) variant">
|
||||
<Tabs defaultValue="overview">
|
||||
<TabsList>
|
||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||
<TabsTrigger value="runs">Runs</TabsTrigger>
|
||||
<TabsTrigger value="config">Config</TabsTrigger>
|
||||
<TabsTrigger value="costs">Costs</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="overview">
|
||||
<p className="text-sm text-muted-foreground py-4">Overview tab content.</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="runs">
|
||||
<p className="text-sm text-muted-foreground py-4">Runs tab content.</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="config">
|
||||
<p className="text-sm text-muted-foreground py-4">Config tab content.</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="costs">
|
||||
<p className="text-sm text-muted-foreground py-4">Costs tab content.</p>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Line variant">
|
||||
<Tabs defaultValue="summary">
|
||||
<TabsList variant="line">
|
||||
<TabsTrigger value="summary">Summary</TabsTrigger>
|
||||
<TabsTrigger value="details">Details</TabsTrigger>
|
||||
<TabsTrigger value="comments">Comments</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="summary">
|
||||
<p className="text-sm text-muted-foreground py-4">Summary content with underline tabs.</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="details">
|
||||
<p className="text-sm text-muted-foreground py-4">Details content.</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="comments">
|
||||
<p className="text-sm text-muted-foreground py-4">Comments content.</p>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* ENTITY ROWS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Entity Rows">
|
||||
<div className="border border-border rounded-md">
|
||||
<EntityRow
|
||||
leading={
|
||||
<>
|
||||
<StatusIcon status="in_progress" />
|
||||
<PriorityIcon priority="high" />
|
||||
</>
|
||||
}
|
||||
identifier="PAP-001"
|
||||
title="Implement authentication flow"
|
||||
subtitle="Assigned to Agent Alpha"
|
||||
trailing={<StatusBadge status="in_progress" />}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<EntityRow
|
||||
leading={
|
||||
<>
|
||||
<StatusIcon status="done" />
|
||||
<PriorityIcon priority="medium" />
|
||||
</>
|
||||
}
|
||||
identifier="PAP-002"
|
||||
title="Set up CI/CD pipeline"
|
||||
subtitle="Completed 2 days ago"
|
||||
trailing={<StatusBadge status="done" />}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<EntityRow
|
||||
leading={
|
||||
<>
|
||||
<StatusIcon status="todo" />
|
||||
<PriorityIcon priority="low" />
|
||||
</>
|
||||
}
|
||||
identifier="PAP-003"
|
||||
title="Write API documentation"
|
||||
trailing={<StatusBadge status="todo" />}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<EntityRow
|
||||
leading={
|
||||
<>
|
||||
<StatusIcon status="blocked" />
|
||||
<PriorityIcon priority="critical" />
|
||||
</>
|
||||
}
|
||||
identifier="PAP-004"
|
||||
title="Deploy to production"
|
||||
subtitle="Blocked by PAP-001"
|
||||
trailing={<StatusBadge status="blocked" />}
|
||||
selected
|
||||
/>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* FILTER BAR */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Filter Bar">
|
||||
<FilterBar
|
||||
filters={filters}
|
||||
onRemove={(key) => setFilters((f) => f.filter((x) => x.key !== key))}
|
||||
onClear={() => setFilters([])}
|
||||
/>
|
||||
{filters.length === 0 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
setFilters([
|
||||
{ key: "status", label: "Status", value: "Active" },
|
||||
{ key: "priority", label: "Priority", value: "High" },
|
||||
])
|
||||
}
|
||||
>
|
||||
Reset filters
|
||||
</Button>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* AVATARS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Avatars">
|
||||
<SubSection title="Sizes">
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar size="sm"><AvatarFallback>SM</AvatarFallback></Avatar>
|
||||
<Avatar><AvatarFallback>DF</AvatarFallback></Avatar>
|
||||
<Avatar size="lg"><AvatarFallback>LG</AvatarFallback></Avatar>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Group">
|
||||
<AvatarGroup>
|
||||
<Avatar><AvatarFallback>A1</AvatarFallback></Avatar>
|
||||
<Avatar><AvatarFallback>A2</AvatarFallback></Avatar>
|
||||
<Avatar><AvatarFallback>A3</AvatarFallback></Avatar>
|
||||
<AvatarGroupCount>+5</AvatarGroupCount>
|
||||
</AvatarGroup>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* TOOLTIPS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Tooltips">
|
||||
<div className="flex items-center gap-4">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="sm">Hover me</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>This is a tooltip</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon-sm"><Settings /></Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Settings</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* DIALOG */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Dialog">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline">Open Dialog</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Dialog Title</DialogTitle>
|
||||
<DialogDescription>
|
||||
This is a sample dialog showing the standard layout with header, content, and footer.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Label>Name</Label>
|
||||
<Input placeholder="Enter a name" className="mt-1.5" />
|
||||
</div>
|
||||
<div>
|
||||
<Label>Description</Label>
|
||||
<Textarea placeholder="Describe..." className="mt-1.5" />
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
<Button>Save</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* EMPTY STATE */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Empty State">
|
||||
<div className="border border-border rounded-md">
|
||||
<EmptyState
|
||||
icon={Inbox}
|
||||
message="No items to show. Create your first one to get started."
|
||||
action="Create Item"
|
||||
onAction={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* PROGRESS BARS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Progress Bars (Budget)">
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ label: "Under budget (40%)", pct: 40, color: "bg-green-400" },
|
||||
{ label: "Warning (75%)", pct: 75, color: "bg-yellow-400" },
|
||||
{ label: "Over budget (95%)", pct: 95, color: "bg-red-400" },
|
||||
].map(({ label, pct, color }) => (
|
||||
<div key={label} className="space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-muted-foreground">{label}</span>
|
||||
<span className="text-xs font-mono">{pct}%</span>
|
||||
</div>
|
||||
<div className="w-full h-2 bg-muted rounded-full overflow-hidden">
|
||||
<div
|
||||
className={`h-full rounded-full transition-all ${color}`}
|
||||
style={{ width: `${pct}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* LOG VIEWER */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Log Viewer">
|
||||
<div className="bg-neutral-950 rounded-lg p-3 font-mono text-xs max-h-80 overflow-y-auto">
|
||||
<div className="text-foreground">[12:00:01] INFO Agent started successfully</div>
|
||||
<div className="text-foreground">[12:00:02] INFO Processing task PAP-001</div>
|
||||
<div className="text-yellow-400">[12:00:05] WARN Rate limit approaching (80%)</div>
|
||||
<div className="text-foreground">[12:00:08] INFO Task PAP-001 completed</div>
|
||||
<div className="text-red-400">[12:00:12] ERROR Connection timeout to upstream service</div>
|
||||
<div className="text-blue-300">[12:00:12] SYS Retrying connection in 5s...</div>
|
||||
<div className="text-foreground">[12:00:17] INFO Reconnected successfully</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="relative flex h-1.5 w-1.5">
|
||||
<span className="absolute inline-flex h-full w-full rounded-full bg-cyan-400 animate-ping" />
|
||||
<span className="inline-flex h-full w-full rounded-full bg-cyan-400" />
|
||||
</span>
|
||||
<span className="text-cyan-400">Live</span>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* PROPERTY ROW PATTERN */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Property Row Pattern">
|
||||
<div className="border border-border rounded-md p-4 space-y-1 max-w-sm">
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">Status</span>
|
||||
<StatusBadge status="active" />
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">Priority</span>
|
||||
<PriorityIcon priority="high" />
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">Assignee</span>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Avatar size="sm"><AvatarFallback>A</AvatarFallback></Avatar>
|
||||
<span className="text-xs">Agent Alpha</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-xs text-muted-foreground">Created</span>
|
||||
<span className="text-xs">Jan 15, 2025</span>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* NAVIGATION PATTERNS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Navigation Patterns">
|
||||
<SubSection title="Sidebar nav items">
|
||||
<div className="w-60 border border-border rounded-md p-3 space-y-0.5 bg-card">
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium bg-accent text-accent-foreground">
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
Dashboard
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 hover:text-accent-foreground cursor-pointer">
|
||||
<CircleDot className="h-4 w-4" />
|
||||
Issues
|
||||
<span className="ml-auto text-xs bg-primary text-primary-foreground rounded-full px-1.5 py-0.5">
|
||||
12
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 hover:text-accent-foreground cursor-pointer">
|
||||
<Bot className="h-4 w-4" />
|
||||
Agents
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 hover:text-accent-foreground cursor-pointer">
|
||||
<Hexagon className="h-4 w-4" />
|
||||
Projects
|
||||
</div>
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="View toggle">
|
||||
<div className="flex items-center border border-border rounded-md w-fit">
|
||||
<button className="px-3 py-1.5 text-xs font-medium bg-accent text-foreground rounded-l-md">
|
||||
<ListTodo className="h-3.5 w-3.5 inline mr-1" />
|
||||
List
|
||||
</button>
|
||||
<button className="px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 rounded-r-md">
|
||||
<Target className="h-3.5 w-3.5 inline mr-1" />
|
||||
Org
|
||||
</button>
|
||||
</div>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* GROUPED LIST (Issues pattern) */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Grouped List (Issues pattern)">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 px-4 py-2 bg-muted/50 rounded-t-md">
|
||||
<StatusIcon status="in_progress" />
|
||||
<span className="text-sm font-medium">In Progress</span>
|
||||
<span className="text-xs text-muted-foreground ml-1">2</span>
|
||||
</div>
|
||||
<div className="border border-border rounded-b-md">
|
||||
<EntityRow
|
||||
leading={<PriorityIcon priority="high" />}
|
||||
identifier="PAP-101"
|
||||
title="Build agent heartbeat system"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<EntityRow
|
||||
leading={<PriorityIcon priority="medium" />}
|
||||
identifier="PAP-102"
|
||||
title="Add cost tracking dashboard"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* COMMENT THREAD PATTERN */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Comment Thread Pattern">
|
||||
<div className="space-y-3 max-w-2xl">
|
||||
<h3 className="text-sm font-semibold">Comments (2)</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="rounded-md border border-border p-3">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-xs font-medium text-muted-foreground">Agent</span>
|
||||
<span className="text-xs text-muted-foreground">Jan 15, 2025</span>
|
||||
</div>
|
||||
<p className="text-sm">Started working on the authentication module. Will need API keys configured.</p>
|
||||
</div>
|
||||
<div className="rounded-md border border-border p-3">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-xs font-medium text-muted-foreground">Human</span>
|
||||
<span className="text-xs text-muted-foreground">Jan 16, 2025</span>
|
||||
</div>
|
||||
<p className="text-sm">API keys have been added to the vault. Please proceed.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Textarea placeholder="Leave a comment..." rows={3} />
|
||||
<Button size="sm">Comment</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* COST TABLE PATTERN */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Cost Table Pattern">
|
||||
<div className="border border-border rounded-lg overflow-hidden">
|
||||
<table className="w-full text-xs">
|
||||
<thead className="border-b border-border bg-accent/20">
|
||||
<tr>
|
||||
<th className="text-left px-3 py-2 font-medium text-muted-foreground">Model</th>
|
||||
<th className="text-left px-3 py-2 font-medium text-muted-foreground">Tokens</th>
|
||||
<th className="text-left px-3 py-2 font-medium text-muted-foreground">Cost</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-b border-border">
|
||||
<td className="px-3 py-2">claude-sonnet-4-20250514</td>
|
||||
<td className="px-3 py-2 font-mono">1.2M</td>
|
||||
<td className="px-3 py-2 font-mono">$18.00</td>
|
||||
</tr>
|
||||
<tr className="border-b border-border">
|
||||
<td className="px-3 py-2">claude-haiku-4-20250506</td>
|
||||
<td className="px-3 py-2 font-mono">500k</td>
|
||||
<td className="px-3 py-2 font-mono">$1.25</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-3 py-2 font-medium">Total</td>
|
||||
<td className="px-3 py-2 font-mono">1.7M</td>
|
||||
<td className="px-3 py-2 font-mono font-medium">$19.25</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* SKELETONS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Skeletons">
|
||||
<SubSection title="Individual">
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-4 w-48" />
|
||||
<Skeleton className="h-8 w-full max-w-sm" />
|
||||
<Skeleton className="h-20 w-full" />
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Page Skeleton (list)">
|
||||
<div className="border border-border rounded-md p-4">
|
||||
<PageSkeleton variant="list" />
|
||||
</div>
|
||||
</SubSection>
|
||||
|
||||
<SubSection title="Page Skeleton (detail)">
|
||||
<div className="border border-border rounded-md p-4">
|
||||
<PageSkeleton variant="detail" />
|
||||
</div>
|
||||
</SubSection>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* SEPARATOR */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Separator">
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-muted-foreground">Horizontal</p>
|
||||
<Separator />
|
||||
<div className="flex items-center gap-4 h-8">
|
||||
<span className="text-sm">Left</span>
|
||||
<Separator orientation="vertical" />
|
||||
<span className="text-sm">Right</span>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* ICON REFERENCE */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Common Icons (Lucide)">
|
||||
<div className="grid grid-cols-4 md:grid-cols-6 gap-4">
|
||||
{[
|
||||
["Inbox", Inbox],
|
||||
["ListTodo", ListTodo],
|
||||
["CircleDot", CircleDot],
|
||||
["Hexagon", Hexagon],
|
||||
["Target", Target],
|
||||
["LayoutDashboard", LayoutDashboard],
|
||||
["Bot", Bot],
|
||||
["DollarSign", DollarSign],
|
||||
["History", History],
|
||||
["Search", Search],
|
||||
["Plus", Plus],
|
||||
["Trash2", Trash2],
|
||||
["Settings", Settings],
|
||||
["User", User],
|
||||
["Mail", Mail],
|
||||
["Upload", Upload],
|
||||
["Zap", Zap],
|
||||
].map(([name, Icon]) => {
|
||||
const LucideIcon = Icon as React.FC<{ className?: string }>;
|
||||
return (
|
||||
<div key={name as string} className="flex flex-col items-center gap-1.5 p-2">
|
||||
<LucideIcon className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-[10px] text-muted-foreground font-mono">{name as string}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* ============================================================ */}
|
||||
{/* KEYBOARD SHORTCUTS */}
|
||||
{/* ============================================================ */}
|
||||
<Section title="Keyboard Shortcuts">
|
||||
<div className="border border-border rounded-md divide-y divide-border text-sm">
|
||||
{[
|
||||
["Cmd+K / Ctrl+K", "Open Command Palette"],
|
||||
["C", "New Issue (outside inputs)"],
|
||||
["[", "Toggle Sidebar"],
|
||||
["]", "Toggle Properties Panel"],
|
||||
["Cmd+Enter", "Submit current dialog"],
|
||||
].map(([key, desc]) => (
|
||||
<div key={key} className="flex items-center justify-between px-4 py-2">
|
||||
<span className="text-muted-foreground">{desc}</span>
|
||||
<kbd className="px-2 py-0.5 text-xs font-mono bg-muted rounded border border-border">
|
||||
{key}
|
||||
</kbd>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user