Skip to content

Commit c61d39c

Browse files
committed
Show the table header along with the message if a query returns no results
1 parent ad7ba8e commit c61d39c

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

apps/webapp/app/components/code/TSQLResultsTable.tsx

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ChevronDownIcon, ChevronUpDownIcon, ChevronUpIcon } from "@heroicons/react/20/solid";
22
import type { OutputColumnMetadata } from "@internal/clickhouse";
33
import { IconFilter2, IconFilter2X, IconTable } from "@tabler/icons-react";
4-
import { ChartBlankState } from "../primitives/charts/ChartBlankState";
54
import { rankItem } from "@tanstack/match-sorter-utils";
65
import {
76
flexRender,
@@ -20,7 +19,7 @@ import {
2019
} from "@tanstack/react-table";
2120
import { useVirtualizer } from "@tanstack/react-virtual";
2221
import { formatDurationMilliseconds, MachinePresetName } from "@trigger.dev/core/v3";
23-
import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
22+
import { AlertCircle, ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
2423
import { forwardRef, memo, useEffect, useMemo, useRef, useState } from "react";
2524
import { EnvironmentLabel, EnvironmentSlug } from "~/components/environments/EnvironmentLabel";
2625
import { MachineLabelCombo } from "~/components/MachineLabelCombo";
@@ -38,6 +37,8 @@ import { useProject } from "~/hooks/useProject";
3837
import { cn } from "~/utils/cn";
3938
import { formatCurrencyAccurate, formatNumber } from "~/utils/numberFormatter";
4039
import { v3ProjectPath, v3RunPathFromFriendlyId } from "~/utils/pathBuilder";
40+
import { ChartBlankState } from "../primitives/charts/ChartBlankState";
41+
import { Paragraph } from "../primitives/Paragraph";
4142

4243
import { TextLink } from "../primitives/TextLink";
4344
import { InfoIconTooltip, SimpleTooltip } from "../primitives/Tooltip";
@@ -905,11 +906,14 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
905906
columns,
906907
prettyFormatting = true,
907908
sorting: defaultSorting = [],
909+
showHeaderOnEmpty = false,
908910
}: {
909911
rows: Record<string, unknown>[];
910912
columns: OutputColumnMetadata[];
911913
prettyFormatting?: boolean;
912914
sorting?: SortingState;
915+
/** When true, show column headers + "No results" on empty data. When false, show a blank state icon. */
916+
showHeaderOnEmpty?: boolean;
913917
}) {
914918
const tableContainerRef = useRef<HTMLDivElement>(null);
915919

@@ -979,7 +983,62 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
979983

980984
// Empty state
981985
if (rows.length === 0) {
982-
return <ChartBlankState icon={IconTable} message="No data to display" />;
986+
if (!showHeaderOnEmpty) {
987+
return <ChartBlankState icon={IconTable} message="No data to display" />;
988+
}
989+
990+
return (
991+
<div
992+
className="h-full min-h-0 w-full overflow-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
993+
style={{ position: "relative" }}
994+
>
995+
<table style={{ display: "grid" }}>
996+
<thead
997+
className="border-t border-grid-bright bg-background-bright after:absolute after:bottom-0 after:left-0 after:right-0 after:h-px after:bg-grid-bright"
998+
style={{
999+
display: "grid",
1000+
position: "sticky",
1001+
top: 0,
1002+
zIndex: 1,
1003+
}}
1004+
>
1005+
{table.getHeaderGroups().map((headerGroup) => (
1006+
<tr key={headerGroup.id} style={{ display: "flex", width: "100%" }}>
1007+
{headerGroup.headers.map((header) => {
1008+
const meta = header.column.columnDef.meta as ColumnMeta | undefined;
1009+
return (
1010+
<th
1011+
key={header.id}
1012+
className="group/header relative"
1013+
style={{
1014+
display: "flex",
1015+
width: header.getSize(),
1016+
}}
1017+
>
1018+
<HeaderCellContent alignment={meta?.alignment ?? "left"}>
1019+
{flexRender(header.column.columnDef.header, header.getContext())}
1020+
</HeaderCellContent>
1021+
</th>
1022+
);
1023+
})}
1024+
</tr>
1025+
))}
1026+
</thead>
1027+
<tbody style={{ display: "grid" }}>
1028+
<tr style={{ display: "flex", width: "100%" }}>
1029+
<td className="w-full px-3 py-6" colSpan={columns.length}>
1030+
<div className="flex items-center justify-center gap-1.5">
1031+
<AlertCircle className="size-5 text-text-dimmed/50" />
1032+
<Paragraph variant="small" className="text-text-dimmed">
1033+
This query returned no results
1034+
</Paragraph>
1035+
</div>
1036+
</td>
1037+
</tr>
1038+
</tbody>
1039+
</table>
1040+
</div>
1041+
);
9831042
}
9841043

9851044
return (

apps/webapp/app/components/metrics/QueryWidget.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ export type QueryWidgetProps = {
154154
onDelete?: () => void;
155155
/** Callback when duplicate is clicked. Receives the current data. */
156156
onDuplicate?: (data: QueryWidgetData) => void;
157+
/** When true, show table column headers even when there are no rows */
158+
showTableHeaderOnEmpty?: boolean;
157159
};
158160

159161
export function QueryWidget({
@@ -403,6 +405,7 @@ type QueryWidgetBodyProps = {
403405
isFullscreen: boolean;
404406
setIsFullscreen: (open: boolean) => void;
405407
isLoading: boolean;
408+
showTableHeaderOnEmpty?: boolean;
406409
};
407410

408411
function QueryWidgetBody({
@@ -413,6 +416,7 @@ function QueryWidgetBody({
413416
isFullscreen,
414417
setIsFullscreen,
415418
isLoading,
419+
showTableHeaderOnEmpty,
416420
}: QueryWidgetBodyProps) {
417421
const type = config.type;
418422

@@ -431,6 +435,7 @@ function QueryWidgetBody({
431435
columns={data.columns}
432436
prettyFormatting={config.prettyFormatting}
433437
sorting={config.sorting}
438+
showHeaderOnEmpty={showTableHeaderOnEmpty}
434439
/>
435440
<Dialog open={isFullscreen} onOpenChange={setIsFullscreen}>
436441
<DialogContent
@@ -444,6 +449,7 @@ function QueryWidgetBody({
444449
columns={data.columns}
445450
prettyFormatting={config.prettyFormatting}
446451
sorting={config.sorting}
452+
showHeaderOnEmpty={showTableHeaderOnEmpty}
447453
/>
448454
</div>
449455
</DialogContent>

apps/webapp/app/components/query/QueryEditor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ export function QueryEditor({
785785
<div className="overflow-hidden">
786786
<QueryWidget
787787
className="border-0"
788+
showTableHeaderOnEmpty
788789
title={
789790
<QueryTitle
790791
isTitleLoading={isTitleLoading}

0 commit comments

Comments
 (0)