Skip to content

Commit d111d46

Browse files
committed
Add filter to home page
1 parent 5bba05f commit d111d46

4 files changed

Lines changed: 93 additions & 1 deletion

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { ReactNode } from "react";
2+
3+
import { Icon } from "@/components/ui/icon";
4+
import {
5+
Tooltip,
6+
TooltipContent,
7+
TooltipTrigger,
8+
} from "@/components/ui/tooltip";
9+
import { ExistingFlags } from "@/flags";
10+
import { cn } from "@/lib/utils";
11+
12+
interface BetaFeatureWrapperProps {
13+
flagKey: keyof typeof ExistingFlags;
14+
children: ReactNode;
15+
className?: string;
16+
}
17+
18+
export function BetaFeatureWrapper({
19+
flagKey,
20+
children,
21+
className,
22+
}: BetaFeatureWrapperProps) {
23+
const flag = ExistingFlags[flagKey];
24+
25+
if (!flag) {
26+
return <>{children}</>;
27+
}
28+
29+
return (
30+
<div
31+
className={cn(
32+
"relative rounded-lg border-2 border-amber-400/50 bg-amber-50/30 dark:bg-amber-950/10",
33+
className,
34+
)}
35+
>
36+
<Tooltip>
37+
<TooltipTrigger asChild>
38+
<div className="absolute -right-2 -top-2 flex size-6 cursor-help items-center justify-center rounded-full border border-amber-400 bg-amber-100 dark:bg-amber-900">
39+
<Icon
40+
name="FlaskConical"
41+
size="sm"
42+
className="text-amber-600 dark:text-amber-400"
43+
/>
44+
</div>
45+
</TooltipTrigger>
46+
<TooltipContent side="top" className="max-w-xs">
47+
<div className="space-y-1">
48+
<p className="font-medium">{flag.name}</p>
49+
<p className="text-xs opacity-90">{flag.description}</p>
50+
<p className="text-xs italic opacity-70">Beta feature</p>
51+
</div>
52+
</TooltipContent>
53+
</Tooltip>
54+
<div className="p-3">{children}</div>
55+
</div>
56+
);
57+
}

src/routes/Home/Home.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useRef, useState } from "react";
22

33
import { PipelineSection, RunSection } from "@/components/Home";
4+
import { BetaFeatureWrapper } from "@/components/shared/BetaFeatureWrapper/BetaFeatureWrapper";
45
import { PipelineRunFiltersBar } from "@/components/shared/PipelineRunFiltersBar/PipelineRunFiltersBar";
56
import { useFlagValue } from "@/components/shared/Settings/useFlags";
67
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
@@ -40,7 +41,11 @@ const Home = () => {
4041
<PipelineSection />
4142
</TabsContent>
4243
<TabsContent value="runs" className="flex flex-col gap-4">
43-
{isFiltersBarEnabled && <PipelineRunFiltersBar />}
44+
{isFiltersBarEnabled && (
45+
<BetaFeatureWrapper flagKey="pipeline-run-filters-bar">
46+
<PipelineRunFiltersBar />
47+
</BetaFeatureWrapper>
48+
)}
4449
<RunSection onEmptyList={handlePipelineRunsEmpty} />
4550
</TabsContent>
4651
</Tabs>

src/types/pipelineRunFilters.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ export interface PipelineRunFilters {
2020
created_before?: string; // ISO datetime
2121
pipeline_name?: string;
2222
annotations?: AnnotationFilter[];
23+
sort_field?: "created_at" | "pipeline_name";
24+
sort_direction?: "asc" | "desc";
2325
}
26+
27+
export type SortField = NonNullable<PipelineRunFilters["sort_field"]>;
28+
export type SortDirection = NonNullable<PipelineRunFilters["sort_direction"]>;

src/utils/pipelineRunFilterUtils.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import type {
22
AnnotationFilter,
33
PipelineRunFilters,
4+
SortDirection,
5+
SortField,
46
} from "@/types/pipelineRunFilters";
57
import { isValidExecutionStatus } from "@/utils/executionStatus";
68

9+
const VALID_SORT_FIELDS: SortField[] = ["created_at", "pipeline_name"];
10+
const VALID_SORT_DIRECTIONS: SortDirection[] = ["asc", "desc"];
11+
712
export function isRecord(value: unknown): value is Record<string, unknown> {
813
return typeof value === "object" && value !== null && !Array.isArray(value);
914
}
@@ -16,6 +21,14 @@ function isValidAnnotationFilter(value: unknown): value is AnnotationFilter {
1621
);
1722
}
1823

24+
function isValidSortField(value: string): value is SortField {
25+
return (VALID_SORT_FIELDS as string[]).includes(value);
26+
}
27+
28+
function isValidSortDirection(value: string): value is SortDirection {
29+
return (VALID_SORT_DIRECTIONS as string[]).includes(value);
30+
}
31+
1932
/**
2033
* Validates and extracts filter fields from an unknown object.
2134
* Used for both parsed JSON and router-provided objects.
@@ -49,6 +62,18 @@ export function validateFilters(parsed: unknown): PipelineRunFilters {
4962
filters.annotations = validAnnotations;
5063
}
5164
}
65+
if (
66+
typeof parsed.sort_field === "string" &&
67+
isValidSortField(parsed.sort_field)
68+
) {
69+
filters.sort_field = parsed.sort_field;
70+
}
71+
if (
72+
typeof parsed.sort_direction === "string" &&
73+
isValidSortDirection(parsed.sort_direction)
74+
) {
75+
filters.sort_direction = parsed.sort_direction;
76+
}
5277

5378
return filters;
5479
}

0 commit comments

Comments
 (0)