fix: (dashboard) ui
This commit is contained in:
parent
ee6cfe9531
commit
edb92047a6
|
|
@ -403,24 +403,24 @@ function TopMetricCard({
|
|||
}) {
|
||||
const iconTone =
|
||||
accent === "blue"
|
||||
? "bg-blue-50 text-blue-600"
|
||||
? "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
|
||||
: accent === "green"
|
||||
? "bg-emerald-50 text-emerald-600"
|
||||
? "bg-[color:rgba(52,211,153,0.15)] text-[color:var(--success)]"
|
||||
: accent === "violet"
|
||||
? "bg-violet-50 text-violet-600"
|
||||
: "bg-green-50 text-green-600";
|
||||
? "bg-[color:rgba(251,191,36,0.15)] text-[color:var(--warning)]"
|
||||
: "bg-[color:rgba(52,211,153,0.15)] text-[color:var(--success)]";
|
||||
|
||||
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>
|
||||
<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}
|
||||
</p>
|
||||
{infoText ? (
|
||||
<span
|
||||
className="inline-flex text-slate-400"
|
||||
className="inline-flex text-muted"
|
||||
title={infoText}
|
||||
aria-label={infoText}
|
||||
>
|
||||
|
|
@ -429,11 +429,11 @@ function TopMetricCard({
|
|||
) : null}
|
||||
</div>
|
||||
<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}
|
||||
</p>
|
||||
{secondary ? (
|
||||
<p className="pb-1 text-xs text-slate-500">{secondary}</p>
|
||||
<p className="pb-1 text-xs text-muted">{secondary}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -455,13 +455,13 @@ function InfoBlock({
|
|||
rows: SummaryRow[];
|
||||
}) {
|
||||
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="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 ? (
|
||||
<span
|
||||
className="inline-flex text-slate-400"
|
||||
className="inline-flex text-muted"
|
||||
title={infoText}
|
||||
aria-label={infoText}
|
||||
>
|
||||
|
|
@ -473,32 +473,32 @@ function InfoBlock({
|
|||
<span
|
||||
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${
|
||||
badge.tone === "online"
|
||||
? "bg-emerald-100 text-emerald-700"
|
||||
? "bg-[color:rgba(52,211,153,0.15)] text-[color:var(--success)]"
|
||||
: badge.tone === "offline"
|
||||
? "bg-rose-100 text-rose-700"
|
||||
: "bg-slate-200 text-slate-700"
|
||||
? "bg-[color:rgba(248,113,113,0.12)] text-[color:var(--danger)]"
|
||||
: "bg-[color:var(--surface-strong)] text-muted"
|
||||
}`}
|
||||
>
|
||||
{badge.text}
|
||||
</span>
|
||||
) : null}
|
||||
</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) => (
|
||||
<div
|
||||
key={`${row.label}-${row.value}`}
|
||||
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
|
||||
className={`max-w-[65%] break-words text-right text-sm font-medium leading-5 ${
|
||||
row.tone === "success"
|
||||
? "text-emerald-700"
|
||||
? "text-[color:var(--success)]"
|
||||
: row.tone === "warning"
|
||||
? "text-amber-700"
|
||||
? "text-[color:var(--warning)]"
|
||||
: row.tone === "danger"
|
||||
? "text-rose-700"
|
||||
: "text-slate-800"
|
||||
? "text-[color:var(--danger)]"
|
||||
: "text-strong"
|
||||
}`}
|
||||
>
|
||||
{row.value}
|
||||
|
|
@ -1022,10 +1022,10 @@ export default function DashboardPage() {
|
|||
</SignedOut>
|
||||
<SignedIn>
|
||||
<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">
|
||||
{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}
|
||||
</div>
|
||||
) : null}
|
||||
|
|
@ -1088,14 +1088,14 @@ export default function DashboardPage() {
|
|||
/>
|
||||
</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">
|
||||
<h3 className="text-lg font-semibold text-slate-900">
|
||||
<h3 className="text-lg font-semibold text-strong">
|
||||
Pending Approvals
|
||||
</h3>
|
||||
<Link
|
||||
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
|
||||
<ArrowUpRight className="h-3.5 w-3.5" />
|
||||
|
|
@ -1103,73 +1103,73 @@ export default function DashboardPage() {
|
|||
</div>
|
||||
|
||||
{!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...
|
||||
</div>
|
||||
) : !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.
|
||||
</div>
|
||||
) : hasPendingApprovals ? (
|
||||
<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) => (
|
||||
<Link
|
||||
key={item.approval_id}
|
||||
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="block truncate font-medium text-slate-800">
|
||||
<span className="min-w-0 text-sm text-strong">
|
||||
<span className="block truncate font-medium text-strong">
|
||||
{item.task_title || "Pending approval"}
|
||||
</span>
|
||||
<span className="block truncate text-xs text-slate-500">
|
||||
<span className="block truncate text-xs text-muted">
|
||||
{item.board_name} · {item.confidence}% score
|
||||
</span>
|
||||
</span>
|
||||
<span className="shrink-0 text-xs text-slate-500">
|
||||
<span className="shrink-0 text-xs text-muted">
|
||||
{formatRelativeTimestamp(item.created_at)}
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
{pendingApprovalsTotal > pendingApprovalItems.length ? (
|
||||
<p className="text-xs text-slate-500">
|
||||
<p className="text-xs text-muted">
|
||||
Showing latest {formatCount(pendingApprovalItems.length)}{" "}
|
||||
of {formatCount(pendingApprovalsTotal)} pending approvals.
|
||||
</p>
|
||||
) : null}
|
||||
</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.
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<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">
|
||||
<h3 className="text-lg font-semibold text-slate-900">
|
||||
<h3 className="text-lg font-semibold text-strong">
|
||||
Sessions
|
||||
</h3>
|
||||
<span className="text-xs text-slate-500">
|
||||
<span className="text-xs text-muted">
|
||||
{formatCount(activeSessions)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="max-h-[310px] space-y-2 overflow-x-hidden overflow-y-auto pr-1">
|
||||
{!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.
|
||||
</div>
|
||||
) : 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...
|
||||
</div>
|
||||
) : sessionSummaries.length > 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
|
||||
{gatewayUnavailableCount === 1 ? "" : "s"}{" "}
|
||||
unavailable; showing sessions from reachable gateways.
|
||||
|
|
@ -1178,31 +1178,31 @@ export default function DashboardPage() {
|
|||
{sessionSummaries.map((session) => (
|
||||
<div
|
||||
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="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
|
||||
className={`mr-2 inline-block h-2 w-2 rounded-full ${
|
||||
session.isMain
|
||||
? "bg-emerald-500"
|
||||
: "bg-slate-400"
|
||||
? "bg-[color:var(--success)]"
|
||||
: "bg-[color:var(--border-strong)]"
|
||||
}`}
|
||||
/>
|
||||
{session.title}
|
||||
</p>
|
||||
<p className="mt-0.5 truncate text-xs text-slate-500">
|
||||
<p className="mt-0.5 truncate text-xs text-muted">
|
||||
{session.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
<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
|
||||
? "Usage unavailable"
|
||||
: session.usage}
|
||||
</p>
|
||||
<p className="text-[11px] text-slate-500">
|
||||
<p className="text-[11px] text-muted">
|
||||
{session.lastSeenAt
|
||||
? formatRelativeTimestamp(session.lastSeenAt)
|
||||
: "Activity unavailable"}
|
||||
|
|
@ -1213,25 +1213,25 @@ export default function DashboardPage() {
|
|||
))}
|
||||
</>
|
||||
) : 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.
|
||||
</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.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</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">
|
||||
<h3 className="text-lg font-semibold text-slate-900">
|
||||
<h3 className="text-lg font-semibold text-strong">
|
||||
Recent Activity
|
||||
</h3>
|
||||
<Link
|
||||
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
|
||||
<ArrowUpRight className="h-3.5 w-3.5" />
|
||||
|
|
@ -1253,11 +1253,11 @@ export default function DashboardPage() {
|
|||
onKeyDown={(interactionEvent) =>
|
||||
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="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
|
||||
content={
|
||||
event.message?.trim() || event.event_type
|
||||
|
|
@ -1265,11 +1265,11 @@ export default function DashboardPage() {
|
|||
variant="comment"
|
||||
/>
|
||||
</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}
|
||||
</p>
|
||||
</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>{formatTimestamp(event.created_at)}</p>
|
||||
</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">
|
||||
<Shield className="mb-2 h-5 w-5 text-slate-400" />
|
||||
<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-muted" />
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue