Skip to content

Commit a684427

Browse files
authored
Add some color to the filters and update occupancy links (#689)
1 parent a484225 commit a684427

File tree

6 files changed

+48
-9
lines changed

6 files changed

+48
-9
lines changed

src/ui/src/components/data-table/data-table.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ export interface DataTableProps<TData, TSectionMeta = unknown> {
8080
onRowDoubleClick?: (row: TData) => void;
8181
/** For middle-click: returns URL for new tab, or undefined to call onRowClick */
8282
getRowHref?: (row: TData) => string | undefined;
83+
/** Native tooltip (title attribute) for a row, shown on hover */
84+
getRowTitle?: (row: TData) => string | undefined;
8385
selectedRowId?: string;
8486
/** Override default header cell padding/styling for all columns (default: "px-4 py-3") */
8587
headerClassName?: string;
@@ -141,6 +143,7 @@ function DataTableInner<TData, TSectionMeta = unknown>({
141143
onFocusedRowChange,
142144
onRowDoubleClick,
143145
getRowHref,
146+
getRowTitle,
144147
selectedRowId,
145148
headerClassName: tableHeaderClassName,
146149
theadClassName,
@@ -565,6 +568,7 @@ function DataTableInner<TData, TSectionMeta = unknown>({
565568
onRowClick={onRowClick}
566569
onRowDoubleClick={onRowDoubleClick}
567570
getRowHref={getRowHref}
571+
getRowTitle={getRowTitle}
568572
selectedRowId={selectedRowId}
569573
getRowId={getRowId}
570574
rowClassName={rowClassName}

src/ui/src/components/data-table/virtual-table-body.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export interface VirtualTableBodyProps<TData, TSectionMeta = unknown> {
3838
onRowDoubleClick?: (item: TData, index: number) => void;
3939
/** Returns URL for middle-click "open in new tab", or undefined to fall back to onRowClick */
4040
getRowHref?: (item: TData) => string | undefined;
41+
/** Native tooltip (title attribute) for a row, shown on hover */
42+
getRowTitle?: (item: TData) => string | undefined;
4143
selectedRowId?: string;
4244
getRowId?: (item: TData) => string;
4345
rowClassName?: string | ((item: TData, index: number) => string);
@@ -61,6 +63,7 @@ function VirtualTableBodyInner<TData, TSectionMeta = unknown>({
6163
onRowClick,
6264
onRowDoubleClick,
6365
getRowHref,
66+
getRowTitle,
6467
selectedRowId,
6568
getRowId,
6669
rowClassName,
@@ -113,6 +116,10 @@ function VirtualTableBodyInner<TData, TSectionMeta = unknown>({
113116
(e: React.MouseEvent<HTMLTableSectionElement>) => {
114117
if (!onRowClick && !getRowHref) return;
115118

119+
// If the user dragged to select text, don't trigger navigation.
120+
const selection = window.getSelection();
121+
if (selection && selection.toString().length > 0) return;
122+
116123
// Skip the second click of a multi-click sequence when dblclick is handled,
117124
// otherwise two competing startViewTransition calls cause the dblclick's
118125
// router.push to never execute.
@@ -276,6 +283,7 @@ function VirtualTableBodyInner<TData, TSectionMeta = unknown>({
276283
data-interactive={isInteractive || undefined}
277284
aria-rowindex={virtualRow.index + 2}
278285
aria-selected={isSelected ? true : undefined}
286+
title={getRowTitle?.(rowData)}
279287
tabIndex={tabIndex}
280288
className={cn(
281289
"data-table-row border-b border-zinc-200 dark:border-zinc-800",

src/ui/src/components/filter-bar/filter-bar-dropdown.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,10 @@ function SuggestionItemInner<T>({ suggestion, onSelect }: SuggestionItemProps<T>
446446
{suggestion.type === "field" ? (
447447
<span className="fb-suggestion-field-prefix">{suggestion.label}</span>
448448
) : (
449-
<span>{suggestion.label}</span>
449+
<span>
450+
<span className="fb-suggestion-field-prefix">{suggestion.field.prefix}</span>
451+
{suggestion.label.slice(suggestion.field.prefix.length)}
452+
</span>
450453
)}
451454
</span>
452455
{suggestion.hint && <span className="text-muted-foreground ml-2 shrink-0 text-xs">{suggestion.hint}</span>}

src/ui/src/features/occupancy/components/occupancy-column-defs.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function PriorityBadge({ value, colorClass }: { value: number; colorClass: strin
6969
);
7070
}
7171

72-
function buildWorkflowsUrl(row: OccupancyFlatRow, groupBy: OccupancyGroupBy, searchChips: SearchChip[]): string {
72+
export function buildWorkflowsUrl(row: OccupancyFlatRow, groupBy: OccupancyGroupBy, searchChips: SearchChip[]): string {
7373
const params: string[] = [];
7474
if (row._type === "parent") {
7575
params.push(`f=${groupBy}:${encodeURIComponent(row.key)}`);
@@ -200,7 +200,8 @@ export function createOccupancyColumns(
200200
return (
201201
<Link
202202
href={href}
203-
className="hover:text-primary pl-2 text-sm text-zinc-600 underline decoration-zinc-400/50 underline-offset-2 transition-colors hover:decoration-current dark:text-zinc-400"
203+
className="pl-2 text-sm text-zinc-600 dark:text-zinc-400"
204+
tabIndex={-1}
204205
onClick={(e) => e.stopPropagation()}
205206
>
206207
{original.key}

src/ui/src/features/occupancy/components/occupancy-data-table.tsx

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"use client";
1818

1919
import { useMemo, useCallback, memo } from "react";
20+
import { useRouter } from "next/navigation";
2021
import { DataTable } from "@/components/data-table/data-table";
2122
import { TableEmptyState } from "@/components/data-table/table-empty-state";
2223
import { TableLoadingSkeleton, TableErrorState } from "@/components/data-table/table-states";
@@ -31,12 +32,11 @@ import {
3132
asOccupancyColumnIds,
3233
OCCUPANCY_COLUMN_SIZE_CONFIG,
3334
} from "@/features/occupancy/lib/occupancy-columns";
34-
import { createOccupancyColumns } from "@/features/occupancy/components/occupancy-column-defs";
35+
import { createOccupancyColumns, buildWorkflowsUrl } from "@/features/occupancy/components/occupancy-column-defs";
3536
import { useOccupancyTableStore } from "@/features/occupancy/stores/occupancy-table-store";
3637
import "@/features/occupancy/styles/occupancy.css";
3738

3839
const FIXED_COLUMNS = Array.from(MANDATORY_COLUMN_IDS);
39-
const isOccupancyRowInteractive = (row: OccupancyFlatRow) => row._type === "parent";
4040

4141
function flattenForTable(groups: OccupancyGroup[], expandedKeys: Set<string>): OccupancyFlatRow[] {
4242
return groups.flatMap((group, groupIndex) => {
@@ -88,6 +88,7 @@ export const OccupancyDataTable = memo(function OccupancyDataTable({
8888
error,
8989
onRetry,
9090
}: OccupancyDataTableProps) {
91+
const router = useRouter();
9192
const compactMode = useCompactMode();
9293
const rowHeight = compactMode ? TABLE_ROW_HEIGHTS.COMPACT : TABLE_ROW_HEIGHTS.NORMAL;
9394

@@ -114,9 +115,30 @@ export const OccupancyDataTable = memo(function OccupancyDataTable({
114115

115116
const handleRowClick = useCallback(
116117
(row: OccupancyFlatRow) => {
117-
if (isOccupancyRowInteractive(row)) onToggleExpand(row.key);
118+
if (row._type === "parent") {
119+
onToggleExpand(row.key);
120+
} else {
121+
router.push(buildWorkflowsUrl(row, groupBy, searchChips));
122+
}
118123
},
119-
[onToggleExpand],
124+
[onToggleExpand, router, groupBy, searchChips],
125+
);
126+
127+
const getRowHref = useCallback(
128+
(row: OccupancyFlatRow) => {
129+
if (row._type === "child") return buildWorkflowsUrl(row, groupBy, searchChips);
130+
return undefined;
131+
},
132+
[groupBy, searchChips],
133+
);
134+
135+
const getRowTitle = useCallback(
136+
(row: OccupancyFlatRow) => {
137+
if (row._type !== "child") return undefined;
138+
if (groupBy === "pool") return `View ${row.key}'s workflows`;
139+
return `View workflows for ${row.key}`;
140+
},
141+
[groupBy],
120142
);
121143

122144
// Zebra striping keyed on group index so parent + children share the same stripe
@@ -171,8 +193,9 @@ export const OccupancyDataTable = memo(function OccupancyDataTable({
171193
isLoading={isLoading}
172194
emptyContent={emptyContent}
173195
onRowClick={handleRowClick}
196+
getRowHref={getRowHref}
197+
getRowTitle={getRowTitle}
174198
rowClassName={rowClassName}
175-
isRowInteractive={isOccupancyRowInteractive}
176199
/>
177200
</div>
178201
);

src/ui/src/features/occupancy/styles/occupancy.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
}
3636

3737
.occupancy-row--child {
38-
@apply cursor-default text-sm;
38+
@apply cursor-pointer text-sm;
3939
}
4040

4141
.occupancy-row:not(.occupancy-row--child):hover {

0 commit comments

Comments
 (0)