-
-
-
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..9f56eaa 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(
null
@@ -133,9 +141,23 @@ const TemplateGrid: React.FC = () => {
if (loading) {
return (
-
- Loading templates...
-
+
+ {[1, 2, 3].map((item) => (
+
+ ))}
+
);
}
@@ -154,14 +176,27 @@ const TemplateGrid: React.FC = () => {
return (
<>
-
+
{filteredTemplates.length > 0 ? (
filteredTemplates.map((template) => (
handleTemplateClick(template)}
+ className={cn(
+ " cursor-pointer hover:shadow-lg transition-all duration-200 h-full max-h-[300px]",
+ {
+ "flex-col": view === "grid",
+ "flex-row": view === "rows",
+ }
+ )}
>
- handleTemplateClick(template)}>
+
{
{template.description}
-
+
{template.tags.slice(0, 3).map((tag) => (
addSelectedTag(tag)}
+ onClick={(e) => {
+ e.stopPropagation();
+ addSelectedTag(tag);
+ }}
key={tag}
className="inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-blue-100 text-blue-800"
>
@@ -187,10 +225,7 @@ const TemplateGrid: React.FC = () => {
))}
-
handleTemplateClick(template)}
- >
+
{template.version}
@@ -224,7 +259,7 @@ const TemplateGrid: React.FC = () => {
open={!!selectedTemplate}
onOpenChange={() => setSelectedTemplate(null)}
>
-
+
{selectedTemplate?.logo && (
@@ -278,7 +313,7 @@ const TemplateGrid: React.FC = () => {
-
+
{selectedTemplate?.description}
@@ -339,7 +374,7 @@ const TemplateGrid: React.FC = () => {
{templateFiles?.dockerCompose && (
-
+