Skip to content

Commit dd177da

Browse files
committed
design: tighten UX states and mobile detail flows
1 parent 5189c58 commit dd177da

32 files changed

+637
-291
lines changed

app/globals.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,17 @@ h3 {
473473
background: var(--bg-elevated);
474474
}
475475

476+
@media (max-width: 767px) {
477+
.data-table th {
478+
padding: 10px 12px;
479+
font-size: 0.625rem;
480+
}
481+
482+
.data-table td {
483+
padding: 12px;
484+
}
485+
}
486+
476487
/* ========================================
477488
PROGRESS BAR - Market prices
478489
======================================== */

components/navigation/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export const PUBLIC_NAV_LINKS = [
33
{ href: '/cohorts', label: 'Cohorts' },
44
{ href: '/markets', label: 'Markets' },
55
{ href: '/methodology', label: 'Methodology' },
6+
{ href: '/changelog', label: 'Changelog' },
67
{ href: '/about', label: 'About' },
78
] as const;

features/admin/benchmark/AdminBenchmarkPageClient.tsx

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ import { AdminBenchmarkConfigsTable } from '@/features/admin/benchmark/component
55
import { AdminBenchmarkCurrentLineup } from '@/features/admin/benchmark/components/AdminBenchmarkCurrentLineup';
66
import { AdminBenchmarkReleaseForm } from '@/features/admin/benchmark/components/AdminBenchmarkReleaseForm';
77
import { AdminBenchmarkRolloverPreview } from '@/features/admin/benchmark/components/AdminBenchmarkRolloverPreview';
8+
import { AdminPageShell } from '@/features/admin/components/AdminPageShell';
89
import { useAdminBenchmarkController } from '@/features/admin/benchmark/useAdminBenchmarkController';
9-
import { PageIntro } from '@/components/ui/PageIntro';
1010
import { AdminLoginCard } from '@/features/admin/dashboard/components/AdminLoginCard';
11-
import { AdminNavigationLinks } from '@/features/admin/dashboard/components/AdminNavigationLinks';
1211
import { AdminResultBanner } from '@/features/admin/dashboard/components/AdminResultBanner';
1312

