From a96b436576f7071fd2bc2417a9947a8961519f99 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Sat, 20 Dec 2025 23:09:08 +0000 Subject: [PATCH] Added tabbed help --- apps/webapp/app/components/code/CodeBlock.tsx | 4 + .../route.tsx | 484 +++++++++++++++++- 2 files changed, 467 insertions(+), 21 deletions(-) diff --git a/apps/webapp/app/components/code/CodeBlock.tsx b/apps/webapp/app/components/code/CodeBlock.tsx index ea1d55e5d..efa144138 100644 --- a/apps/webapp/app/components/code/CodeBlock.tsx +++ b/apps/webapp/app/components/code/CodeBlock.tsx @@ -20,6 +20,8 @@ async function setup() { await import("prismjs/components/prism-json"); //@ts-ignore await import("prismjs/components/prism-typescript"); + //@ts-ignore + await import("prismjs/components/prism-sql.js"); } setup(); @@ -470,6 +472,8 @@ function HighlightCode({ import("prismjs/components/prism-json"), //@ts-ignore import("prismjs/components/prism-typescript"), + //@ts-ignore + import("prismjs/components/prism-sql.js"), ]).then(() => setIsLoaded(true)); }, []); diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx index 920b75d5a..f28c04349 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx @@ -1,4 +1,9 @@ -import { ArrowDownTrayIcon, ClipboardIcon, LightBulbIcon } from "@heroicons/react/20/solid"; +import { + ArrowDownTrayIcon, + ClipboardIcon, + LightBulbIcon, + PlayIcon, +} from "@heroicons/react/20/solid"; import type { OutputColumnMetadata } from "@internal/clickhouse"; import type { ColumnSchema } from "@internal/tsql"; import { Form, useNavigation } from "@remix-run/react"; @@ -13,8 +18,15 @@ import { z } from "zod"; import { ClockRotateLeftIcon } from "~/assets/icons/ClockRotateLeftIcon"; import { ExitIcon } from "~/assets/icons/ExitIcon"; import { AlphaTitle } from "~/components/AlphaBadge"; +import { CodeBlock } from "~/components/code/CodeBlock"; import { TSQLEditor } from "~/components/code/TSQLEditor"; import { TSQLResultsTable } from "~/components/code/TSQLResultsTable"; +import { + ClientTabs, + ClientTabsContent, + ClientTabsList, + ClientTabsTrigger, +} from "~/components/primitives/ClientTabs"; import { DateTime } from "~/components/primitives/DateTime"; import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; @@ -334,7 +346,13 @@ export default function Page() { max="500px" className="w-full" > - setShowHelpSidebar(false)} /> + setShowHelpSidebar(false)} + onTryExample={(exampleQuery, exampleScope) => { + setQuery(exampleQuery); + setScope(exampleScope); + }} + /> )} @@ -398,7 +416,67 @@ function ExportResultsButton({ ); } -function QueryHelpSidebar({ onClose }: { onClose: () => void }) { +// Example queries for the Examples tab +const exampleQueries: Array<{ + title: string; + description: string; + query: string; + scope: QueryScope; +}> = [ + { + title: "Failed runs by task (past 7 days)", + description: "Count of failed runs grouped by task identifier over the last 7 days.", + query: `SELECT + task_identifier, + count() AS failed_count +FROM runs +WHERE status = 'Failed' + AND created_at > now() - INTERVAL 7 DAY +GROUP BY task_identifier +ORDER BY failed_count DESC +LIMIT 20`, + scope: "environment", + }, + { + title: "Execution duration p50 by task (past 7d)", + description: "Median (50th percentile) execution duration for each task.", + query: `SELECT + task_identifier, + quantile(0.5)(execution_duration) AS p50_duration_ms +FROM runs +WHERE created_at > now() - INTERVAL 7 DAY + AND execution_duration IS NOT NULL +GROUP BY task_identifier +ORDER BY p50_duration_ms DESC +LIMIT 20`, + scope: "environment", + }, + { + title: "Most expensive 100 runs (past 7d)", + description: "Top 100 runs by compute cost over the last 7 days.", + query: `SELECT + run_id, + task_identifier, + status, + compute_cost, + usage_duration, + machine, + created_at +FROM runs +WHERE created_at > now() - INTERVAL 7 DAY +ORDER BY compute_cost DESC +LIMIT 100`, + scope: "environment", + }, +]; + +function QueryHelpSidebar({ + onClose, + onTryExample, +}: { + onClose: () => void; + onTryExample: (query: string, scope: QueryScope) => void; +}) { return (
@@ -415,25 +493,389 @@ function QueryHelpSidebar({ onClose }: { onClose: () => void }) { className="pl-[0.375rem]" />
-
- {querySchemas.map((table) => ( -
-
- {table.name} - {table.description && ( - - {table.description} - - )} -
-
- {Object.values(table.columns).map((col) => ( - - ))} -
+ + + + Writing TRQL + + + Table schema + + + Examples + + + + + + + + + + + + +
+ ); +} + +function TRQLGuideContent() { + return ( +
+ {/* Table of contents */} + + + {/* Basic queries */} +
+ Basic queries + + Select columns from a table. Use * to select all columns, + or list specific columns. + + + + Alias columns with AS: + + +
+ + {/* Filtering */} +
+ Filtering with WHERE + + Use comparison operators: =,{" "} + !=, <,{" "} + >, <=,{" "} + >= + + now() - INTERVAL 1 DAY`} + language="sql" + showLineNumbers={false} + showOpenInModal={false} + className="text-xs" + /> + + Other operators: + + +
+ + {/* Sorting & limiting */} +
+ Sorting & limiting + + Sort results with ORDER BY (ASC/DESC). Limit results with{" "} + LIMIT. + + +
+ + {/* Grouping */} +
+ Grouping & aggregation + + Use GROUP BY with aggregate functions. Filter groups with{" "} + HAVING. + + 10 +ORDER BY run_count DESC`} + language="sql" + showLineNumbers={false} + showOpenInModal={false} + className="text-xs" + /> +
+ + {/* Functions */} +
+ Available functions + +
+
+ + Aggregate functions + +
- ))} -
+ +
+ + String functions + + +
+ +
+ + Date/time functions + + +
+ +
+ + Conditional functions + + +
+ +
+ + Array functions + + +
+
+ +
+ ); +} + +function FunctionList({ + functions, +}: { + functions: Array<{ name: string; desc: string; example: string }>; +}) { + return ( +
+ {functions.map((fn) => ( +
+ {fn.name} + — {fn.desc} +
{fn.example}
+
+ ))} +
+ ); +} + +function TableSchemaContent() { + return ( +
+ {querySchemas.map((table) => ( +
+
+ {table.name} + {table.description && ( + + {table.description} + + )} +
+
+ {Object.values(table.columns).map((col) => ( + + ))} +
+
+ ))} +
+ ); +} + +function ExamplesContent({ + onTryExample, +}: { + onTryExample: (query: string, scope: QueryScope) => void; +}) { + return ( +
+ {exampleQueries.map((example) => ( +
+ {example.title} + + {example.description} + + + +
+ ))}
); }