Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions frontend/src/app/(dashboard)/analysis/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ type TopNTab = 'api' | 'sql' | 'filter' | 'escalation' | 'queued'
/** In-page anchors for the jump rail (see CollapsibleSection `id` pattern). */
const DASHBOARD_JUMP_LINKS: DashboardJumpLink[] = [
{ href: '#dashboard-overview', label: 'Overview & KPIs', shortLabel: 'Overview' },
{ href: '#dashboard-activity', label: 'Throughput chart', shortLabel: 'Chart' },
{ href: '#dashboard-explorer', label: 'Top entries & distribution', shortLabel: 'Top N' },
{ href: '#dashboard-activity', label: 'Throughput & distribution', shortLabel: 'Charts' },
{ href: '#dashboard-explorer', label: 'Top entries table', shortLabel: 'Top N' },
]

const TOP_N_TABS: Array<{
Expand Down Expand Up @@ -219,12 +219,17 @@ export default function AnalysisDashboardPage() {
<DashboardJobCompare currentJobId={jobId} />
</div>

<div id="dashboard-activity" className="scroll-mt-24">
<TimeSeriesChart data={time_series} />
<div id="dashboard-activity" className="scroll-mt-24 grid grid-cols-1 gap-5 xl:grid-cols-5">
<div className="xl:col-span-3">
<TimeSeriesChart data={time_series} />
</div>
<div className="xl:col-span-2">
<DistributionChart distribution={distribution} />
</div>
</div>

<div id="dashboard-explorer" className="scroll-mt-24 grid grid-cols-1 gap-5 xl:grid-cols-5">
<div className="xl:col-span-3 rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] shadow-sm overflow-hidden">
<div id="dashboard-explorer" className="scroll-mt-24">
<div className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] shadow-sm overflow-hidden">
<div
className="flex border-b border-[var(--color-border)] bg-[var(--color-bg-secondary)]"
role="tablist"
Expand Down Expand Up @@ -276,10 +281,6 @@ export default function AnalysisDashboardPage() {
/>
</div>
</div>

<div className="xl:col-span-2">
<DistributionChart distribution={distribution} />
</div>
</div>

<section className="rounded-lg border border-[var(--color-border)] bg-[var(--color-bg-secondary)] p-4">
Expand Down
19 changes: 13 additions & 6 deletions frontend/src/components/dashboard/stats-cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function StatCard({
return (
<div
className={cn(
'flex flex-col gap-2 rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-4 shadow-sm',
'flex min-h-[7rem] flex-col gap-2 rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] p-4 shadow-sm',
'border-l-[3px] transition-[transform,box-shadow] duration-200 ease-out',
'hover:-translate-y-0.5 hover:shadow-md',
className
Expand All @@ -63,18 +63,25 @@ function StatCard({
style={{ backgroundColor: accentColor }}
aria-hidden="true"
/>
<span className="text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-[0.12em] truncate">
<span
className="line-clamp-2 min-h-[2rem] text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-[0.12em]"
title={label}
>
{label}
</span>
</div>
<span
className="text-2xl font-bold tabular-nums text-[var(--color-text-primary)]"
className="truncate text-2xl font-bold tabular-nums text-[var(--color-text-primary)]"
title={typeof value === 'number' ? value.toLocaleString() : value}
style={textColor ? { color: textColor } : undefined}
>
{typeof value === 'number' ? value.toLocaleString() : value}
</span>
{description && (
<span className="text-[11px] text-[var(--color-text-tertiary)] leading-tight">
<span
className="line-clamp-2 text-[11px] leading-tight text-[var(--color-text-tertiary)]"
title={description}
>
{description}
</span>
)}
Expand All @@ -95,8 +102,8 @@ export function StatsCards({ stats, distribution, errorSummary, className }: Sta

const gridCols =
errorSummary && errorSummary.jar_event_total > 0
? 'grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-7'
: 'grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6'
? 'grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-7'
: 'grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6'

return (
<div
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/components/dashboard/top-n-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ interface ColumnDef {
getValue: (entry: TopNEntry, parsed: ParsedDetails) => string | number | null
/** How to render the cell */
render: (entry: TopNEntry, parsed: ParsedDetails, maxDuration: number) => React.ReactNode
/** Responsive visibility to reduce horizontal scrolling on smaller viewports. */
visibilityClass?: string
}

interface ParsedDetails {
Expand Down Expand Up @@ -182,6 +184,7 @@ const FORM_COL: ColumnDef = {
label: 'Form',
align: 'left',
width: 'max-w-[10rem]',
visibilityClass: 'hidden lg:table-cell',
sortable: true,
getValue: (e) => e.form ?? '',
render: (e) => (
Expand All @@ -196,6 +199,7 @@ const QUEUE_COL: ColumnDef = {
label: 'Queue',
align: 'left',
width: 'max-w-[8rem]',
visibilityClass: 'hidden xl:table-cell',
sortable: true,
getValue: (e) => e.queue,
render: (e) => (
Expand Down Expand Up @@ -223,6 +227,7 @@ const TIMESTAMP_COL: ColumnDef = {
label: 'Time',
align: 'left',
width: 'w-24',
visibilityClass: 'hidden lg:table-cell',
sortable: true,
getValue: (e) => e.timestamp,
render: (e) => (
Expand All @@ -237,6 +242,7 @@ const USER_COL: ColumnDef = {
label: 'User',
align: 'left',
width: 'max-w-[7rem]',
visibilityClass: 'hidden xl:table-cell',
sortable: true,
getValue: (e) => e.user,
render: (e) => (
Expand All @@ -261,6 +267,7 @@ const QUEUE_TIME_COL: ColumnDef = {
label: 'Q-Time',
align: 'right',
width: 'w-20',
visibilityClass: 'hidden xl:table-cell',
sortable: true,
getValue: (e) => e.queue_time_ms ?? 0,
render: (e) => {
Expand All @@ -282,6 +289,7 @@ const ESC_POOL_COL: ColumnDef = {
label: 'Pool',
align: 'left',
width: 'max-w-[8rem]',
visibilityClass: 'hidden lg:table-cell',
sortable: false,
getValue: (_e, p) => p.esc_pool ?? '',
render: (_e, p) => (
Expand All @@ -299,6 +307,7 @@ const ESC_DELAY_COL: ColumnDef = {
label: 'Delay',
align: 'right',
width: 'w-20',
visibilityClass: 'hidden xl:table-cell',
sortable: false,
getValue: (_e, p) => p.delay_ms ?? 0,
render: (_e, p) => {
Expand All @@ -317,6 +326,7 @@ const FILTER_LEVEL_COL: ColumnDef = {
label: 'Level',
align: 'center',
width: 'w-14',
visibilityClass: 'hidden lg:table-cell',
sortable: false,
getValue: (_e, p) => p.filter_level ?? 0,
render: (_e, p) => {
Expand Down Expand Up @@ -389,6 +399,7 @@ const SQL_STATEMENT_COL: ColumnDef = {
key: 'sql_statement',
label: 'SQL Statement',
align: 'left',
visibilityClass: 'hidden xl:table-cell',
sortable: false,
getValue: (_e, p) => p.sql_statement ?? '',
render: (_e, p) => {
Expand Down Expand Up @@ -654,6 +665,7 @@ function TopNTableContent({
col.sortable && 'cursor-pointer select-none',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-inset',
col.align === 'right' ? 'text-right' : col.align === 'center' ? 'text-center' : 'text-left',
col.visibilityClass,
)}
onClick={col.sortable ? () => handleSort(col.key) : undefined}
onKeyDown={col.sortable ? (e) => {
Expand Down Expand Up @@ -701,6 +713,7 @@ function TopNTableContent({
className={cn(
'box-border px-3 py-2 align-top',
col.align === 'right' ? 'text-right' : col.align === 'center' ? 'text-center' : 'text-left',
col.visibilityClass,
)}
>
{col.render(entry, parsed, maxDuration)}
Expand Down
Loading