Skip to content

Commit

Permalink
feat: add pocketbase template
Browse files Browse the repository at this point in the history
  • Loading branch information
Siumauricio committed Jun 2, 2024
1 parent 4aaebd5 commit 34f98a5
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 110 deletions.
219 changes: 133 additions & 86 deletions components/dashboard/project/add-template.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
import { AlertBlock } from "@/components/shared/alert-block";
import { api } from "@/utils/api";
import { Github, Globe, PuzzleIcon } from "lucide-react";
import { toast } from "sonner";
import { Code, Github, Globe, PuzzleIcon } from "lucide-react";
import Link from "next/link";
import { Input } from "@/components/ui/input";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
AlertDialog,
AlertDialogAction,
Expand All @@ -24,19 +26,22 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import Link from "next/link";

import { toast } from "sonner";
interface Props {
projectId: string;
}

export const AddTemplate = ({ projectId }: Props) => {
const utils = api.useUtils();
const [query, setQuery] = useState("");
const { data } = api.compose.templates.useQuery();

const { mutateAsync, isLoading, error, isError } =
api.compose.deployTemplate.useMutation();

const templates = data?.filter((t) =>
t.name.toLowerCase().includes(query.toLowerCase()),
);

return (
<Dialog>
<DialogTrigger className="w-full">
Expand All @@ -48,54 +53,136 @@ export const AddTemplate = ({ projectId }: Props) => {
<span>Templates</span>
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl">
<DialogHeader>
<DialogTitle>Create Template</DialogTitle>
<DialogDescription>
Deploy a open source template to your project
</DialogDescription>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}

<div
id="hook-form"
className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 w-full gap-4"
>
{data?.map((template, index) => (
<AlertDialog key={`template-${index}`}>
<AlertDialogTrigger asChild>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl p-0">
<div className="sticky top-0 z-10 flex flex-col gap-4 bg-black p-6 border-b">
<DialogHeader>
<DialogTitle>Create Template</DialogTitle>
<DialogDescription>
Deploy a open source template to your project
</DialogDescription>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Input
placeholder="Search Template"
onChange={(e) => setQuery(e.target.value)}
value={query}
/>
</div>
<div className="p-6">
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 w-full gap-4">
{templates?.map((template, index) => (
<div key={`template-${index}`}>
<div
key={template.name}
className="flex flex-col gap-4 border p-6 rounded-lg hover:bg-card/20 cursor-pointer transition-colors"
className="flex flex-col gap-4 border p-6 rounded-lg h-full"
>
<div className="flex flex-col gap-4">
<div className="flex flex-col items-center gap-2">
<img
src={template.logo}
className="size-20 object-contain"
src={`/templates/${template.logo}`}
className="size-28 object-contain"
alt=""
/>
</div>

<div className="flex flex-col gap-2">
<div className="flex flex-col gap-2">
<span>{template.name}</span>
<div className="flex flex-row gap-2">
<Link
href={template.links.github}
target="_blank"
className="text-sm text-muted-foreground"
>
<Github className="size-4 text-muted-foreground" />
</Link>
<Link
href={template.links.docs}
target="_blank"
className="text-sm text-muted-foreground"
>
<Globe className="size-4 text-muted-foreground" />
</Link>
<div className="flex flex-col gap-2 justify-center items-center">
<div className="flex flex-col gap-2 items-center justify-center">
<span className="text-sm font-medium">
{template.name}
</span>
<div className="flex flex-row gap-0">
<Link
href={template.links.github}
target="_blank"
className={
"text-sm text-muted-foreground p-3 rounded-full hover:bg-border items-center flex transition-colors"
}
>
<Github className="size-4 text-muted-foreground" />
</Link>
{template.links.website && (
<Link
href={template.links.website}
target="_blank"
className={
"text-sm text-muted-foreground p-3 rounded-full hover:bg-border items-center flex transition-colors"
}
>
<Globe className="size-4 text-muted-foreground" />
</Link>
)}
{template.links.docs && (
<Link
href={template.links.docs}
target="_blank"
className={
"text-sm text-muted-foreground p-3 rounded-full hover:bg-border items-center flex transition-colors"
}
>
<Globe className="size-4 text-muted-foreground" />
</Link>
)}
<Link
href={`https://github.com/dokploy/dokploy/tree/canary/templates/${template.id}`}
target="_blank"
className={
"text-sm text-muted-foreground p-3 rounded-full hover:bg-border items-center flex transition-colors"
}
>
<Code className="size-4 text-muted-foreground" />
</Link>
</div>
<div className="flex flex-row gap-2 flex-wrap justify-center">
{template.tags.map((tag) => (
<Badge variant="secondary" key={tag}>
{tag}
</Badge>
))}
</div>
</div>

<AlertDialog>
<AlertDialogTrigger asChild>
<Button onSelect={(e) => e.preventDefault()}>
Deploy
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Are you absolutely sure?
</AlertDialogTitle>
<AlertDialogDescription>
This will deploy {template.name} template to
your project.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
projectId,
id: template.id,
})
.then(async () => {
toast.success(
`${template.name} template deleted succesfully`,
);
})
.catch(() => {
toast.error(
`Error to delete ${template.name} template`,
);
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>

<p className="text-sm text-muted-foreground">
Expand All @@ -104,49 +191,9 @@ export const AddTemplate = ({ projectId }: Props) => {
</div>
</div>
</div>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Are you absolutely sure to deploy {template.name} to your
project?
</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. Automatically will setup the
template in your project.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
if (!template.folder) {
toast.error(
"This template doest not have a folder specified",
);
return;
}
await mutateAsync({
folder: template.folder,
projectId,
})
.then(async () => {
toast.success("Template Created");
await utils.project.one.invalidate({
projectId,
});
})
.catch(() => {
toast.error("Error to create the template");
});
}}
>
Continue
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
))}
</div>
))}
</div>
</div>
</DialogContent>
</Dialog>
Expand Down
9 changes: 9 additions & 0 deletions public/templates/pocketbase.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions server/api/routers/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,11 @@ export const composeRouter = createTRPCRouter({
deployTemplate: protectedProcedure
.input(apiCreateComposeByTemplate)
.mutation(async ({ input }) => {
const composeFile = await readComposeFile(input.folder);
const composeFile = await readComposeFile(input.id);

console.log(composeFile);

const generate = await loadTemplateModule(input.folder as TemplatesKeys);
const generate = await loadTemplateModule(input.id as TemplatesKeys);

const admin = await findAdmin();

Expand All @@ -213,7 +213,7 @@ export const composeRouter = createTRPCRouter({

const project = await findProjectById(input.projectId);

const projectName = slugifyProjectName(`${project.name}-${input.folder}`);
const projectName = slugifyProjectName(`${project.name}-${input.id}`);
const { envs, mounts } = generate({
serverIp: admin.serverIp,
projectName: projectName,
Expand All @@ -223,7 +223,7 @@ export const composeRouter = createTRPCRouter({
...input,
composeFile: composeFile,
env: envs.join("\n"),
name: input.folder,
name: input.id,
sourceType: "raw",
});

Expand All @@ -246,9 +246,9 @@ export const composeRouter = createTRPCRouter({
const templatesData = templates.map((t) => ({
name: t.name,
description: t.description,
type: t.type,
folder: t.folder,
id: t.id,
links: t.links,
tags: t.tags,
logo: t.logo,
}));

Expand Down
2 changes: 1 addition & 1 deletion server/db/schema/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const apiCreateComposeByTemplate = createSchema
projectId: true,
})
.extend({
folder: z.string().min(1),
id: z.string().min(1),
});

export const apiFindCompose = z.object({
Expand Down
Loading

0 comments on commit 34f98a5

Please sign in to comment.