"use client"; import { AssetInput } from "@pascal-app/core"; import { resolveCdnUrl } from "@pascal-app/viewer"; import Image from "next/image"; import { useEffect, useState } from "react"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/primitives/tooltip"; import { cn } from "@/lib/utils"; import useEditor, { CatalogCategory } from "@/store/use-editor"; import { CATALOG_ITEMS } from "./catalog-items"; const PLACEMENT_TAGS = new Set(["floor", "wall", "ceiling", "countertop"]); export function ItemCatalog({ category }: { category: CatalogCategory }) { const selectedItem = useEditor((state) => state.selectedItem); const setSelectedItem = useEditor((state) => state.setSelectedItem); const [activePlacementTag, setActivePlacementTag] = useState(null); const [activeFunctionalTag, setActiveFunctionalTag] = useState(null); const categoryItems = CATALOG_ITEMS.filter( (item) => item.category === category, ); // Collect tags available in this category const allTags = Array.from( new Set(categoryItems.flatMap((item) => item.tags ?? [])), ); const placementTags = allTags.filter((t) => PLACEMENT_TAGS.has(t)); const functionalTags = allTags.filter((t) => !PLACEMENT_TAGS.has(t)); const hasFilters = allTags.length > 1; // Count items for a placement tag given the current functional filter const placementCount = (tag: string | null) => categoryItems.filter((item) => { const tags = item.tags ?? []; if (tag !== null && !tags.includes(tag)) return false; if (activeFunctionalTag && !tags.includes(activeFunctionalTag)) return false; return true; }).length; // Count items for a functional tag given the current placement filter const functionalCount = (tag: string) => categoryItems.filter((item) => { const tags = item.tags ?? []; if (!tags.includes(tag)) return false; if (activePlacementTag && !tags.includes(activePlacementTag)) return false; return true; }).length; const filteredItems = categoryItems.filter((item) => { const tags = item.tags ?? []; if (activePlacementTag && !tags.includes(activePlacementTag)) return false; if (activeFunctionalTag && !tags.includes(activeFunctionalTag)) return false; return true; }); // Auto-select first item if current selection is not in the filtered list useEffect(() => { const isCurrentItemInCategory = filteredItems.some( (item) => item.src === selectedItem?.src, ); if (!isCurrentItemInCategory && filteredItems.length > 0) { setSelectedItem(filteredItems[0] as AssetInput); } }, [filteredItems, selectedItem?.src, setSelectedItem]); // Get attachment icon based on attachTo type const getAttachmentIcon = (attachTo: AssetInput["attachTo"]) => { if (attachTo === "wall" || attachTo === "wall-side") { return "/icons/wall.png"; } if (attachTo === "ceiling") { return "/icons/ceiling.png"; } return null; }; return (
{/* Filter chips */} {hasFilters && (
{/* Placement row */} {placementTags.length > 0 && (
{placementTags.map((tag) => { const count = placementCount(tag); const isActive = activePlacementTag === tag; const isEmpty = count === 0 && !isActive; return ( ); })}
)} {/* Functional row */} {functionalTags.length > 0 && (
{functionalTags.map((tag) => { const count = functionalCount(tag); const isActive = activeFunctionalTag === tag; const isEmpty = count === 0 && !isActive; return ( ); })}
)}
)} {/* Items */}
{filteredItems.map((item, index) => { const isSelected = selectedItem?.src === item?.src; const attachmentIcon = getAttachmentIcon(item?.attachTo); return ( {item.name} ); })}
); }