diff --git a/src/app/(dashboard)/library/shared-components/new/page.tsx b/src/app/(dashboard)/library/shared-components/new/page.tsx index c4ecfb5..7d0595a 100644 --- a/src/app/(dashboard)/library/shared-components/new/page.tsx +++ b/src/app/(dashboard)/library/shared-components/new/page.tsx @@ -8,7 +8,7 @@ import { useEnvironmentStore } from "@/stores/environment-store"; import { VECTOR_CATALOG } from "@/lib/vector/catalog"; import { toast } from "sonner"; import Link from "next/link"; -import { ArrowLeft, Loader2, Plus, Search } from "lucide-react"; +import { ArrowLeft, ChevronDown, Loader2, Plus, Search } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; @@ -22,12 +22,18 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; import { SchemaForm } from "@/components/config-forms/schema-form"; +import { cn } from "@/lib/utils"; import type { VectorComponentDef } from "@/lib/vector/types"; /* ------------------------------------------------------------------ */ -/* Kind badge styling */ +/* Kind styling */ /* ------------------------------------------------------------------ */ const kindVariant: Record = { @@ -38,6 +44,23 @@ const kindVariant: Record = { sink: "bg-orange-100 text-orange-800 dark:bg-orange-900/40 dark:text-orange-300", }; +const kindSectionConfig: Record = { + source: { + label: "Sources", + accent: "text-emerald-600 dark:text-emerald-400", + }, + transform: { + label: "Transforms", + accent: "text-sky-600 dark:text-sky-400", + }, + sink: { + label: "Sinks", + accent: "text-orange-600 dark:text-orange-400", + }, +}; + +const KIND_ORDER = ["source", "transform", "sink"] as const; + /* ------------------------------------------------------------------ */ /* Page Component */ /* ------------------------------------------------------------------ */ @@ -71,6 +94,16 @@ export default function NewSharedComponentPage() { ); }, [search]); + const groupedCatalog = useMemo( + () => + KIND_ORDER.map((kind) => ({ + kind, + ...kindSectionConfig[kind], + items: filteredCatalog.filter((c) => c.kind === kind), + })), + [filteredCatalog], + ); + const createMutation = useMutation( trpc.sharedComponent.create.mutationOptions({ onSuccess: (sc) => { @@ -162,42 +195,30 @@ export default function NewSharedComponentPage() { /> - {/* Component grid */} -
- {filteredCatalog.map((comp) => ( - handleSelectComponent(comp)} - > - -
- - {comp.displayName} - - - {comp.kind} - -
-
- -

- {comp.description} -

-
-
- ))} -
- - {filteredCatalog.length === 0 && ( + {/* Component sections by kind */} + {filteredCatalog.length === 0 ? (

No components match your search.

+ ) : ( +
+ {groupedCatalog.map((group) => { + if (group.items.length === 0) return null; + return ( + + ); + })} +
)} )} @@ -289,3 +310,72 @@ export default function NewSharedComponentPage() { ); } + +/* ------------------------------------------------------------------ */ +/* Catalog Kind Section (collapsible) */ +/* ------------------------------------------------------------------ */ + +function CatalogKindSection({ + label, + accent, + badgeClass, + count, + items, + onSelect, +}: { + label: string; + accent: string; + badgeClass: string; + count: number; + items: VectorComponentDef[]; + onSelect: (comp: VectorComponentDef) => void; +}) { + const [open, setOpen] = useState(true); + + return ( + + + + {label} + + {count} + + + +
+ {items.map((comp) => ( + onSelect(comp)} + > + +
+ + {comp.displayName} + + + {comp.kind} + +
+
+ +

+ {comp.description} +

+
+
+ ))} +
+
+
+ ); +} diff --git a/src/app/(dashboard)/library/shared-components/page.tsx b/src/app/(dashboard)/library/shared-components/page.tsx index 4e509df..3f8ec66 100644 --- a/src/app/(dashboard)/library/shared-components/page.tsx +++ b/src/app/(dashboard)/library/shared-components/page.tsx @@ -12,6 +12,12 @@ import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Skeleton } from "@/components/ui/skeleton"; import { PageHeader } from "@/components/page-header"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Collapsible, CollapsibleContent, @@ -216,26 +222,30 @@ function KindSection({ -
+
{items.map((sc) => ( - +
+ + {sc.linkedPipelineCount} linked + {formatRelativeTime(sc.updatedAt)} +
+ + ))}