fix accent color variables

This commit is contained in:
Cole Goldsmith 2025-09-30 11:22:37 -05:00
parent 90639d9010
commit 70a75ee63d
7 changed files with 154 additions and 232 deletions

View file

@ -1,78 +1,78 @@
"use client";
import React, { type SVGProps } from "react";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Filter as FilterIcon,
Star,
File,
Book,
FileText,
Folder,
Globe,
Calendar,
User,
Users,
Tag,
Briefcase,
Building2,
Cog,
Scroll,
Library,
Map,
FileImage,
Layers3,
Database,
Cpu,
Bot,
MessageSquare,
Search,
Folder,
Archive,
MessagesSquare,
SquareStack,
Ghost,
Gem,
Swords,
Bolt,
Shield,
Lock,
Key,
Link,
Mail,
Phone,
Hammer,
Globe,
HardDrive,
Upload,
Cable,
ShoppingCart,
ShoppingBag,
Check,
Plus,
} from "lucide-react";
import { filterAccentClasses } from "./knowledge-filter-panel";
import { cn } from "@/lib/utils";
const ICON_MAP = {
Filter: FilterIcon,
Star,
Book,
FileText,
Folder,
Globe,
Calendar,
User,
Users,
Tag,
Briefcase,
Building2,
Cog,
Database,
Cpu,
Bot,
MessageSquare,
Search,
Shield,
Lock,
Key,
Link,
Mail,
Phone,
file: File,
book: Book,
scroll: Scroll,
library: Library,
map: Map,
image: FileImage,
layers3: Layers3,
database: Database,
folder: Folder,
archive: Archive,
messagesSquare: MessagesSquare,
squareStack: SquareStack,
ghost: Ghost,
gem: Gem,
swords: Swords,
bolt: Bolt,
shield: Shield,
hammer: Hammer,
globe: Globe,
hardDrive: HardDrive,
upload: Upload,
cable: Cable,
shoppingCart: ShoppingCart,
shoppingBag: ShoppingBag,
} as const;
export type IconKey = keyof typeof ICON_MAP;
function iconKeyToComponent(
key: string
): React.ComponentType<SVGProps<SVGSVGElement>> {
export function iconKeyToComponent(
key?: string
): React.ComponentType<SVGProps<SVGSVGElement>> | undefined {
if (!key) return undefined;
return (
(ICON_MAP as Record<string, React.ComponentType<SVGProps<SVGSVGElement>>>)[
key
] || FilterIcon
);
ICON_MAP as Record<string, React.ComponentType<SVGProps<SVGSVGElement>>>
)[key];
}
const COLORS = [
@ -87,21 +87,21 @@ const COLORS = [
export type FilterColor = (typeof COLORS)[number];
const colorSwatchClasses = {
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",
"": "bg-muted-foreground",
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",
};
export interface FilterIconPopoverProps {
color: FilterColor;
iconKey: IconKey | string;
iconKey?: IconKey | undefined;
onColorChange: (c: FilterColor) => void;
onIconChange: (k: IconKey) => void;
onIconChange: (k: IconKey | undefined) => void;
triggerClassName?: string;
}
@ -116,56 +116,55 @@ export function FilterIconPopover({
return (
<Popover>
<PopoverTrigger asChild>
<Button
type="button"
variant="outline"
size="icon"
className={"h-8 w-8 p-0 " + (triggerClassName || "")}
<button
className={cn(
"h-10 w-10 min-w-10 min-h-10 rounded-sm flex items-center justify-center transition-colors",
filterAccentClasses[color || ""],
triggerClassName
)}
>
<span
className={
filterAccentClasses[color || ""] +
" inline-flex items-center justify-center rounded h-6 w-6"
}
>
<Icon className="h-3.5 w-3.5" />
</span>
</Button>
{Icon && <Icon className="h-5 w-5" />}
{!Icon && <Plus className="h-5 w-5" />}
</button>
</PopoverTrigger>
<PopoverContent className="w-80" align="start">
<div className="space-y-3">
<div className="space-y-4">
<div className="grid grid-cols-7 items-center gap-2">
{COLORS.map((c) => (
<button
key={c}
type="button"
onClick={() => onColorChange(c)}
className={
"flex items-center justify-center h-6 w-6 rounded-sm transition-colors " +
className={cn(
"flex items-center justify-center h-6 w-6 rounded-sm transition-colors",
colorSwatchClasses[c || ""]
}
)}
aria-label={c}
>
{c === color && <Check className="h-3.5 w-3.5" />}
</button>
))}
</div>
<div className="text-xs font-medium text-muted-foreground mt-2">
Icon
</div>
<div className="grid grid-cols-6 gap-2">
{(Object.keys(ICON_MAP) as IconKey[]).map((k) => {
const OptIcon = ICON_MAP[k];
{Object.keys(ICON_MAP).map((k: string) => {
const OptIcon = ICON_MAP[k as IconKey];
const active = iconKey === k;
return (
<button
key={k}
type="button"
onClick={() => onIconChange(k)}
onClick={() => {
if (active) {
onIconChange(undefined);
} else {
onIconChange(k as IconKey);
}
}}
className={
"h-8 w-8 inline-flex items-center justify-center rounded border " +
(active ? "border-foreground" : "border-border")
"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")
}
aria-label={k}
>

View file

@ -2,73 +2,19 @@
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Filter as FilterIcon,
Loader2,
Plus,
X,
Star,
Book,
FileText,
Folder,
Globe,
Calendar,
User,
Users,
Tag,
Briefcase,
Building2,
Cog,
Database,
Cpu,
Bot,
MessageSquare,
Search,
Shield,
Lock,
Key,
Link,
Mail,
Phone,
} from "lucide-react";
import { Loader2, Plus, X } from "lucide-react";
import { cn } from "@/lib/utils";
import {
useGetFiltersSearchQuery,
type KnowledgeFilter,
} from "@/src/app/api/queries/useGetFiltersSearchQuery";
import { useKnowledgeFilter } from "@/src/contexts/knowledge-filter-context";
import type { SVGProps } from "react";
const ICON_MAP = {
Filter: FilterIcon,
Star,
Book,
FileText,
Folder,
Globe,
Calendar,
User,
Users,
Tag,
Briefcase,
Building2,
Cog,
Database,
Cpu,
Bot,
MessageSquare,
Search,
Shield,
Lock,
Key,
Link,
Mail,
Phone,
} as const;
function iconKeyToComponent(key: string): React.ComponentType<SVGProps<SVGSVGElement>> {
return (ICON_MAP as Record<string, React.ComponentType<SVGProps<SVGSVGElement>>>)[key] || FilterIcon;
}
import {
FilterColor,
IconKey,
iconKeyToComponent,
} from "./filter-icon-popover";
import { filterAccentClasses } from "./knowledge-filter-panel";
interface ParsedQueryData {
query: string;
@ -79,8 +25,8 @@ interface ParsedQueryData {
};
limit: number;
scoreThreshold: number;
color?: "zinc" | "pink" | "purple" | "indigo" | "emerald" | "amber" | "red";
icon?: string;
color?: FilterColor;
icon?: IconKey;
}
interface KnowledgeFilterListProps {
@ -103,6 +49,10 @@ export function KnowledgeFilterList({
const filters = data || [];
const handleFilterSelect = (filter: KnowledgeFilter) => {
if (filter.id === selectedFilter?.id) {
onFilterSelect(null);
return;
}
onFilterSelect(filter);
};
@ -150,35 +100,24 @@ export function KnowledgeFilterList({
className={cn(
"flex items-center gap-3 px-3 py-2 w-full rounded-lg hover:bg-accent hover:text-accent-foreground cursor-pointer group transition-colors",
selectedFilter?.id === filter.id &&
"bg-accent text-accent-foreground"
"active bg-accent text-accent-foreground"
)}
>
<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 color = (parsed.color || "zinc") as
| "zinc"
| "pink"
| "purple"
| "indigo"
| "emerald"
| "amber"
| "red";
const Icon = iconKeyToComponent(parsed.icon || "Filter");
const colorMap = {
zinc: "bg-zinc-500/20 text-zinc-500",
pink: "bg-pink-500/20 text-pink-500",
purple: "bg-purple-500/20 text-purple-500",
indigo: "bg-indigo-500/20 text-indigo-500",
emerald: "bg-emerald-500/20 text-emerald-500",
amber: "bg-amber-500/20 text-amber-500",
red: "bg-red-500/20 text-red-500",
} as const;
const colorClasses = colorMap[color];
const Icon = iconKeyToComponent(parsed.icon);
return (
<div className={`flex items-center justify-center ${colorClasses} w-5 h-5 rounded`}>
<Icon className="h-3 w-3" />
<div
className={cn(
"flex items-center justify-center w-5 h-5 rounded transition-colors",
filterAccentClasses[parsed.color || ""],
parsed.color === "zinc" &&
"group-hover:bg-background group-[.active]:bg-background"
)}
>
{Icon && <Icon className="h-3 w-3" />}
</div>
);
})()}
@ -187,19 +126,19 @@ export function KnowledgeFilterList({
</div>
</div>
{filter.description && (
<div className="text-xs text-muted-foreground group-hover:text-accent-foreground/70 line-clamp-2">
<div className="text-xs text-muted-foreground line-clamp-2">
{filter.description}
</div>
)}
<div className="flex items-center gap-2">
<div className="text-xs text-muted-foreground group-hover:text-accent-foreground/70">
<div className="text-xs text-muted-foreground">
{new Date(filter.created_at).toLocaleDateString(undefined, {
month: "short",
day: "numeric",
year: "numeric",
})}
</div>
<span className="text-xs bg-muted text-muted-foreground px-1 py-0.5 rounded-sm">
<span className="text-xs bg-muted text-muted-foreground px-1 py-0.5 rounded-sm group-hover:bg-background group-[.active]:bg-background transition-colors">
{(() => {
const dataSources = parseQueryData(filter.query_data)
.filters.data_sources;
@ -210,19 +149,6 @@ export function KnowledgeFilterList({
</span>
</div>
</div>
{selectedFilter?.id === filter.id && (
<Button
variant="ghost"
size="sm"
className="px-0"
onClick={(e) => {
e.stopPropagation();
onFilterSelect(null);
}}
>
<X className="h-4 w-4 flex-shrink-0 opacity-0 group-hover:opacity-100 text-muted-foreground" />
</Button>
)}
</div>
))
)}

View file

@ -14,7 +14,7 @@ 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 } from "@/components/filter-icon-popover";
import { FilterIconPopover, IconKey } from "@/components/filter-icon-popover";
interface FacetBucket {
key: string;
@ -32,14 +32,14 @@ export const filterAccentClasses: Record<
"zinc" | "pink" | "purple" | "indigo" | "emerald" | "amber" | "red" | "",
string
> = {
zinc: "bg-accent text-accent-foreground",
zinc: "bg-accent 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-accent-foreground",
"": "bg-accent text-muted-foreground",
};
export function KnowledgeFilterPanel() {
@ -62,7 +62,7 @@ export function KnowledgeFilterPanel() {
const [color, setColor] = useState<
"zinc" | "pink" | "purple" | "indigo" | "emerald" | "amber" | "red"
>("zinc");
const [iconKey, setIconKey] = useState<string>("Filter");
const [iconKey, setIconKey] = useState<IconKey>();
// Filter configuration states (mirror search page exactly)
const [query, setQuery] = useState("");
@ -108,7 +108,7 @@ export function KnowledgeFilterPanel() {
setName(selectedFilter.name);
setDescription(selectedFilter.description || "");
setColor(parsedFilterData.color || "zinc");
setIconKey(parsedFilterData.icon || "Filter");
setIconKey(parsedFilterData.icon as IconKey);
}
}, [selectedFilter, parsedFilterData]);
@ -122,7 +122,7 @@ export function KnowledgeFilterPanel() {
setName("");
setDescription("");
setColor(parsedFilterData.color || "zinc");
setIconKey(parsedFilterData.icon || "Filter");
setIconKey(parsedFilterData.icon as IconKey);
}
}, [createMode, parsedFilterData]);

View file

@ -179,7 +179,7 @@ export const useGetSearchQuery = (
const queryResult = useQuery(
{
queryKey: ["search", effectiveQuery],
queryKey: ["search", queryData],
placeholderData: (prev) => prev,
queryFn: getFiles,
...options,

View file

@ -32,18 +32,18 @@
--ring: 0 0% 0%;
--placeholder-foreground: 240 5% 65%;
--accent-amber: 48 96% 88%; /* amber-100 #fef3c7 */
--accent-amber-foreground: 26 90.5% 37.1%; /* amber-700 #b45309 */
--accent-emerald: 152 76% 90%; /* emerald-100 #d1fae5 */
--accent-emerald-foreground: 161.4 93.5% 30.4%; /* emerald-600 #059669 */
--accent-red: 0 93.5% 94.1%; /* red-100 #fee2e2 */
--accent-red-foreground: 0 72.2% 50.6%; /* red-600 #dc2626 */
--accent-indigo: 226.7 100% 94.9%; /* indigo-100 #e0e7ff */
--accent-indigo-foreground: 243.4 75.4% 58.6%; /* indigo-600 #4f46e5 */
--accent-pink: 320.6 76.1% 95.5%; /* pink-100 #fce7f3 */
--accent-pink-foreground: 333.3 71.4% 50.6%; /* pink-600 #db2777 */
--accent-purple: 270 100% 95.5%; /* purple-100 #f3e8ff */
--accent-purple-foreground: 262.1 83.3% 57.8%; /* purple-600 #7c3aed */
--accent-amber: 48, 96%, 89%; /* amber-100 #fef3c7 */
--accent-amber-foreground: 26, 90%, 37%; /* amber-700 #b45309 */
--accent-emerald: 149, 80%, 90%; /* emerald-100 #d1fae5 */
--accent-emerald-foreground: 161, 94%, 30%; /* emerald-600 #059669 */
--accent-red: 0, 93%, 94%; /* red-100 #fee2e2 */
--accent-red-foreground: 0, 72%, 51%; /* red-600 #dc2626 */
--accent-indigo: 226, 100%, 94%; /* indigo-100 #e0e7ff */
--accent-indigo-foreground: 243, 75%, 59%; /* indigo-600 #4f46e5 */
--accent-pink: 326, 78%, 95%; /* pink-100 #fce7f3 */
--accent-pink-foreground: 333, 71%, 51%; /* pink-600 #db2777 */
--accent-purple: 269, 100%, 95%; /* purple-100 #f3e8ff */
--accent-purple-foreground: 271, 81%, 56%; /* purple-600 #7c3aed */
/* Component Colors */
--component-icon: #d8598a;
@ -80,21 +80,18 @@
--ring: 0 0% 100%;
--placeholder-foreground: 240 4% 46%;
--accent-amber: 23 88% 27%; /* amber-900 #78350f */
--accent-amber-foreground: 45.9 96.7% 64.5%; /* amber-300 #fcd34d */
--accent-emerald: 164.3 88% 16.1%; /* emerald-900 #064e3b */
--accent-emerald-foreground: 156.2 71.6% 66.9%; /* emerald-400 #34d399 */
--accent-red: 0 75% 30%; /* red-900 #7f1d1d */
--accent-red-foreground: 0 93.5% 81.8%; /* red-400 #f87171 */
--accent-indigo: 239 46% 34.5%; /* indigo-900 #312e81 */
--accent-indigo-foreground: 234.5 89.5% 74.9%; /* indigo-400 #818cf8 */
--accent-pink: 327.4 70.7% 31.4%; /* pink-900 #831843 */
--accent-pink-foreground: 328.6 85.5% 70.2%; /* pink-400 #f472b6 */
--accent-purple: 261.2 67.8% 34.3%; /* purple-900 #4c1d95 */
--accent-purple-foreground: 255.1 89.5% 76.3%; /* purple-400 #a78bfa */
--warning: 45.9 96.7% 64.5%;
--warning-foreground: 240 6% 10%;
--accent-amber: 22, 78%, 26%; /* amber-900 #78350f */
--accent-amber-foreground: 46, 97%, 65%; /* amber-300 #fcd34d */
--accent-emerald: 164, 86%, 16%; /* emerald-900 #064e3b */
--accent-emerald-foreground: 158, 64%, 52%; /* emerald-400 #34d399 */
--accent-red: 0, 63%, 31%; /* red-900 #7f1d1d */
--accent-red-foreground: 0, 91%, 71%; /* red-400 #f87171 */
--accent-indigo: 242, 47%, 34%; /* indigo-900 #312e81 */
--accent-indigo-foreground: 234, 89%, 74%; /* indigo-400 #818cf8 */
--accent-pink: 336, 69%, 30%; /* pink-900 #831843 */
--accent-pink-foreground: 329, 86%, 70%; /* pink-400 #f472b6 */
--accent-purple: 274, 66%, 32%; /* purple-900 #4c1d95 */
--accent-purple-foreground: 270, 95%, 75%; /* purple-400 #a78bfa */
}
* {

View file

@ -147,7 +147,7 @@ function SearchPage() {
initialFlex: 0.5,
cellRenderer: ({ value }: CustomCellRendererProps<File>) => {
return (
<span className="text-xs text-green-400 bg-green-400/20 px-2 py-1 rounded">
<span className="text-xs text-accent-emerald-foreground bg-accent-emerald px-2 py-1 rounded">
{value?.toFixed(2) ?? "-"}
</span>
);

View file

@ -16,27 +16,27 @@ interface StatusBadgeProps {
const statusConfig = {
processing: {
label: "Processing",
className: "text-muted-foreground dark:text-muted-foreground ",
className: "text-muted-foreground ",
},
active: {
label: "Active",
className: "text-emerald-600 dark:text-emerald-400 ",
className: "text-accent-emerald-foreground ",
},
unavailable: {
label: "Unavailable",
className: "text-red-600 dark:text-red-400 ",
className: "text-accent-red-foreground ",
},
failed: {
label: "Failed",
className: "text-red-600 dark:text-red-400 ",
className: "text-accent-red-foreground ",
},
hidden: {
label: "Hidden",
className: "text-zinc-400 dark:text-zinc-500 ",
className: "text-muted-foreground ",
},
sync: {
label: "Sync",
className: "text-amber-700 dark:text-amber-300 underline",
className: "text-accent-amber-foreground underline",
},
};