blob: cd17ac61752867ec4a6dcc5b41785c2571cbd8e1 [file] [log] [blame]
gio5f2f1002025-03-20 18:38:48 +04001import { Button } from "@/components/ui/button";
gio90a265b2025-06-26 09:16:58 +00002import { Input } from "@/components/ui/input";
gio5f2f1002025-03-20 18:38:48 +04003import { v4 as uuidv4 } from "uuid";
gio90a265b2025-06-26 09:16:58 +00004import { useCallback, useState, useMemo } from "react";
gio5f2f1002025-03-20 18:38:48 +04005import { Accordion, AccordionTrigger } from "./ui/accordion";
6import { AccordionContent, AccordionItem } from "@radix-ui/react-accordion";
gio69148322025-06-19 23:16:12 +04007import { AppState, useCategories, useMode, useProjectId, useStateStore } from "@/lib/state";
gio5f2f1002025-03-20 18:38:48 +04008import { CategoryItem } from "@/lib/categories";
9import { Icon } from "./icon";
gio69148322025-06-19 23:16:12 +040010import { AppNode, NodeType } from "config";
gio5f2f1002025-03-20 18:38:48 +040011
gio1037ee22025-06-26 09:25:43 +000012function addResource(i: CategoryItem<NodeType>, store: AppState, onResourceAdded?: () => void) {
gioaf8db832025-05-13 14:43:05 +000013 const deselected = store.nodes.map((n) => ({
gio818da4e2025-05-12 14:45:35 +000014 ...n,
15 selected: false,
16 }));
gioaf8db832025-05-13 14:43:05 +000017 store.setNodes(deselected);
18 store.addNode({
19 id: uuidv4(),
20 type: i.type,
21 data: i.init,
22 selected: true,
23 connectable: true,
24 } as any); // eslint-disable-line @typescript-eslint/no-explicit-any
gio1037ee22025-06-26 09:25:43 +000025 if (onResourceAdded) {
26 onResourceAdded();
27 }
gio5f2f1002025-03-20 18:38:48 +040028}
29
gio1037ee22025-06-26 09:25:43 +000030interface ResourcesProps {
31 onResourceAdded?: () => void;
32}
33
34export function Resources({ onResourceAdded }: ResourcesProps = {}) {
gio818da4e2025-05-12 14:45:35 +000035 const store = useStateStore();
giod0026612025-05-08 13:00:36 +000036 const categories = useCategories();
gio33046722025-05-16 14:49:55 +000037 const projectId = useProjectId();
38 const mode = useMode();
giod0026612025-05-08 13:00:36 +000039 const onResourceAdd = useCallback(
40 (item: CategoryItem<NodeType>) => {
gio1037ee22025-06-26 09:25:43 +000041 return () => addResource(item, store, onResourceAdded);
giod0026612025-05-08 13:00:36 +000042 },
gio1037ee22025-06-26 09:25:43 +000043 [store, onResourceAdded],
giod0026612025-05-08 13:00:36 +000044 );
gioaf8db832025-05-13 14:43:05 +000045
giod0026612025-05-08 13:00:36 +000046 const [open, setOpen] = useState<string[]>(categories.map((c) => c.title));
gio90a265b2025-06-26 09:16:58 +000047 const [searchValue, setSearchValue] = useState<string>("");
48
49 // Filter categories based on search value
50 const filteredCategories = useMemo(() => {
51 if (!searchValue.trim()) {
52 return categories;
53 }
54
55 const searchLower = searchValue.toLowerCase();
56 return categories
57 .map((category) => ({
58 ...category,
59 items: category.items.filter((item) => item.title.toLowerCase().includes(searchLower)),
60 }))
61 .filter((category) => category.items.length > 0); // Remove empty categories
62 }, [categories, searchValue]);
gioaf8db832025-05-13 14:43:05 +000063
giod0026612025-05-08 13:00:36 +000064 return (
65 <>
gio90a265b2025-06-26 09:16:58 +000066 <div className="px-3 pb-3 mt-2">
67 <Input
68 type="text"
69 placeholder="Search resources..."
70 value={searchValue}
71 onChange={(e) => setSearchValue(e.target.value)}
72 className="w-full"
73 />
74 </div>
giod0026612025-05-08 13:00:36 +000075 <Accordion type="multiple" value={open} onValueChange={(v) => setOpen(v)}>
gio90a265b2025-06-26 09:16:58 +000076 {filteredCategories.map((c) => (
giod0026612025-05-08 13:00:36 +000077 <AccordionItem key={c.title} value={c.title} className={"px-3" + (c.active ? " bg-amber-100" : "")}>
78 <AccordionTrigger>{c.title}</AccordionTrigger>
79 <AccordionContent>
80 <div className="flex flex-col space-y-1">
gio818da4e2025-05-12 14:45:35 +000081 {c.items.map((item: CategoryItem<NodeType>) => (
giod0026612025-05-08 13:00:36 +000082 <Button
83 key={item.title}
84 onClick={onResourceAdd(item)}
85 style={{ justifyContent: "flex-start" }}
gio33046722025-05-16 14:49:55 +000086 disabled={projectId == null || mode !== "edit"}
giod0026612025-05-08 13:00:36 +000087 >
gio69148322025-06-19 23:16:12 +040088 <Icon node={{ type: item.type, data: item.init } as AppNode} />
giod0026612025-05-08 13:00:36 +000089 {item.title}
90 </Button>
91 ))}
92 </div>
93 </AccordionContent>
94 </AccordionItem>
95 ))}
96 </Accordion>
97 </>
98 );
gio5f2f1002025-03-20 18:38:48 +040099}