diff --git a/src/client/features/audit/results/AuditResultsTableFilterLogic.ts b/src/client/features/audit/results/AuditResultsTableFilterLogic.ts
index e58676b..8df925b 100644
--- a/src/client/features/audit/results/AuditResultsTableFilterLogic.ts
+++ b/src/client/features/audit/results/AuditResultsTableFilterLogic.ts
@@ -163,3 +163,29 @@ function parseFilterNumber(value: string) {
const parsed = Number(trimmed);
return Number.isFinite(parsed) ? parsed : null;
}
+
+export function nullableNumberSort(
+ left: { getValue: (columnId: string) => number | null },
+ right: { getValue: (columnId: string) => number | null },
+ columnId: string,
+) {
+ const a = left.getValue(columnId);
+ const b = right.getValue(columnId);
+ if (a == null && b == null) return 0;
+ if (a == null) return 1;
+ if (b == null) return -1;
+ return a - b;
+}
+
+export function nullableStringSort(
+ left: { getValue: (columnId: string) => string | null },
+ right: { getValue: (columnId: string) => string | null },
+ columnId: string,
+) {
+ const a = left.getValue(columnId);
+ const b = right.getValue(columnId);
+ if (!a && !b) return 0;
+ if (!a) return 1;
+ if (!b) return -1;
+ return a.localeCompare(b);
+}
diff --git a/src/client/features/audit/results/AuditResultsTableFilters.tsx b/src/client/features/audit/results/AuditResultsTableFilters.tsx
index 039d84f..2e9b086 100644
--- a/src/client/features/audit/results/AuditResultsTableFilters.tsx
+++ b/src/client/features/audit/results/AuditResultsTableFilters.tsx
@@ -1,24 +1,24 @@
-import {
- EMPTY_PAGES_FILTERS,
- EMPTY_PERFORMANCE_FILTERS,
- type PagesFilters,
- type PerformanceFilters,
+import { RotateCcw, SlidersHorizontal } from "lucide-react";
+import type { ReactNode } from "react";
+import type {
+ PagesFilters,
+ PerformanceFilters,
} from "@/client/features/audit/results/AuditResultsTableFilterLogic";
export function PagesFilterBar({
filters,
onChange,
- resultCount,
- totalCount,
+ activeFilterCount,
+ onReset,
}: {
filters: PagesFilters;
onChange: (filters: PagesFilters) => void;
- resultCount: number;
- totalCount: number;
+ activeFilterCount: number;
+ onReset: () => void;
}) {
return (
-
-
+
+
+ onChange({ ...filters, missingAlt })}
+ options={[
+ ["all", "All"],
+ ["yes", "Missing alt"],
+ ["no", "No missing alt"],
+ ]}
+ />
+
+
- onChange({ ...filters, missingAlt })}
- options={[
- ["all", "All"],
- ["yes", "Missing alt"],
- ["no", "No missing alt"],
- ]}
- />
- onChange(EMPTY_PAGES_FILTERS)}
- />
-
+
);
}
export function PerformanceFilterBar({
filters,
onChange,
- resultCount,
- totalCount,
+ activeFilterCount,
+ onReset,
}: {
filters: PerformanceFilters;
onChange: (filters: PerformanceFilters) => void;
- resultCount: number;
- totalCount: number;
+ activeFilterCount: number;
+ onReset: () => void;
}) {
return (
-
-
+
+
+ onChange({ ...filters, maxLcpSeconds })}
+ />
+
+
onChange({ ...filters, minSeo })}
onMaxChange={(maxSeo) => onChange({ ...filters, maxSeo })}
/>
- onChange({ ...filters, maxLcpSeconds })}
- />
- onChange(EMPTY_PERFORMANCE_FILTERS)}
- />
-
+
);
}
@@ -150,6 +144,87 @@ export function EmptyTableMessage({ label }: { label: string }) {
return
{label}
;
}
+export function TableFilterToggle({
+ showFilters,
+ onToggle,
+ activeFilterCount,
+ resultCount,
+ totalCount,
+}: {
+ showFilters: boolean;
+ onToggle: () => void;
+ activeFilterCount: number;
+ resultCount: number;
+ totalCount: number;
+}) {
+ return (
+
+
+
+ {resultCount.toLocaleString()} of {totalCount.toLocaleString()}
+
+
+ );
+}
+
+export function countActiveFilters
>(
+ filters: TFilters,
+ emptyFilters: TFilters,
+) {
+ return Object.keys(filters).reduce((count, key) => {
+ const filterKey = key as keyof TFilters;
+ return filters[filterKey] !== emptyFilters[filterKey] ? count + 1 : count;
+ }, 0);
+}
+
+function FilterPanel({
+ activeFilterCount,
+ onReset,
+ children,
+}: {
+ activeFilterCount: number;
+ onReset: () => void;
+ children: ReactNode;
+}) {
+ return (
+
+
+
+
Refine results
+ {activeFilterCount > 0 ? (
+
+ {activeFilterCount} active
+
+ ) : null}
+
+
+
+ {children}
+
+ );
+}
+
function TextFilter({
label,
value,
@@ -164,12 +239,12 @@ function TextFilter({
onChange: (value: string) => void;
}) {
return (
-