Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
42e5b52
Data format for Metric layouts
matt-aitken Feb 1, 2026
9ad8bed
Refactored some code from the action into the service
matt-aitken Jan 29, 2026
50ecd78
QueryWidget that can be used on the Query page and on Metrics dashboards
matt-aitken Feb 1, 2026
e3ce3d8
Added loading state
matt-aitken Feb 1, 2026
b908073
Metric resource route
matt-aitken Feb 1, 2026
a9b2b8a
Scaffolding for built in metric page
matt-aitken Feb 1, 2026
922dda5
Very basic dashboard rendering
matt-aitken Feb 1, 2026
50f92dc
Metric errors and set concurrency limit
matt-aitken Feb 1, 2026
f4b3360
Time filtering and auto-reloading
matt-aitken Feb 1, 2026
ad7305e
Drag and drop styles working
matt-aitken Feb 2, 2026
2e4b550
Drag from all corners
matt-aitken Feb 2, 2026
19aff51
Shows resizing state when resizing
matt-aitken Feb 2, 2026
264ae1c
Don't allow text selection when resizing
matt-aitken Feb 2, 2026
129b088
Customised the style of the resize bg and handles
matt-aitken Feb 2, 2026
1f14fb6
Page for custom dashboard
matt-aitken Feb 2, 2026
e94b27b
description column added
matt-aitken Feb 2, 2026
340307c
Add metrics to the side menu, inc creating custom dashboards
matt-aitken Feb 2, 2026
81c6457
Add query table/chart to custom dashboard
matt-aitken Feb 2, 2026
989da4a
Editing and saving layouts working
matt-aitken Feb 2, 2026
0d854fd
Cancel button reverts the layout
matt-aitken Feb 2, 2026
1a600fc
SideMenu section for Metrics
matt-aitken Feb 2, 2026
92f2523
Fix for side menu collapsible bg
matt-aitken Feb 2, 2026
0c9bb22
Drag and resize without edit mode
matt-aitken Feb 4, 2026
b9c02c2
Delete dashboard implemented
matt-aitken Feb 4, 2026
212f605
Rename dashboard
matt-aitken Feb 4, 2026
a777bab
Separate components for rename/delete
matt-aitken Feb 4, 2026
3c14359
Metric add/edit chart
matt-aitken Feb 4, 2026
ab353ea
The data now gets passed through
matt-aitken Feb 4, 2026
a901cf5
Side menu collapsing improved
matt-aitken Feb 4, 2026
41bccff
Query add/edit from Metrics is a Sheet
matt-aitken Feb 4, 2026
d9e9a5c
Padding improvements
matt-aitken Feb 4, 2026
51776e9
More work on editing/saving
matt-aitken Feb 4, 2026
250d839
New revalidate
matt-aitken Feb 5, 2026
791d0b4
Added duplication and working on fixes
matt-aitken Feb 5, 2026
a1df4c1
Consolidate to a single resource route
matt-aitken Feb 5, 2026
1f7295f
Change to a sync for metrics changes
matt-aitken Feb 5, 2026
bc3cf0f
Reload widget when the query changes
matt-aitken Feb 5, 2026
98fd841
Fix for duplicated widgets updating improperly
matt-aitken Feb 5, 2026
03eb3a4
Renaming widgets
matt-aitken Feb 5, 2026
c3325b1
Added filters to metrics
matt-aitken Feb 5, 2026
537f000
Side menu: Metrics -> Insights
matt-aitken Feb 5, 2026
99e76e5
Fixed wiget popover width
matt-aitken Feb 5, 2026
d1a55e3
Fix TSQL fallback expression missing "in" operator support
samejr Feb 5, 2026
f0ab6d8
@trigger.dev/platform@1.0.23
matt-aitken Feb 5, 2026
424b734
Brighter + button to add a dashboard
samejr Feb 5, 2026
a35a991
Differentiated icon colors for the insights section
samejr Feb 5, 2026
3e5f34b
Nice left padding
samejr Feb 5, 2026
2d5292f
Nicer padding
samejr Feb 5, 2026
686d918
Adds rename dashboard to the triple dot menu
samejr Feb 5, 2026
edab60b
Add secondary variant style to the triple dot menu
samejr Feb 5, 2026
b79642a
Adds a classname to override the value in the filters
samejr Feb 5, 2026
3cbe4e5
Default wider task and queues popovers
samejr Feb 5, 2026
59d608e
Added limits
matt-aitken Feb 5, 2026
1ee80af
Improved save layout
matt-aitken Feb 5, 2026
5a44b44
Moves the add new dashboard button to the Metrics side menu item
samejr Feb 5, 2026
07e1ee9
Removed `any` from limits
matt-aitken Feb 5, 2026
4eb09ca
New metrics side menu icon colors
samejr Feb 5, 2026
45cc964
Fix tailwind config error
samejr Feb 6, 2026
7bb4c41
Adds connector lines for the custom dashboard icons
samejr Feb 6, 2026
7751296
Icon colour improvements
samejr Feb 6, 2026
ccdab0e
Reorderable custom dashboards
samejr Feb 6, 2026
a45d8ca
Fixes the side menu collapsing animation for Metrics items
samejr Feb 6, 2026
6804022
Smoother side menu item transition when expanding
samejr Feb 6, 2026
3d33ee5
Update the connector type on drag
samejr Feb 6, 2026
5469a4b
Improve the upgrade dashboard modal
samejr Feb 6, 2026
e29c1d7
Reorder hoverstate style tweak
samejr Feb 6, 2026
cc1b3c0
Click anywhere on the side menu header to collapse it
samejr Feb 6, 2026
7e5b70c
Prevent text selection when reordering side menu items
samejr Feb 6, 2026
df99ce0
Improved the delete dashboard modal
samejr Feb 6, 2026
ed796be
Add a nice blank state for the custom dashboard
samejr Feb 6, 2026
2b47e91
Fix edit menu items on a widget
samejr Feb 6, 2026
ee65ea3
Consistent button text
samejr Feb 6, 2026
166f23e
Make sure the height of the edit query page is 100%
samejr Feb 6, 2026
754eee1
icon improvements
samejr Feb 6, 2026
035e70a
Consistent icons
samejr Feb 7, 2026
d722094
Refactored the reordering so we can reuse it
matt-aitken Feb 8, 2026
1530900
WIP adding BigNumber chart type
matt-aitken Feb 8, 2026
e0ea070
Improved BigNumber config and styling
matt-aitken Feb 8, 2026
7bd7370
Auto-fit for BigNumber
matt-aitken Feb 8, 2026
5c9d995
Fix for useFetcher errors bubbling up to the web inspector
matt-aitken Feb 9, 2026
c954ae4
Added a title widget type and the ability to add them
matt-aitken Feb 9, 2026
0da1a8b
Fix for duplicating widget id clash
matt-aitken Feb 9, 2026
0a92f66
Rounded corners for the Sheet
samejr Feb 9, 2026
b5e0bf2
Remove Beta badge
samejr Feb 9, 2026
76403c7
Fixed history popover scrolling + improved the styling
samejr Feb 9, 2026
c81f6b9
Nicer query blank state
samejr Feb 9, 2026
58d097a
Match the AI example query buttons with the AskAI examples
samejr Feb 9, 2026
81ee7dc
Use ellipsis
samejr Feb 9, 2026
0039378
Style updates and remove the tool call displaying in the UI
samejr Feb 9, 2026
64c8a68
Fix rounded corners on the AI query panel
samejr Feb 9, 2026
1e3e7cc
Fixed rounded corners for the AI query panel
samejr Feb 9, 2026
7075454
WIP for adding timeBucket() function for auto-bin
matt-aitken Feb 9, 2026
5c236b9
Time bucketing using auto bins working
matt-aitken Feb 10, 2026
76852cd
Improved alias matching case insensitivity
matt-aitken Feb 10, 2026
df75003
Prevent text selection when resizing or dragging
matt-aitken Feb 10, 2026
21d8137
Fix for typecheck issues
matt-aitken Feb 10, 2026
6664b90
NIcer icons for add chart or text
samejr Feb 10, 2026
e482ff2
Prevent tooltips from collapsing the child when `isChild` is used
samejr Feb 10, 2026
ef9b5f6
Fixes the date/time button not showing the tooltip or icon
samejr Feb 10, 2026
12df146
Show the ResizableHandle horizontal hover line on top of the triple dots
samejr Feb 10, 2026
1796c2f
Remove TS go from settings, users can set it individually
matt-aitken Feb 10, 2026
a9e32fb
Adds shortcut key to Cancel buttons
samejr Feb 10, 2026
e544188
Date Time filters have secondary style cancel button for consistency
samejr Feb 10, 2026
2645d93
text-bright for the Scope filters
samejr Feb 10, 2026
0524baa
Nicer behaviour when the example prompts container scales down
samejr Feb 10, 2026
5a91dea
Only show the loading state when there's not data already
matt-aitken Feb 10, 2026
bb6dfb0
Unify button text and style
samejr Feb 10, 2026
1944a8a
Consistent button revealing on the widgets
samejr Feb 10, 2026
de9c2de
More consistent button reveals
samejr Feb 10, 2026
f31a133
asChild fix
samejr Feb 10, 2026
dc52750
Improves the add to dashboard modal
samejr Feb 10, 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
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@
"**/node_modules/**": true,
"packages/cli-v3/e2e": true
},
"vitest.disableWorkspaceWarning": true,
"typescript.experimental.useTsgo": false
"vitest.disableWorkspaceWarning": true
}
29 changes: 29 additions & 0 deletions apps/webapp/app/components/AlphaBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,32 @@ export function AlphaTitle({ children }: { children: React.ReactNode }) {
</>
);
}

