Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
98e715e
Move the Add to dashboard button to the right side of the chart
samejr Feb 13, 2026
d5aaba1
Show a checkmark on the selected dashboard when choosing from the modal
samejr Feb 13, 2026
188dcf5
Remove the ‘card’ style from the Query page so the sections are clearer
samejr Feb 14, 2026
dddef81
Merge remote-tracking branch 'origin/main' into chore(webapp)-metrics…
samejr Feb 14, 2026
0e0586e
Segmented control accepts react node
samejr Feb 14, 2026
4a41f68
Improves the graph query section side bar
samejr Feb 14, 2026
5bbe2fc
Bright text for the big number side panel items
samejr Feb 14, 2026
1513d8b
Import tidy
samejr Feb 14, 2026
355e5ab
Consistent chart title padding
samejr Feb 14, 2026
1e21fdd
Nicer legend padding
samejr Feb 14, 2026
ddbf70d
Use our segmented control instead
samejr Feb 14, 2026
f67c131
New shortcut key style variant
samejr Feb 14, 2026
9fafcb5
Adds “v” shortcut to quick view charts full screen
samejr Feb 14, 2026
8df40ca
Use our useShortcutKeys component instead
samejr Feb 14, 2026
4c3e073
Adds new full screen shortcut to shortcuts sheet
samejr Feb 14, 2026
392d666
Improved accessability styles when tabbing through buttons
samejr Feb 14, 2026
3166621
More accessible tabbing improvements
samejr Feb 14, 2026
d8f3ca0
small fix for hovering on a client tab
samejr Feb 14, 2026
95443d2
Fixes table hover state height
samejr Feb 14, 2026
ed64032
Center the status column
samejr Feb 14, 2026
0462aa6
Nice chart blank states
samejr Feb 14, 2026
ad7ba8e
Merge branch 'main' into chore(webapp)-metrics-ui-improvements
samejr Feb 14, 2026
c61d39c
Show the table header along with the message if a query returns no re…
samejr Feb 14, 2026
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
6 changes: 6 additions & 0 deletions apps/webapp/app/components/Shortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ function ShortcutContent() {
<ShortcutKey shortcut={{ key: "v" }} variant="medium/bright" />
</Shortcut>
</div>
<div className="space-y-3">
<Header3>Metrics page</Header3>
<Shortcut name="Toggle fullscreen chart">
<ShortcutKey shortcut={{ key: "v" }} variant="medium/bright" />
</Shortcut>
</div>
<div className="space-y-3">
<Header3>Schedules page</Header3>
<Shortcut name="New schedule">
Expand Down
137 changes: 52 additions & 85 deletions apps/webapp/app/components/code/ChartConfigPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { OutputColumnMetadata } from "@internal/clickhouse";
import { IconSortAscending, IconSortDescending } from "@tabler/icons-react";
import { BarChart, CheckIcon, LineChart, Plus, XIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { cn } from "~/utils/cn";
import { Paragraph } from "../primitives/Paragraph";
import { Popover, PopoverContent, PopoverTrigger } from "../primitives/Popover";
import { Select, SelectItem } from "../primitives/Select";
import { Switch } from "../primitives/Switch";
import SegmentedControl from "../primitives/SegmentedControl";
import { Button } from "../primitives/Buttons";
import {
type AggregationType,
Expand Down Expand Up @@ -234,54 +236,38 @@ export function ChartConfigPanel({ columns, config, onChange, className }: Chart
}

return (
<div className={cn("flex flex-col gap-2 p-2", className)}>
<div className={cn("flex flex-col gap-3 p-2", className)}>
{/* Chart Type */}
<div className="flex flex-col gap-3">
<ConfigField label="Type">
<div className="flex items-center">
<Button
type="button"
variant="tertiary/small"
className={cn(
"rounded-r-none border-b pl-1 pr-2",
config.chartType === "bar" ? "border-indigo-500" : "border-transparent"
)}
iconSpacing="gap-x-1"
onClick={() => updateConfig({ chartType: "bar" })}
LeadingIcon={BarChart}
leadingIconClassName={
config.chartType === "bar" ? "text-indigo-500" : "text-text-dimmed"
}
>
<span className={config.chartType === "bar" ? "text-indigo-500" : "text-text-dimmed"}>
Bar
</span>
</Button>
<Button
type="button"
variant="tertiary/small"
className={cn(
"rounded-l-none border-b pl-1 pr-2",
config.chartType === "line" ? "border-indigo-500" : "border-transparent"
)}
iconSpacing="gap-x-1"
onClick={() => updateConfig({ chartType: "line" })}
LeadingIcon={LineChart}
leadingIconClassName={
config.chartType === "line" ? "text-indigo-500" : "text-text-dimmed"
}
>
<span
className={config.chartType === "line" ? "text-indigo-500" : "text-text-dimmed"}
>
Line
</span>
</Button>
</div>
<SegmentedControl
name="chartType"
value={config.chartType}
variant="secondary/small"
options={[
{
label: (
<span className="flex items-center gap-1">
<BarChart className="size-3" /> Bar
</span>
),
value: "bar",
},
{
label: (
<span className="flex items-center gap-1">
<LineChart className="size-3" /> Line
</span>
),
value: "line",
},
]}
onChange={(value) => updateConfig({ chartType: value as "bar" | "line" })}
/>
</ConfigField>
</div>

<div className="flex flex-col gap-2">
<div className="flex flex-col gap-3">
{/* X-Axis */}
<ConfigField label="X-Axis">
<Select
Expand Down Expand Up @@ -529,9 +515,29 @@ export function ChartConfigPanel({ columns, config, onChange, className }: Chart
{/* Sort Direction (only when sorting) */}
{config.sortByColumn && (
<ConfigField label="Sort direction">
<SortDirectionToggle
direction={config.sortDirection}
onChange={(direction) => updateConfig({ sortDirection: direction })}
<SegmentedControl
name="sortDirection"
value={config.sortDirection}
variant="secondary/small"
options={[
{
label: (
<span className="flex items-center gap-1">
<IconSortAscending className="size-3" /> Asc
</span>
),
value: "asc",
},
{
label: (
<span className="flex items-center gap-1">
<IconSortDescending className="size-3" /> Desc
</span>
),
value: "desc",
},
]}
onChange={(value) => updateConfig({ sortDirection: value as SortDirection })}
/>
</ConfigField>
)}
Expand All @@ -543,51 +549,12 @@ export function ChartConfigPanel({ columns, config, onChange, className }: Chart
function ConfigField({ label, children }: { label: string; children: React.ReactNode }) {
return (
<div className="flex flex-col gap-1">
{label && <span className="text-xs text-text-dimmed">{label}</span>}
{label && <span className="text-xs text-text-bright">{label}</span>}
{children}
</div>
);
}

function SortDirectionToggle({
direction,
onChange,
}: {
direction: SortDirection;
onChange: (direction: SortDirection) => void;
}) {
return (
<div className="flex gap-1">
<button
type="button"
onClick={() => onChange("asc")}
className={cn(
"rounded px-2 py-1 text-xs transition-colors",
direction === "asc"
? "bg-charcoal-700 text-text-bright"
: "text-text-dimmed hover:bg-charcoal-800 hover:text-text-bright"
)}
title="Ascending"
>
Asc
</button>
<button
type="button"
onClick={() => onChange("desc")}
className={cn(
"rounded px-2 py-1 text-xs transition-colors",
direction === "desc"
? "bg-charcoal-700 text-text-bright"
: "text-text-dimmed hover:bg-charcoal-800 hover:text-text-bright"
)}
title="Descending"
>
Desc
</button>
</div>
);
}

function SeriesColorPicker({
color,
onColorChange,
Expand Down
22 changes: 8 additions & 14 deletions apps/webapp/app/components/code/QueryResultsChart.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { OutputColumnMetadata } from "@internal/clickhouse";
import { BarChart3, LineChart } from "lucide-react";
import { memo, useMemo } from "react";
import type { ChartConfig } from "~/components/primitives/charts/Chart";
import { Chart } from "~/components/primitives/charts/ChartCompound";
import { Paragraph } from "../primitives/Paragraph";
import { ChartBlankState } from "../primitives/charts/ChartBlankState";
import type { AggregationType, ChartConfiguration } from "../metrics/QueryWidget";
import { aggregateValues } from "../primitives/charts/aggregation";
import { getRunStatusHexColor } from "~/components/runs/v3/TaskRunStatus";
Expand Down Expand Up @@ -947,20 +948,22 @@ export const QueryResultsChart = memo(function QueryResultsChart({
}, [isDateBased, xAxisTickFormatter, xAxisAngle]);

// Validation — all hooks must be above this point
const chartIcon = chartType === "bar" ? BarChart3 : LineChart;

if (!xAxisColumn) {
return <EmptyState message="Select an X-axis column to display the chart" />;
return <ChartBlankState icon={chartIcon} message="Select an X-axis column to display the chart" />;
}

if (yAxisColumns.length === 0) {
return <EmptyState message="Select a Y-axis column to display the chart" />;
return <ChartBlankState icon={chartIcon} message="Select a Y-axis column to display the chart" />;
}

if (rows.length === 0) {
return <EmptyState message="No data to display" />;
return <ChartBlankState icon={chartIcon} message="No data to display" />;
}

if (data.length === 0) {
return <EmptyState message="Unable to transform data for chart" />;
return <ChartBlankState icon={chartIcon} message="Unable to transform data for chart" />;
}

// Base x-axis props shared by all chart types
Expand Down Expand Up @@ -1113,12 +1116,3 @@ function createYAxisFormatter(data: Record<string, unknown>[], series: string[])
};
}

function EmptyState({ message }: { message: string }) {
return (
<div className="flex h-full min-h-[300px] items-center justify-center">
<Paragraph variant="small" className="text-text-dimmed">
{message}
</Paragraph>
</div>
);
}
Loading