1413
export default function AdminBenchmarkPageClient() {
@@ -45,53 +44,60 @@ export default function AdminBenchmarkPageClient() {
4544

4645
if (!hasResolvedAuth) {
4746
return (
48-
<div className="min-h-[60vh] flex items-center justify-center px-4 py-12">
49-
<div className="glass-card p-8 w-full max-w-md mx-auto text-center border border-[var(--border-medium)]">
50-
<h1 className="text-2xl font-bold mb-3 text-[var(--text-primary)]">Benchmark Control</h1>
51-
<p className="text-[var(--text-secondary)]">Checking admin session...</p>
47+
<AdminPageShell
48+
title="Benchmark Control"
49+
description="Manage stable model families, exact releases, and future cohort lineups."
50+
>
51+
<div className="glass-card mx-auto max-w-md p-8 text-center border border-[var(--border-medium)]">
52+
<h2 className="text-2xl font-bold mb-3 text-[var(--text-primary)]">Checking admin session...</h2>
53+
<p className="text-[var(--text-secondary)]">Verifying your admin session before loading benchmark controls.</p>
5254
</div>
53-
</div>
55+
</AdminPageShell>
5456
);
5557
}
5658

5759
if (!isAuthenticated) {
5860
return (
59-
<AdminLoginCard
60-
password={password}
61-
error={error}
62-
loading={loading}
63-
onPasswordChange={setPassword}
64-
onSubmit={handleLogin}
65-
/>
61+
<AdminPageShell
62+
title="Benchmark Control"
63+
description="Manage stable model families, exact releases, and future cohort lineups."
64+
>
65+
<AdminLoginCard
66+
password={password}
67+
error={error}
68+
loading={loading}
69+
embedded
70+
onPasswordChange={setPassword}
71+
onSubmit={handleLogin}
72+
/>
73+
</AdminPageShell>
6674
);
6775
}
6876

6977
if (!overview || !configState) {
7078
return (
71-
<div className="container-wide mx-auto px-6 py-12">
72-
<div className="glass-card p-8 text-center border border-[var(--border-medium)]">
73-
<h1 className="text-2xl font-bold mb-3">Benchmark Control</h1>
74-
<p className="text-[var(--text-secondary)]">Loading benchmark lineage data...</p>
79+
<AdminPageShell
80+
title="Benchmark Control"
81+
description="Manage stable model families, exact releases, and future cohort lineups."
82+
>
83+
<div className="glass-card mx-auto max-w-md p-8 text-center border border-[var(--border-medium)]">
84+
<h2 className="text-2xl font-bold mb-3">Loading benchmark lineage data...</h2>
85+
<p className="text-[var(--text-secondary)]">Reading releases, configs, and current lineup details.</p>
7586
</div>
76-
</div>
87+
</AdminPageShell>
7788
);
7889
}
7990

8091
return (
81-
<div className="container-wide mx-auto px-6 py-12">
82-
<div className="-mx-6 mb-8">
83-
<PageIntro
84-
eyebrow="Admin"
85-
title="Benchmark Control"
86-
description="Manage stable model families, exact releases, and future cohort lineups."
87-
actions={(
88-
<button onClick={handleLogout} className="btn btn-secondary">
89-
Logout
90-
</button>
91-
)}
92-
/>
93-
</div>
94-
92+
<AdminPageShell
93+
title="Benchmark Control"
94+
description="Manage stable model families, exact releases, and future cohort lineups."
95+
actions={(
96+
<button onClick={handleLogout} className="btn btn-secondary">
97+
Logout
98+
</button>
99+
)}
100+
>
95101
<AdminResultBanner result={result} />
96102
<AdminBenchmarkCurrentLineup overview={overview} />
97103
{rolloverPreview && (
@@ -131,8 +137,6 @@ export default function AdminBenchmarkPageClient() {
131137
onPreviewRollover={handlePreviewRollover}
132138
/>
133139
</div>
134-
135-
<AdminNavigationLinks />
136-
</div>
140+
</AdminPageShell>
137141
);
138142
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Link from 'next/link';
2+
import type { ReactNode } from 'react';
3+
4+
interface AdminEmptyStateProps {
5+
title: string;
6+
description: string;
7+
actionLabel?: string;
8+
actionHref?: string;
9+
icon?: ReactNode;
10+
}
11+
12+
export function AdminEmptyState({
13+
title,
14+
description,
15+
actionLabel,
16+
actionHref,
17+
icon
18+
}: AdminEmptyStateProps) {
19+
return (
20+
<div className="flex min-h-[16rem] flex-col items-center justify-center rounded-2xl border border-dashed border-[var(--border-medium)] bg-[var(--bg-secondary)]/55 px-6 py-10 text-center">
21+
{icon && (
22+
<div className="mb-4 flex h-12 w-12 items-center justify-center rounded-2xl bg-[var(--bg-tertiary)] text-[var(--accent-gold)]">
23+
{icon}
24+
</div>
25+
)}
26+
<h3 className="text-lg font-semibold">{title}</h3>
27+
<p className="mt-2 max-w-md text-sm leading-relaxed text-[var(--text-secondary)]">
28+
{description}
29+
</p>
30+
{actionLabel && actionHref && (
31+
<Link href={actionHref} className="btn btn-secondary btn-sm mt-5">
32+
{actionLabel}
33+
</Link>
34+
)}
35+
</div>
36+
);
37+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use client';
2+
3+
import Link from 'next/link';
4+
import { usePathname } from 'next/navigation';
5+
import type { ReactNode } from 'react';
6+
import { PageIntro } from '@/components/ui/PageIntro';
7+
8+
interface AdminPageShellProps {
9+
title: string;
10+
description: string;
11+
actions?: ReactNode;
12+
children: ReactNode;
13+
}
14+
15+
const adminLinks = [
16+
{ href: '/admin', label: 'Dashboard' },
17+
{ href: '/admin/benchmark', label: 'Benchmark' },
18+
{ href: '/admin/costs', label: 'Costs' },
19+
{ href: '/admin/logs', label: 'Logs' },
20+
];
21+
22+
export function AdminPageShell({
23+
title,
24+
description,
25+
actions,
26+
children
27+
}: AdminPageShellProps) {
28+
const pathname = usePathname();
29+
30+
return (
31+
<div className="container-wide mx-auto px-6 py-12">
32+
<div className="-mx-6 mb-8">
33+
<PageIntro
34+
eyebrow="Admin"
35+
title={title}
36+
description={description}
37+
actions={actions}
38+
/>
39+
</div>
40+
41+
<nav
42+
aria-label="Admin sections"
43+
className="mb-8 grid grid-cols-2 gap-2 rounded-2xl border border-[var(--border-subtle)] bg-[var(--bg-secondary)]/75 p-2 backdrop-blur sm:flex sm:flex-wrap sm:items-center"
44+
>
45+
{adminLinks.map((link) => {
46+
const active = pathname === link.href;
47+
return (
48+
<Link
49+
key={link.href}
50+
href={link.href}
51+
className={`inline-flex min-h-[44px] items-center justify-center rounded-xl px-4 py-2 text-sm font-medium transition-colors sm:min-h-0 ${
52+
active
53+
? 'bg-[var(--accent-gold)] text-[var(--bg-primary)]'
54+
: 'text-[var(--text-secondary)] hover:bg-[var(--bg-tertiary)] hover:text-[var(--text-primary)]'
55+
}`}
56+
>
57+
{link.label}
58+
</Link>
59+
);
60+
})}
61+
</nav>
62+
63+
{children}
64+
</div>
65+
);
66+
}

0 commit comments

Comments
 (0)