From df2ddfdab000e364df9020934a24e89edbe0d0ac Mon Sep 17 00:00:00 2001 From: Zakher Masri Date: Tue, 11 Mar 2025 17:43:30 +0300 Subject: [PATCH] chore(ui): improve search UI --- app/src/App.tsx | 5 +- app/src/components/Search.tsx | 86 ++++++++++++++--------------- app/src/components/SelectedTags.tsx | 30 ++++++++++ app/src/components/TemplateGrid.tsx | 48 +++++++++++++--- app/src/store.ts | 47 ++++++++++------ 5 files changed, 146 insertions(+), 70 deletions(-) create mode 100644 app/src/components/SelectedTags.tsx diff --git a/app/src/App.tsx b/app/src/App.tsx index edac97c..9f7195f 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,14 +1,17 @@ import TemplateGrid from "./components/TemplateGrid"; import Navigation from "./components/Navigation"; import Search from "./components/Search"; +import { useStore } from "./store"; import "./App.css"; function App() { + const view = useStore((state) => state.view); + return (
- +
); } diff --git a/app/src/components/Search.tsx b/app/src/components/Search.tsx index 5c8b64a..8b37f0f 100644 --- a/app/src/components/Search.tsx +++ b/app/src/components/Search.tsx @@ -1,7 +1,6 @@ import { Input } from "./ui/input"; import { useStore } from "../store"; -import { SearchIcon, XIcon } from "lucide-react"; -import { Badge } from "./ui/badge"; +import { Grid, List, SearchIcon, XIcon } from "lucide-react"; import { Button } from "./ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { @@ -15,14 +14,16 @@ import { import { cn } from "@/lib/utils"; import { Check, ChevronsUpDown } from "lucide-react"; import React from "react"; - +import { Tabs, TabsList, TabsTrigger } from "./ui/tabs"; +import SelectedTags from "./SelectedTags"; const Search = () => { - const { templates, searchQuery, setSearchQuery } = useStore(); + const { templates, searchQuery, setSearchQuery, setView } = useStore(); const selectedTags = useStore((state) => state.selectedTags); const addSelectedTag = useStore((state) => state.addSelectedTag); const removeSelectedTag = useStore((state) => state.removeSelectedTag); const [open, setOpen] = React.useState(false); const [tagSearch, setTagSearch] = React.useState(""); + const view = useStore((state) => state.view); // Get all unique tags, safely handle empty templates const uniqueTags = React.useMemo(() => { @@ -41,31 +42,44 @@ const Search = () => { }, [uniqueTags, tagSearch]); return ( -
+

Available Templates ({templates?.length || 0})

-
-
-
- setSearchQuery(e.target.value)} - className="w-full" - /> - {searchQuery.length > 0 ? ( -
setSearchQuery("")} - > - -
- ) : ( - - )} -
+
+
+ setSearchQuery(e.target.value)} + className="w-full p-6" + /> + {searchQuery.length > 0 ? ( +
setSearchQuery("")}> + +
+ ) : ( + + )} +
+ +
+ { + setView(value as "grid" | "rows"); + }} + > + + + Grid + + + List + + + @@ -118,25 +132,7 @@ const Search = () => {
- {selectedTags.length > 0 && ( -
- {selectedTags.map((tag) => ( - - {tag} -
removeSelectedTag(tag)} - > - -
-
- ))} -
- )} + {selectedTags.length > 0 && }
); diff --git a/app/src/components/SelectedTags.tsx b/app/src/components/SelectedTags.tsx new file mode 100644 index 0000000..feaa888 --- /dev/null +++ b/app/src/components/SelectedTags.tsx @@ -0,0 +1,30 @@ +import { XIcon } from "lucide-react"; +import { useStore } from "../store"; +import { Badge } from "./ui/badge"; + +const SelectedTags = () => { + const selectedTags = useStore((state) => state.selectedTags); + const removeSelectedTag = useStore((state) => state.removeSelectedTag); + + return ( +
+ {selectedTags.map((tag) => ( + + {tag} +
removeSelectedTag(tag)} + > + +
+
+ ))} +
+ ); +}; + +export default SelectedTags; diff --git a/app/src/components/TemplateGrid.tsx b/app/src/components/TemplateGrid.tsx index 658c450..ea06047 100644 --- a/app/src/components/TemplateGrid.tsx +++ b/app/src/components/TemplateGrid.tsx @@ -22,6 +22,8 @@ import { useStore } from "../store"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs"; import { Label } from "./ui/label"; import { Clipboard } from "lucide-react"; +import { Skeleton } from "./ui/skeleton"; +import { cn } from "@/lib/utils"; interface Template { id: string; @@ -42,11 +44,17 @@ interface TemplateFiles { config: string | null; } -const TemplateGrid: React.FC = () => { +interface TemplateGridProps { + view: "grid" | "rows"; +} + +const TemplateGrid: React.FC = ({ view }) => { const { templates, setTemplates } = useStore(); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const { addSelectedTag, searchQuery, selectedTags } = useStore(); + const searchQuery = useStore((state) => state.searchQuery); + const selectedTags = useStore((state) => state.selectedTags); + const { addSelectedTag } = useStore(); const [selectedTemplate, setSelectedTemplate] = useState