export function BetaBadge({
inline = false,
className,
}: {
inline?: boolean;
className?: string;
}) {
return (
<SimpleTooltip
button={
<Badge variant="extra-small" className={cn(inline ? "inline-grid" : "", className)}>
Beta
</Badge>
}
content="This feature is in Beta."
disableHoverableContent
/>
);
}

export function BetaTitle({ children }: { children: React.ReactNode }) {
return (
<>
<span>{children}</span>
<BetaBadge />
</>
);
}
149 changes: 65 additions & 84 deletions apps/webapp/app/components/code/AIQueryInput.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { PencilSquareIcon, PlusIcon, SparklesIcon } from "@heroicons/react/20/solid";
import { CheckIcon, PencilSquareIcon, PlusIcon, XMarkIcon } from "@heroicons/react/20/solid";
import { AnimatePresence, motion } from "framer-motion";
import { Suspense, lazy, useCallback, useEffect, useRef, useState } from "react";
import { AISparkleIcon } from "~/assets/icons/AISparkleIcon";
import { Button } from "~/components/primitives/Buttons";
import { Spinner } from "~/components/primitives/Spinner";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import type { AITimeFilter } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types";
import { cn } from "~/utils/cn";

// Lazy load streamdown components to avoid SSR issues
const StreamdownRenderer = lazy(() =>
Expand All @@ -13,13 +19,6 @@ const StreamdownRenderer = lazy(() =>
),
}))
);
import { Button } from "~/components/primitives/Buttons";
import { Spinner } from "~/components/primitives/Spinner";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import type { AITimeFilter } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/types";
import { cn } from "~/utils/cn";

