fix: (dashboard) ui

This commit is contained in:
null 2026-05-19 22:59:39 -05:00
parent ee6cfe9531
commit edb92047a6
1 changed files with 61 additions and 61 deletions

View File

@ -403,24 +403,24 @@ function TopMetricCard({
}) { }) {
const iconTone = const iconTone =
accent === "blue" accent === "blue"
? "bg-blue-50 text-blue-600" ? "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
: accent === "green" : accent === "green"
? "bg-emerald-50 text-emerald-600" ? "bg-[color:rgba(52,211,153,0.15)] text-[color:var(--success)]"
: accent === "violet" : accent === "violet"
? "bg-violet-50 text-violet-600" ? "bg-[color:rgba(251,191,36,0.15)] text-[color:var(--warning)]"
: "bg-green-50 text-green-600"; : "bg-[color:rgba(52,211,153,0.15)] text-[color:var(--success)]";
return ( return (
<section className="rounded-xl border border-slate-200 bg-white p-4 md:p-6 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md"> <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 md:p-6 shadow-lush transition hover:-translate-y-0.5 hover:shadow-md">
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div> <div>
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<p className="text-xs font-semibold uppercase tracking-wider text-slate-500"> <p className="text-xs font-semibold uppercase tracking-wider text-muted">
{title} {title}
</p> </p>
{infoText ? ( {infoText ? (
<span <span
className="inline-flex text-slate-400" className="inline-flex text-muted"
title={infoText} title={infoText}
aria-label={infoText} aria-label={infoText}
> >
@ -429,11 +429,11 @@ function TopMetricCard({
) : null} ) : null}
</div> </div>
<div className="mt-2 flex items-end gap-2"> <div className="mt-2 flex items-end gap-2">
<p className="font-heading text-4xl font-bold text-slate-900"> <p className="font-heading text-4xl font-bold text-strong">
{value} {value}
</p> </p>
{secondary ? ( {secondary ? (
<p className="pb-1 text-xs text-slate-500">{secondary}</p> <p className="pb-1 text-xs text-muted">{secondary}</p>
) : null} ) : null}
</div> </div>
</div> </div>
@ -455,13 +455,13 @@ function InfoBlock({
rows: SummaryRow[]; rows: SummaryRow[];
}) { }) {
return ( return (
<section className="rounded-xl border border-slate-200 bg-white p-4 md:p-6 shadow-sm"> <section className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 md:p-6 shadow-lush">
<div className="mb-4 flex items-center justify-between gap-3"> <div className="mb-4 flex items-center justify-between gap-3">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<h3 className="text-lg font-semibold text-slate-900">{title}</h3> <h3 className="text-lg font-semibold text-strong">{title}</h3>
{infoText ? ( {infoText ? (
<span <span
className="inline-flex text-slate-400" className="inline-flex text-muted"
title={infoText} title={infoText}
aria-label={infoText} aria-label={infoText}
> >
@ -473,32 +473,32 @@ function InfoBlock({
<span <span
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${ className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${
badge.tone === "online" badge.tone === "online"
? "bg-emerald-100 text-emerald-700" ? "bg-[color:rgba(52,211,153,0.15)] text-[color:var(--success)]"
: badge.tone === "offline" : badge.tone === "offline"
? "bg-rose-100 text-rose-700" ? "bg-[color:rgba(248,113,113,0.12)] text-[color:var(--danger)]"
: "bg-slate-200 text-slate-700" : "bg-[color:var(--surface-strong)] text-muted"
}`} }`}
> >
{badge.text} {badge.text}
</span> </span>
) : null} ) : null}
</div> </div>
<div className="divide-y divide-slate-100 rounded-lg border border-slate-200 bg-white"> <div className="divide-y divide-[color:var(--border)] rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)]">
{rows.map((row) => ( {rows.map((row) => (
<div <div
key={`${row.label}-${row.value}`} key={`${row.label}-${row.value}`}
className="flex items-start justify-between gap-3 px-3 py-2" className="flex items-start justify-between gap-3 px-3 py-2"
> >
<span className="min-w-0 text-sm text-slate-500">{row.label}</span> <span className="min-w-0 text-sm text-muted">{row.label}</span>
<span <span
className={`max-w-[65%] break-words text-right text-sm font-medium leading-5 ${ className={`max-w-[65%] break-words text-right text-sm font-medium leading-5 ${
row.tone === "success" row.tone === "success"
? "text-emerald-700" ? "text-[color:var(--success)]"
: row.tone === "warning" : row.tone === "warning"
? "text-amber-700" ? "text-[color:var(--warning)]"
: row.tone === "danger" : row.tone === "danger"
? "text-rose-700" ? "text-[color:var(--danger)]"
: "text-slate-800" : "text-strong"
}`} }`}
> >
{row.value} {row.value}
@ -1022,10 +1022,10 @@ export default function DashboardPage() {
</SignedOut> </SignedOut>
<SignedIn> <SignedIn>
<DashboardSidebar /> <DashboardSidebar />
<main className="flex-1 overflow-y-auto bg-slate-50"> <main className="flex-1 overflow-y-auto bg-app">
<div className="p-4 md:p-8"> <div className="p-4 md:p-8">
{metricsQuery.error ? ( {metricsQuery.error ? (
<div className="mb-4 rounded-lg border border-rose-300 bg-rose-50 p-3 text-sm text-rose-700"> <div className="mb-4 rounded-lg border border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] p-3 text-sm text-[color:var(--danger)]">
Load failed: {metricsQuery.error.message} Load failed: {metricsQuery.error.message}
</div> </div>
) : null} ) : null}
@ -1088,14 +1088,14 @@ export default function DashboardPage() {
/> />
</div> </div>
<section className="mt-4 rounded-xl border border-slate-200 bg-white p-4 md:p-6 shadow-sm"> <section className="mt-4 rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 md:p-6 shadow-lush">
<div className="mb-3 flex items-center justify-between gap-3"> <div className="mb-3 flex items-center justify-between gap-3">
<h3 className="text-lg font-semibold text-slate-900"> <h3 className="text-lg font-semibold text-strong">
Pending Approvals Pending Approvals
</h3> </h3>
<Link <Link
href="/approvals" href="/approvals"
className="inline-flex items-center gap-1 text-xs text-slate-500 transition hover:text-slate-700" className="inline-flex items-center gap-1 text-xs text-muted transition hover:text-strong"
> >
Open global approvals Open global approvals
<ArrowUpRight className="h-3.5 w-3.5" /> <ArrowUpRight className="h-3.5 w-3.5" />
@ -1103,73 +1103,73 @@ export default function DashboardPage() {
</div> </div>
{!metrics && metricsQuery.isLoading ? ( {!metrics && metricsQuery.isLoading ? (
<div className="rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-500"> <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-sm text-muted">
Loading pending approvals... Loading pending approvals...
</div> </div>
) : !metrics && metricsQuery.error ? ( ) : !metrics && metricsQuery.error ? (
<div className="rounded-lg border border-amber-300 bg-amber-50 p-3 text-sm text-amber-800"> <div className="rounded-lg border border-[color:rgba(251,191,36,0.35)] bg-[color:rgba(251,191,36,0.08)] p-3 text-sm text-[color:var(--warning)]">
Pending approvals are temporarily unavailable. Pending approvals are temporarily unavailable.
</div> </div>
) : hasPendingApprovals ? ( ) : hasPendingApprovals ? (
<div className="space-y-2"> <div className="space-y-2">
<div className="divide-y divide-slate-100 rounded-lg border border-slate-200 bg-white"> <div className="divide-y divide-[color:var(--border)] rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)]">
{pendingApprovalItems.map((item) => ( {pendingApprovalItems.map((item) => (
<Link <Link
key={item.approval_id} key={item.approval_id}
href={`/boards/${item.board_id}/approvals`} href={`/boards/${item.board_id}/approvals`}
className="flex items-center justify-between gap-3 px-3 py-2 transition hover:bg-slate-50" className="flex items-center justify-between gap-3 px-3 py-2 transition hover:bg-[color:var(--surface-strong)]"
> >
<span className="min-w-0 text-sm text-slate-700"> <span className="min-w-0 text-sm text-strong">
<span className="block truncate font-medium text-slate-800"> <span className="block truncate font-medium text-strong">
{item.task_title || "Pending approval"} {item.task_title || "Pending approval"}
</span> </span>
<span className="block truncate text-xs text-slate-500"> <span className="block truncate text-xs text-muted">
{item.board_name} · {item.confidence}% score {item.board_name} · {item.confidence}% score
</span> </span>
</span> </span>
<span className="shrink-0 text-xs text-slate-500"> <span className="shrink-0 text-xs text-muted">
{formatRelativeTimestamp(item.created_at)} {formatRelativeTimestamp(item.created_at)}
</span> </span>
</Link> </Link>
))} ))}
</div> </div>
{pendingApprovalsTotal > pendingApprovalItems.length ? ( {pendingApprovalsTotal > pendingApprovalItems.length ? (
<p className="text-xs text-slate-500"> <p className="text-xs text-muted">
Showing latest {formatCount(pendingApprovalItems.length)}{" "} Showing latest {formatCount(pendingApprovalItems.length)}{" "}
of {formatCount(pendingApprovalsTotal)} pending approvals. of {formatCount(pendingApprovalsTotal)} pending approvals.
</p> </p>
) : null} ) : null}
</div> </div>
) : ( ) : (
<div className="rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-sm text-emerald-700"> <div className="rounded-lg border border-[color:rgba(52,211,153,0.35)] bg-[color:rgba(52,211,153,0.08)] p-3 text-sm text-[color:var(--success)]">
No pending approvals across your boards. No pending approvals across your boards.
</div> </div>
)} )}
</section> </section>
<div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2"> <div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<section className="min-w-0 overflow-hidden rounded-xl border border-slate-200 bg-white p-4 md:p-6 shadow-sm"> <section className="min-w-0 overflow-hidden rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 md:p-6 shadow-lush">
<div className="mb-3 flex items-center justify-between gap-3"> <div className="mb-3 flex items-center justify-between gap-3">
<h3 className="text-lg font-semibold text-slate-900"> <h3 className="text-lg font-semibold text-strong">
Sessions Sessions
</h3> </h3>
<span className="text-xs text-slate-500"> <span className="text-xs text-muted">
{formatCount(activeSessions)} {formatCount(activeSessions)}
</span> </span>
</div> </div>
<div className="max-h-[310px] space-y-2 overflow-x-hidden overflow-y-auto pr-1"> <div className="max-h-[310px] space-y-2 overflow-x-hidden overflow-y-auto pr-1">
{!hasConfiguredGateways ? ( {!hasConfiguredGateways ? (
<div className="rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-500"> <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-sm text-muted">
No gateways are configured for any board yet. No gateways are configured for any board yet.
</div> </div>
) : gatewayStatusesQuery.isLoading ? ( ) : gatewayStatusesQuery.isLoading ? (
<div className="rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-500"> <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-sm text-muted">
Loading sessions... Loading sessions...
</div> </div>
) : sessionSummaries.length > 0 ? ( ) : sessionSummaries.length > 0 ? (
<> <>
{gatewayUnavailableCount > 0 ? ( {gatewayUnavailableCount > 0 ? (
<div className="rounded-lg border border-amber-300 bg-amber-50 p-3 text-sm text-amber-800"> <div className="rounded-lg border border-[color:rgba(251,191,36,0.35)] bg-[color:rgba(251,191,36,0.08)] p-3 text-sm text-[color:var(--warning)]">
{formatCount(gatewayUnavailableCount)} gateway {formatCount(gatewayUnavailableCount)} gateway
{gatewayUnavailableCount === 1 ? "" : "s"}{" "} {gatewayUnavailableCount === 1 ? "" : "s"}{" "}
unavailable; showing sessions from reachable gateways. unavailable; showing sessions from reachable gateways.
@ -1178,31 +1178,31 @@ export default function DashboardPage() {
{sessionSummaries.map((session) => ( {sessionSummaries.map((session) => (
<div <div
key={session.key} key={session.key}
className="overflow-hidden rounded-lg border border-slate-200 bg-white px-3 py-2" className="overflow-hidden rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 py-2"
> >
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-slate-900"> <p className="truncate text-sm font-medium text-strong">
<span <span
className={`mr-2 inline-block h-2 w-2 rounded-full ${ className={`mr-2 inline-block h-2 w-2 rounded-full ${
session.isMain session.isMain
? "bg-emerald-500" ? "bg-[color:var(--success)]"
: "bg-slate-400" : "bg-[color:var(--border-strong)]"
}`} }`}
/> />
{session.title} {session.title}
</p> </p>
<p className="mt-0.5 truncate text-xs text-slate-500"> <p className="mt-0.5 truncate text-xs text-muted">
{session.subtitle} {session.subtitle}
</p> </p>
</div> </div>
<div className="min-w-0 max-w-[45%] text-right"> <div className="min-w-0 max-w-[45%] text-right">
<p className="truncate text-xs font-medium text-slate-700"> <p className="truncate text-xs font-medium text-strong">
{session.usage === DASH {session.usage === DASH
? "Usage unavailable" ? "Usage unavailable"
: session.usage} : session.usage}
</p> </p>
<p className="text-[11px] text-slate-500"> <p className="text-[11px] text-muted">
{session.lastSeenAt {session.lastSeenAt
? formatRelativeTimestamp(session.lastSeenAt) ? formatRelativeTimestamp(session.lastSeenAt)
: "Activity unavailable"} : "Activity unavailable"}
@ -1213,25 +1213,25 @@ export default function DashboardPage() {
))} ))}
</> </>
) : gatewayUnavailableCount === gatewayTargets.length ? ( ) : gatewayUnavailableCount === gatewayTargets.length ? (
<div className="rounded-lg border border-rose-300 bg-rose-50 p-3 text-sm text-rose-700"> <div className="rounded-lg border border-[color:rgba(248,113,113,0.35)] bg-[color:rgba(248,113,113,0.08)] p-3 text-sm text-[color:var(--danger)]">
Session data is unavailable for all configured gateways. Session data is unavailable for all configured gateways.
</div> </div>
) : ( ) : (
<div className="rounded-lg border border-slate-200 bg-slate-50 p-3 text-sm text-slate-500"> <div className="rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-3 text-sm text-muted">
No active sessions detected. No active sessions detected.
</div> </div>
)} )}
</div> </div>
</section> </section>
<section className="min-w-0 overflow-hidden rounded-xl border border-slate-200 bg-white p-4 md:p-6 shadow-sm"> <section className="min-w-0 overflow-hidden rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4 md:p-6 shadow-lush">
<div className="mb-3 flex items-center justify-between gap-3"> <div className="mb-3 flex items-center justify-between gap-3">
<h3 className="text-lg font-semibold text-slate-900"> <h3 className="text-lg font-semibold text-strong">
Recent Activity Recent Activity
</h3> </h3>
<Link <Link
href={activityFeedHref} href={activityFeedHref}
className="inline-flex items-center gap-1 text-xs text-slate-500 transition hover:text-slate-700" className="inline-flex items-center gap-1 text-xs text-muted transition hover:text-strong"
> >
Open feed Open feed
<ArrowUpRight className="h-3.5 w-3.5" /> <ArrowUpRight className="h-3.5 w-3.5" />
@ -1253,11 +1253,11 @@ export default function DashboardPage() {
onKeyDown={(interactionEvent) => onKeyDown={(interactionEvent) =>
handleLogRowKeyDown(interactionEvent, eventHref) handleLogRowKeyDown(interactionEvent, eventHref)
} }
className="cursor-pointer overflow-hidden rounded-lg border border-slate-200 bg-white px-3 py-2 transition hover:border-slate-300 focus-visible:border-slate-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-300" className="cursor-pointer overflow-hidden rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] px-3 py-2 transition hover:border-[color:var(--border-strong)] hover:bg-[color:var(--surface-strong)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)]"
> >
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="min-w-0 flex-1 overflow-hidden"> <div className="min-w-0 flex-1 overflow-hidden">
<div className="break-words text-sm font-medium text-slate-900 [&_ol]:mb-0 [&_p]:mb-0 [&_pre]:my-1 [&_pre]:max-w-full [&_pre]:overflow-x-auto [&_ul]:mb-0"> <div className="break-words text-sm font-medium text-strong [&_ol]:mb-0 [&_p]:mb-0 [&_pre]:my-1 [&_pre]:max-w-full [&_pre]:overflow-x-auto [&_ul]:mb-0">
<Markdown <Markdown
content={ content={
event.message?.trim() || event.event_type event.message?.trim() || event.event_type
@ -1265,11 +1265,11 @@ export default function DashboardPage() {
variant="comment" variant="comment"
/> />
</div> </div>
<p className="mt-0.5 text-xs uppercase tracking-wider text-slate-500"> <p className="mt-0.5 text-xs uppercase tracking-wider text-muted">
{event.event_type} {event.event_type}
</p> </p>
</div> </div>
<div className="shrink-0 text-right text-[11px] text-slate-500"> <div className="shrink-0 text-right text-[11px] text-muted">
<p>{formatRelativeTimestamp(event.created_at)}</p> <p>{formatRelativeTimestamp(event.created_at)}</p>
<p>{formatTimestamp(event.created_at)}</p> <p>{formatTimestamp(event.created_at)}</p>
</div> </div>
@ -1278,10 +1278,10 @@ export default function DashboardPage() {
); );
}) })
) : ( ) : (
<div className="flex h-[240px] flex-col items-center justify-center rounded-lg border border-slate-200 bg-white text-sm text-slate-500"> <div className="flex h-[240px] flex-col items-center justify-center rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-muted)] text-sm text-muted">
<Shield className="mb-2 h-5 w-5 text-slate-400" /> <Shield className="mb-2 h-5 w-5 text-muted" />
No activity yet No activity yet
<p className="mt-1 text-xs text-slate-500"> <p className="mt-1 text-xs text-muted">
Activity appears here when events are emitted. Activity appears here when events are emitted.
</p> </p>
</div> </div>