make filter icon required

This commit is contained in:
Cole Goldsmith 2025-09-30 13:29:43 -05:00
parent ef98a5a826
commit 60cb732ce2
7 changed files with 48 additions and 61 deletions

View file

@ -7,7 +7,6 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import {
File,
Book,
Scroll,
Library,
@ -32,13 +31,13 @@ import {
ShoppingCart,
ShoppingBag,
Check,
Plus,
Filter,
} from "lucide-react";
import { filterAccentClasses } from "./knowledge-filter-panel";
import { cn } from "@/lib/utils";
const ICON_MAP = {
file: File,
filter: Filter,
book: Book,
scroll: Scroll,
library: Library,
@ -87,21 +86,20 @@ const COLORS = [
export type FilterColor = (typeof COLORS)[number];
const colorSwatchClasses = {
zinc: "bg-muted-foreground text-accent-foreground",
pink: "bg-accent-pink-foreground text-accent-pink",
purple: "bg-accent-purple-foreground text-accent-purple",
indigo: "bg-accent-indigo-foreground text-accent-indigo",
emerald: "bg-accent-emerald-foreground text-accent-emerald",
amber: "bg-accent-amber-foreground text-accent-amber",
red: "bg-accent-red-foreground text-accent-red",
"": "bg-muted-foreground text-accent-foreground",
zinc: "bg-muted-foreground",
pink: "bg-accent-pink-foreground",
purple: "bg-accent-purple-foreground",
indigo: "bg-accent-indigo-foreground",
emerald: "bg-accent-emerald-foreground",
amber: "bg-accent-amber-foreground",
red: "bg-accent-red-foreground",
};
export interface FilterIconPopoverProps {
color: FilterColor;
iconKey?: IconKey | undefined;
iconKey: IconKey;
onColorChange: (c: FilterColor) => void;
onIconChange: (k: IconKey | undefined) => void;
onIconChange: (k: IconKey) => void;
triggerClassName?: string;
}
@ -118,13 +116,12 @@ export function FilterIconPopover({
<PopoverTrigger asChild>
<button
className={cn(
"h-10 w-10 min-w-10 min-h-10 rounded-sm flex items-center justify-center transition-colors",
filterAccentClasses[color || ""],
"h-10 w-10 min-w-10 min-h-10 rounded-md flex items-center justify-center transition-colors",
filterAccentClasses[color],
triggerClassName
)}
>
{Icon && <Icon className="h-5 w-5" />}
{!Icon && <Plus className="h-5 w-5" />}
</button>
</PopoverTrigger>
<PopoverContent className="w-80" align="start">
@ -136,8 +133,8 @@ export function FilterIconPopover({
type="button"
onClick={() => onColorChange(c)}
className={cn(
"flex items-center justify-center h-6 w-6 rounded-sm transition-colors",
colorSwatchClasses[c || ""]
"flex items-center justify-center h-6 w-6 rounded-sm transition-colors text-primary",
colorSwatchClasses[c]
)}
aria-label={c}
>
@ -153,18 +150,10 @@ export function FilterIconPopover({
<button
key={k}
type="button"
onClick={() => {
if (active) {
onIconChange(undefined);
} else {
onIconChange(k as IconKey);
}
}}
onClick={() => onIconChange(k as IconKey)}
className={
"h-8 w-8 inline-flex items-center hover:text-foreground justify-center rounded border " +
(active
? "border-muted-foreground text-foreground"
: "border-0 text-muted-foreground")
"h-8 w-8 inline-flex items-center hover:text-foreground justify-center rounded " +
(active ? "bg-muted text-primary" : "text-muted-foreground")
}
aria-label={k}
>

View file

@ -2,7 +2,7 @@
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Loader2, Plus, X } from "lucide-react";
import { Loader2, Plus } from "lucide-react";
import { cn } from "@/lib/utils";
import {
useGetFiltersSearchQuery,
@ -25,8 +25,8 @@ interface ParsedQueryData {
};
limit: number;
scoreThreshold: number;
color?: FilterColor;
icon?: IconKey;
color: FilterColor;
icon: IconKey;
}
interface KnowledgeFilterListProps {
@ -106,13 +106,13 @@ export function KnowledgeFilterList({
<div className="flex flex-col gap-1 flex-1 min-w-0">
<div className="flex items-center gap-2">
{(() => {
const parsed = parseQueryData(filter.query_data);
const parsed = parseQueryData(filter.query_data) as ParsedQueryData;
const Icon = iconKeyToComponent(parsed.icon);
return (
<div
className={cn(
"flex items-center justify-center w-5 h-5 rounded transition-colors",
filterAccentClasses[parsed.color || ""],
filterAccentClasses[parsed.color],
parsed.color === "zinc" &&
"group-hover:bg-background group-[.active]:bg-background"
)}

View file

@ -14,7 +14,11 @@ import { useDeleteFilter } from "@/app/api/mutations/useDeleteFilter";
import { useUpdateFilter } from "@/app/api/mutations/useUpdateFilter";
import { useCreateFilter } from "@/app/api/mutations/useCreateFilter";
import { useGetSearchAggregations } from "@/src/app/api/queries/useGetSearchAggregations";
import { FilterIconPopover, IconKey } from "@/components/filter-icon-popover";
import {
FilterColor,
FilterIconPopover,
IconKey,
} from "@/components/filter-icon-popover";
interface FacetBucket {
key: string;
@ -28,18 +32,14 @@ interface AvailableFacets {
connector_types: FacetBucket[];
}
export const filterAccentClasses: Record<
"zinc" | "pink" | "purple" | "indigo" | "emerald" | "amber" | "red" | "",
string
> = {
zinc: "bg-accent text-muted-foreground",
export const filterAccentClasses: Record<FilterColor, string> = {
zinc: "bg-muted text-muted-foreground",
pink: "bg-accent-pink text-accent-pink-foreground",
purple: "bg-accent-purple text-accent-purple-foreground",
indigo: "bg-accent-indigo text-accent-indigo-foreground",
emerald: "bg-accent-emerald text-accent-emerald-foreground",
amber: "bg-accent-amber text-accent-amber-foreground",
red: "bg-accent-red text-accent-red-foreground",
"": "bg-accent text-muted-foreground",
};
export function KnowledgeFilterPanel() {
@ -59,10 +59,8 @@ export function KnowledgeFilterPanel() {
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [isSaving, setIsSaving] = useState(false);
const [color, setColor] = useState<
"zinc" | "pink" | "purple" | "indigo" | "emerald" | "amber" | "red"
>("zinc");
const [iconKey, setIconKey] = useState<IconKey>();
const [color, setColor] = useState<FilterColor>("zinc");
const [iconKey, setIconKey] = useState<IconKey>("filter");
// Filter configuration states (mirror search page exactly)
const [query, setQuery] = useState("");
@ -107,8 +105,8 @@ export function KnowledgeFilterPanel() {
setScoreThreshold(parsedFilterData.scoreThreshold || 0);
setName(selectedFilter.name);
setDescription(selectedFilter.description || "");
setColor(parsedFilterData.color || "zinc");
setIconKey(parsedFilterData.icon as IconKey);
setColor(parsedFilterData.color);
setIconKey(parsedFilterData.icon);
}
}, [selectedFilter, parsedFilterData]);
@ -121,8 +119,8 @@ export function KnowledgeFilterPanel() {
setScoreThreshold(parsedFilterData.scoreThreshold || 0);
setName("");
setDescription("");
setColor(parsedFilterData.color || "zinc");
setIconKey(parsedFilterData.icon as IconKey);
setColor(parsedFilterData.color);
setIconKey(parsedFilterData.icon);
}
}, [createMode, parsedFilterData]);
@ -268,7 +266,7 @@ export function KnowledgeFilterPanel() {
color={color}
iconKey={iconKey}
onColorChange={setColor}
onIconChange={(k) => setIconKey(k)}
onIconChange={setIconKey}
/>
<Input
id="filter-name"

View file

@ -2116,7 +2116,7 @@ function ChatPage() {
<div className="flex items-center gap-2 px-4 pt-3 pb-1">
<span
className={`inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-medium transition-colors ${
filterAccentClasses[parsedFilterData?.color || ""]
filterAccentClasses[parsedFilterData?.color || "zinc"]
}`}
>
@filter:{selectedFilter.name}

View file

@ -27,6 +27,8 @@
--accent-foreground: 0 0% 0%;
--destructive: 0 72% 51%;
--destructive-foreground: 0 0% 100%;
--warning: 48, 96%, 89%;
--warning-foreground: 26, 90%, 37%;
--border: 240 4.8% 95.9%;
--input: 240 6% 90%;
--ring: 0 0% 0%;
@ -49,10 +51,6 @@
--component-icon: #d8598a;
--flow-icon: #2f67d0;
/* Warning */
--warning: 48 96.6% 76.7%;
--warning-foreground: 240 6% 10%;
--radius: 0.5rem;
}
@ -75,6 +73,8 @@
--accent-foreground: 0 0% 100%;
--destructive: 0 84% 60%;
--destructive-foreground: 0 0% 100%;
--warning: 22, 78%, 26%;
--warning-foreground: 46, 97%, 65%;
--border: 240 3.7% 15.9%;
--input: 240 5% 34%;
--ring: 0 0% 100%;

View file

@ -256,7 +256,7 @@ function SearchPage() {
{selectedFilter?.name && (
<div
className={`flex items-center gap-1 h-full px-1.5 py-0.5 rounded max-w-[300px] ${
filterAccentClasses[parsedFilterData?.color || ""]
filterAccentClasses[parsedFilterData?.color || "zinc"]
}`}
>
<span className="truncate">{selectedFilter?.name}</span>

View file

@ -1,5 +1,6 @@
"use client";
import { FilterColor, IconKey } from "@/components/filter-icon-popover";
import React, {
createContext,
type ReactNode,
@ -27,9 +28,8 @@ export interface ParsedQueryData {
};
limit: number;
scoreThreshold: number;
// Optional visual metadata for UI
color?: "zinc" | "pink" | "purple" | "indigo" | "emerald" | "amber" | "red";
icon?: string; // lucide icon key we store in the UI mapping
color: FilterColor;
icon: IconKey;
}
interface KnowledgeFilterContextType {
@ -54,7 +54,7 @@ export function useKnowledgeFilter() {
const context = useContext(KnowledgeFilterContext);
if (context === undefined) {
throw new Error(
"useKnowledgeFilter must be used within a KnowledgeFilterProvider",
"useKnowledgeFilter must be used within a KnowledgeFilterProvider"
);
}
return context;
@ -127,7 +127,7 @@ export function KnowledgeFilterProvider({
limit: 10,
scoreThreshold: 0,
color: "zinc",
icon: "Filter",
icon: "filter",
});
setIsPanelOpen(true);
};