Skip to content

Commit fd8027a

Browse files
duyetduyetbot
andauthored
feat(dashboard): redesign app surfaces
Redesign dashboard shell, shared primitives, analytics, projects, conversations, domains, integrations, and organization settings surfaces. Co-Authored-By: Duyet Le <me@duyet.net> Co-Authored-By: duyetbot <bot@duyet.net>
1 parent c7e8c74 commit fd8027a

81 files changed

Lines changed: 716 additions & 620 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/dashboard/src/app/dashboard/_create-project-form-actions.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ export function CreateProjectFormActions({
1313
}: CreateProjectFormActionsProps) {
1414
return (
1515
<>
16-
<Button size="sm" variant="ghost" className="text-xs h-8 px-4" onClick={onCancel}>
16+
<Button size="sm" variant="ghost" onClick={onCancel}>
1717
Cancel
1818
</Button>
19-
<Button size="sm" className="text-xs h-8 px-4" onClick={onCreate} disabled={disabled}>
19+
<Button size="sm" onClick={onCreate} disabled={disabled}>
2020
Create project
2121
</Button>
2222
</>

packages/dashboard/src/app/dashboard/_create-project-form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function CreateProjectForm({
5151
<Card className="mb-6">
5252
<CreateProjectFormHeader />
5353
<CardContent>
54-
<div className="space-y-4">
54+
<div className="flex flex-col gap-4">
5555
<ProjectNameInput value={name} onChange={onNameChange} onSubmit={onCreate} />
5656
<ProjectSlugInput value={slug} status={slugStatus} onChange={onSlugChange} />
5757
</div>

packages/dashboard/src/app/dashboard/_dashboard-header.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export function DashboardHeader({ onCreateClick }: DashboardHeaderProps) {
2424
title="Projects"
2525
description="Manage your API projects and keys."
2626
actions={
27-
<Button size="sm" className="text-xs h-8" onClick={onCreateClick}>
28-
<PlusIcon className="h-3.5 w-3.5 mr-1.5" />
27+
<Button size="sm" onClick={onCreateClick}>
28+
<PlusIcon data-icon="inline-start" aria-hidden="true" />
2929
New Project
3030
</Button>
3131
}

packages/dashboard/src/app/dashboard/_project-name-input.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Input } from "@/components/ui/input";
2+
import { Label } from "@/components/ui/label";
23

34
interface ProjectNameInputProps {
45
value: string;
@@ -8,17 +9,14 @@ interface ProjectNameInputProps {
89

910
export function ProjectNameInput({ value, onChange, onSubmit }: ProjectNameInputProps) {
1011
return (
11-
<div>
12-
<label htmlFor="project-name" className="text-xs font-medium text-foreground mb-1.5 block">
13-
Project name
14-
</label>
12+
<div className="flex flex-col gap-1.5">
13+
<Label htmlFor="project-name">Project name</Label>
1514
<Input
1615
id="project-name"
1716
placeholder="My Chatbot"
1817
value={value}
1918
onChange={(e) => onChange(e.target.value)}
2019
onKeyDown={(e) => e.key === "Enter" && onSubmit()}
21-
className="text-sm h-9"
2220
autoFocus
2321
/>
2422
</div>

packages/dashboard/src/app/dashboard/_project-slug-input.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { CheckIcon, LoaderIcon, XIcon } from "lucide-react";
22
import { Input } from "@/components/ui/input";
3+
import { Label } from "@/components/ui/label";
4+
import { cn } from "@/lib/utils";
35

46
type SlugStatus = "idle" | "checking" | "available" | "taken";
57

@@ -11,39 +13,36 @@ interface ProjectSlugInputProps {
1113

1214
export function ProjectSlugInput({ value, status, onChange }: ProjectSlugInputProps) {
1315
return (
14-
<div>
15-
<label htmlFor="project-slug" className="text-xs font-medium text-foreground mb-1.5 block">
16-
Project slug
17-
</label>
16+
<div className="flex flex-col gap-1.5">
17+
<Label htmlFor="project-slug">Project slug</Label>
1818
<div className="relative">
1919
<Input
2020
id="project-slug"
2121
placeholder="my-chatbot"
2222
value={value}
2323
onChange={(e) => onChange(e.target.value)}
24-
className={`text-sm h-9 font-mono pr-8 ${
25-
status === "taken" ? "border-red-500 focus-visible:ring-red-500" : ""
26-
}`}
24+
className={cn("font-mono pr-8", status === "taken" && "border-destructive")}
25+
aria-invalid={status === "taken"}
2726
aria-describedby="slug-status"
2827
/>
2928
{value && (
3029
<div className="absolute right-2.5 top-1/2 -translate-y-1/2" aria-hidden="true">
3130
{status === "checking" && (
32-
<LoaderIcon className="h-3.5 w-3.5 text-muted-foreground animate-spin" />
31+
<LoaderIcon className="size-4 animate-spin text-muted-foreground" />
3332
)}
34-
{status === "available" && <CheckIcon className="h-3.5 w-3.5 text-green-500" />}
35-
{status === "taken" && <XIcon className="h-3.5 w-3.5 text-red-500" />}
33+
{status === "available" && <CheckIcon className="size-4 text-muted-foreground" />}
34+
{status === "taken" && <XIcon className="size-4 text-destructive" />}
3635
</div>
3736
)}
3837
</div>
3938
<div id="slug-status" className="min-h-[20px]">
4039
{status === "taken" && (
41-
<p className="text-xs text-red-500 mt-1.5" role="alert">
40+
<p className="text-xs text-destructive" role="alert">
4241
This slug is already taken. Choose a different one.
4342
</p>
4443
)}
4544
{status === "available" && value && (
46-
<p className="text-xs text-muted-foreground mt-1.5">Available</p>
45+
<p className="text-xs text-muted-foreground">Available</p>
4746
)}
4847
</div>
4948
</div>

packages/dashboard/src/app/dashboard/_projects-empty-state.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export function ProjectsEmptyState({ onCreateClick }: ProjectsEmptyStateProps) {
2020
return (
2121
<Card className="border-dashed">
2222
<EmptyState
23-
icon={<FolderIcon className="h-5 w-5 text-muted-foreground" />}
23+
icon={<FolderIcon aria-hidden="true" />}
2424
title="No projects yet"
2525
description="Projects group your conversations and API keys."
2626
action={{

packages/dashboard/src/app/dashboard/_projects-table.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import type { ProjectListItem } from "@agentstate/shared";
44
import { FolderIcon, KeyIcon } from "lucide-react";
55
import { useRouter } from "next/navigation";
6+
import { Card } from "@/components/ui/card";
67
import {
78
Table,
89
TableBody,
@@ -36,10 +37,10 @@ export function ProjectsTable({ projects }: ProjectsTableProps) {
3637
const router = useRouter();
3738

3839
return (
39-
<div className="rounded-md border">
40+
<Card className="overflow-hidden py-0">
4041
<Table>
4142
<TableHeader>
42-
<TableRow>
43+
<TableRow className="bg-muted/35 hover:bg-muted/35">
4344
<TableHead>Name</TableHead>
4445
<TableHead className="hidden sm:table-cell">API Keys</TableHead>
4546
<TableHead className="hidden sm:table-cell">Created</TableHead>
@@ -62,7 +63,9 @@ export function ProjectsTable({ projects }: ProjectsTableProps) {
6263
>
6364
<TableCell>
6465
<div className="flex items-center gap-2.5">
65-
<FolderIcon className="h-4 w-4 text-muted-foreground shrink-0" />
66+
<span className="flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
67+
<FolderIcon aria-hidden="true" />
68+
</span>
6669
<div>
6770
<p className="text-sm font-medium text-foreground">{project.name}</p>
6871
<p className="text-xs text-muted-foreground font-mono">{project.slug}</p>
@@ -71,7 +74,7 @@ export function ProjectsTable({ projects }: ProjectsTableProps) {
7174
</TableCell>
7275
<TableCell className="hidden sm:table-cell">
7376
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
74-
<KeyIcon className="h-3 w-3" />
77+
<KeyIcon aria-hidden="true" />
7578
{project.key_count ?? 0}
7679
</div>
7780
</TableCell>
@@ -82,6 +85,6 @@ export function ProjectsTable({ projects }: ProjectsTableProps) {
8285
))}
8386
</TableBody>
8487
</Table>
85-
</div>
88+
</Card>
8689
);
8790
}

packages/dashboard/src/app/dashboard/analytics/_analytics-content.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface AnalyticsContentProps {
1919

2020
export function AnalyticsContent({ data }: AnalyticsContentProps) {
2121
return (
22-
<div className="space-y-6 mt-2">
22+
<div className="mt-2 flex flex-col gap-6">
2323
<SummaryCards
2424
totalConversations={data.summary.total_conversations}
2525
totalMessages={data.summary.total_messages}
@@ -75,12 +75,12 @@ export function AnalyticsContent({ data }: AnalyticsContentProps) {
7575
</div>
7676

7777
<div className="grid gap-4 lg:grid-cols-2">
78-
<div>
79-
<h3 className="text-sm font-medium text-foreground mb-3">Recent activity</h3>
78+
<div className="flex flex-col gap-3">
79+
<h3 className="text-sm font-medium text-foreground">Recent activity</h3>
8080
<RecentActivity conversations={data.recent_conversations} />
8181
</div>
82-
<div>
83-
<h3 className="text-sm font-medium text-foreground mb-3">Top conversations</h3>
82+
<div className="flex flex-col gap-3">
83+
<h3 className="text-sm font-medium text-foreground">Top conversations</h3>
8484
<TopConversations conversations={data.recent_conversations} />
8585
</div>
8686
</div>

packages/dashboard/src/app/dashboard/analytics/_analytics-header.tsx

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import type { ProjectResponse } from "@agentstate/shared";
44
import { type TimeRange, TimeRangeSelect } from "@/components/analytics/time-range-select";
5+
import { PageHeader } from "@/components/dashboard/page-header";
56
import {
67
Select,
78
SelectContent,
@@ -26,30 +27,35 @@ export function AnalyticsHeader({
2627
onRangeChange,
2728
}: AnalyticsHeaderProps) {
2829
return (
29-
<div className="flex items-start justify-between gap-4 flex-wrap">
30-
<div>
31-
<h1 className="text-lg font-semibold tracking-tight text-foreground">Analytics</h1>
32-
<p className="text-sm text-muted-foreground mt-1">
33-
Usage metrics and activity for your project.
34-
</p>
35-
</div>
36-
<div className="flex items-center gap-3">
37-
{projects.length > 1 && (
38-
<Select value={selectedProjectId} onValueChange={(v) => onProjectChange(v ?? "")}>
39-
<SelectTrigger className="h-8 w-[180px] text-xs" aria-label="Select project">
40-
<SelectValue placeholder="Select project" />
41-
</SelectTrigger>
42-
<SelectContent>
43-
{projects.map((p) => (
44-
<SelectItem key={p.id} value={p.id}>
45-
{p.name}
46-
</SelectItem>
47-
))}
48-
</SelectContent>
49-
</Select>
50-
)}
51-
<TimeRangeSelect value={range} onChange={onRangeChange} />
52-
</div>
53-
</div>
30+
<PageHeader
31+
title="Analytics"
32+
description="Usage metrics and activity for your project."
33+
actions={
34+
<>
35+
{projects.length > 1 && (
36+
<Select
37+
value={selectedProjectId}
38+
onValueChange={(value) => {
39+
if (value) {
40+
onProjectChange(value);
41+
}
42+
}}
43+
>
44+
<SelectTrigger className="w-[180px]" aria-label="Select project">
45+
<SelectValue placeholder="Select project" />
46+
</SelectTrigger>
47+
<SelectContent>
48+
{projects.map((p) => (
49+
<SelectItem key={p.id} value={p.id}>
50+
{p.name}
51+
</SelectItem>
52+
))}
53+
</SelectContent>
54+
</Select>
55+
)}
56+
<TimeRangeSelect value={range} onChange={onRangeChange} />
57+
</>
58+
}
59+
/>
5460
);
5561
}

packages/dashboard/src/app/dashboard/analytics/_analytics-loading.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ interface AnalyticsLoadingProps {
1010
export function AnalyticsLoading({ hasData }: AnalyticsLoadingProps) {
1111
if (hasData) {
1212
return (
13-
<output className="flex items-center gap-2 text-xs text-muted-foreground">
14-
<Loader2 className="h-3.5 w-3.5 animate-spin" aria-hidden="true" />
13+
<div className="flex items-center gap-2 text-xs text-muted-foreground">
14+
<Loader2 className="animate-spin" aria-hidden="true" />
1515
<span>Refreshing...</span>
16-
</output>
16+
</div>
1717
);
1818
}
1919

2020
return (
21-
<div className="space-y-4 mt-6">
21+
<div className="mt-6 flex flex-col gap-4">
2222
<StatsCardsSkeleton count={4} />
2323
<div className="grid gap-4 lg:grid-cols-2">
2424
<ChartCardSkeleton height="h-64" />

0 commit comments

Comments
 (0)