type StreamEventType =
| { type: "thinking"; content: string }
Expand Down Expand Up @@ -179,21 +178,7 @@ export function AIQueryInput({
setThinking((prev) => prev + event.content);
break;
case "tool_call":
if (event.tool === "setTimeFilter") {
setThinking((prev) => {
if (prev.trimEnd().endsWith("Setting time filter...")) {
return prev;
}
return prev + `\nSetting time filter...\n`;
});
} else {
setThinking((prev) => {
if (prev.trimEnd().endsWith("Validating query...")) {
return prev;
}
return prev + `\nValidating query...\n`;
});
}
// Tool calls are handled silently — no UI text needed
break;
case "time_filter":
// Apply time filter immediately when the AI sets it
Expand Down Expand Up @@ -262,13 +247,13 @@ export function AIQueryInput({
}, [error]);

return (
<div className="flex flex-col gap-3">
<div className="flex flex-col">
{/* Gradient border wrapper like the schedules AI input */}
<div
className="rounded-md p-px"
className="overflow-hidden rounded-md p-px"
style={{ background: "linear-gradient(to bottom right, #E543FF, #286399)" }}
>
<div className="overflow-hidden rounded-[5px] bg-background-bright">
<div className="overflow-hidden rounded-md bg-background-bright">
<form onSubmit={handleSubmit}>
<textarea
ref={textareaRef}
Expand Down Expand Up @@ -297,10 +282,10 @@ export function AIQueryInput({
variant="tertiary/small"
disabled={true}
LeadingIcon={Spinner}
className="pl-1.5"
className="pl-2"
iconSpacing="gap-1.5"
>
{mode === "edit" ? "Editing..." : "Generating..."}
{mode === "edit" ? "Editing" : "Generating"}
</Button>
) : (
<>
Expand Down Expand Up @@ -366,64 +351,60 @@ export function AIQueryInput({
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="rounded-md border border-grid-dimmed bg-charcoal-850 p-3">
<div className="mb-2 flex items-center justify-between">
<div className="flex items-center gap-2">
{isLoading ? (
<Spinner
color={{
background: "rgba(99, 102, 241, 0.3)",
foreground: "rgba(99, 102, 241, 1)",
}}
className="size-3"
/>
) : lastResult === "success" ? (
<div className="size-3 rounded-full bg-success" />
) : lastResult === "error" ? (
<div className="size-3 rounded-full bg-error" />
) : null}
<span className="text-xs font-medium text-text-dimmed">
{isLoading
? "AI is thinking..."
: lastResult === "success"
<div className="px-1">
<div className="rounded-b-lg border-x border-b border-grid-dimmed bg-charcoal-850 p-3 pb-1">
<div className="mb-1 flex items-center justify-between">
<div className="flex items-center gap-1">
{isLoading ? (
<Spinner className="size-4" />
) : lastResult === "success" ? (
<CheckIcon className="size-4 text-success" />
) : lastResult === "error" ? (
<XMarkIcon className="size-4 text-error" />
) : null}
<span className="text-xs font-medium text-text-dimmed">
{isLoading
? "AI is thinking…"
: lastResult === "success"
? "Query generated"
: lastResult === "error"
? "Generation failed"
: "AI response"}
</span>
? "Generation failed"
: "AI response"}
</span>
</div>
{isLoading ? (
<Button
variant="minimal/small"
onClick={() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
setIsLoading(false);
setShowThinking(false);
setThinking("");
}}
className="text-xs"
>
Cancel
</Button>
) : (
<Button
variant="minimal/small"
onClick={() => {
setShowThinking(false);
setThinking("");
}}
className="text-xs"
>
Dismiss
</Button>
)}
</div>
<div className="streamdown-container max-h-96 overflow-y-auto text-xs text-text-dimmed scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<Suspense fallback={<p className="whitespace-pre-wrap">{thinking}</p>}>
<StreamdownRenderer isAnimating={isLoading}>{thinking}</StreamdownRenderer>
</Suspense>
</div>
{isLoading ? (
<Button
variant="minimal/small"
onClick={() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
setIsLoading(false);
setShowThinking(false);
setThinking("");
}}
className="text-xs"
>
Cancel
</Button>
) : (
<Button
variant="minimal/small"
onClick={() => {
setShowThinking(false);
setThinking("");
}}
className="text-xs"
>
Dismiss
</Button>
)}
</div>
<div className="streamdown-container max-h-96 overflow-y-auto text-xs text-text-dimmed scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<Suspense fallback={<p className="whitespace-pre-wrap">{thinking}</p>}>
<StreamdownRenderer isAnimating={isLoading}>{thinking}</StreamdownRenderer>
</Suspense>
</div>
</div>
</motion.div>
Expand Down
123 changes: 54 additions & 69 deletions apps/webapp/app/components/code/ChartConfigPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,15 @@ import type { OutputColumnMetadata } from "@internal/clickhouse";
import { BarChart, LineChart, Plus, XIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { cn } from "~/utils/cn";
import { Header3 } from "../primitives/Headers";
import { Paragraph } from "../primitives/Paragraph";
import { Select, SelectItem } from "../primitives/Select";
import { Switch } from "../primitives/Switch";
import { Button } from "../primitives/Buttons";

export type ChartType = "bar" | "line";
export type SortDirection = "asc" | "desc";
export type AggregationType = "sum" | "avg" | "count" | "min" | "max";

export interface ChartConfiguration {
chartType: ChartType;
xAxisColumn: string | null;
yAxisColumns: string[];
groupByColumn: string | null;
stacked: boolean;
sortByColumn: string | null;
sortDirection: SortDirection;
aggregation: AggregationType;
}
import {
type AggregationType,
type ChartConfiguration,
type SortDirection,
} from "../metrics/QueryWidget";

export const defaultChartConfig: ChartConfiguration = {
chartType: "bar",
Expand Down Expand Up @@ -329,60 +318,58 @@ export function ChartConfigPanel({ columns, config, onChange, className }: Chart
) : (
<div className="flex flex-col gap-1.5">
{/* Always show at least one dropdown, even if yAxisColumns is empty */}
{(config.yAxisColumns.length === 0 ? [""] : config.yAxisColumns).map(
(col, index) => (
<div key={index} className="flex items-center gap-1">
<Select
value={col}
setValue={(value) => {
const newColumns = [...config.yAxisColumns];
if (value) {
// If this is a new slot (empty string), add it
if (index >= config.yAxisColumns.length) {
newColumns.push(value);
} else {
newColumns[index] = value;
}
} else if (index < config.yAxisColumns.length) {
newColumns.splice(index, 1);
{(config.yAxisColumns.length === 0 ? [""] : config.yAxisColumns).map((col, index) => (
<div key={index} className="flex items-center gap-1">
<Select
value={col}
setValue={(value) => {
const newColumns = [...config.yAxisColumns];
if (value) {
// If this is a new slot (empty string), add it
if (index >= config.yAxisColumns.length) {
newColumns.push(value);
} else {
newColumns[index] = value;
}
} else if (index < config.yAxisColumns.length) {
newColumns.splice(index, 1);
}
updateConfig({ yAxisColumns: newColumns });
}}
variant="tertiary/small"
placeholder="Select column"
items={yAxisOptions.filter(
(opt) => opt.value === col || !config.yAxisColumns.includes(opt.value)
)}
dropdownIcon
className="min-w-[140px] flex-1"
>
{(items) =>
items.map((item) => (
<SelectItem key={item.value} value={item.value}>
<span className="flex items-center gap-2">
<span>{item.label}</span>
<TypeBadge type={item.type} />
</span>
</SelectItem>
))
}
</Select>
{index > 0 && (
<button
type="button"
onClick={() => {
const newColumns = config.yAxisColumns.filter((_, i) => i !== index);
updateConfig({ yAxisColumns: newColumns });
}}
variant="tertiary/small"
placeholder="Select column"
items={yAxisOptions.filter(
(opt) => opt.value === col || !config.yAxisColumns.includes(opt.value)
)}
dropdownIcon
className="min-w-[140px] flex-1"
className="rounded p-1 text-text-dimmed hover:bg-charcoal-700 hover:text-text-bright"
title="Remove series"
>
{(items) =>
items.map((item) => (
<SelectItem key={item.value} value={item.value}>
<span className="flex items-center gap-2">
<span>{item.label}</span>
<TypeBadge type={item.type} />
</span>
</SelectItem>
))
}
</Select>
{index > 0 && (
<button
type="button"
onClick={() => {
const newColumns = config.yAxisColumns.filter((_, i) => i !== index);
updateConfig({ yAxisColumns: newColumns });
}}
className="rounded p-1 text-text-dimmed hover:bg-charcoal-700 hover:text-text-bright"
title="Remove series"
>
<XIcon className="h-3.5 w-3.5" />
</button>
)}
</div>
)
)}
<XIcon className="h-3.5 w-3.5" />
</button>
)}
</div>
))}

{/* Add another series button - only show when we have at least one series and not grouped */}
{config.yAxisColumns.length > 0 &&
Expand Down Expand Up @@ -439,9 +426,7 @@ export function ChartConfigPanel({ columns, config, onChange, className }: Chart
{/* Group By - disabled when multiple series are selected */}
<ConfigField label="Group by">
{config.yAxisColumns.length > 1 ? (
<span className="text-xs text-text-dimmed">
Not available with multiple series
</span>
<span className="text-xs text-text-dimmed">Not available with multiple series</span>
) : (
<Select
value={config.groupByColumn ?? "__none__"}
Expand Down
Loading