Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add AI assistant to dokploy #1079

Open
wants to merge 2 commits into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions apps/dokploy/components/dashboard/project/add-ai-assistant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { TemplateGenerator } from "@/components/dashboard/project/ai/template-generator";

interface Props {
projectId: string;
projectName?: string;
}

export const AddAiAssistant = ({ projectId }: Props) => {
return <TemplateGenerator projectId={projectId} />;
};
79 changes: 79 additions & 0 deletions apps/dokploy/components/dashboard/project/ai/step-four.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import ReactMarkdown from "react-markdown";
import {CodeEditor} from "@/components/shared/code-editor";

export const StepFour = ({
prevStep,
templateInfo,
setOpen,
setTemplateInfo,
}: any) => {
const handleSubmit = () => {
setTemplateInfo(templateInfo); // Update the template info
setOpen(false);
};

return (
<div className="flex flex-col h-full">
<div className="flex-grow">
<div className="space-y-6 pb-20">
<h2 className="text-lg font-semibold">Step 4: Review and Finalize</h2>
<ScrollArea className="h-[400px] p-5">
<div className="space-y-4">
<div className="p-4">
<ReactMarkdown className="prose dark:prose-invert">
{templateInfo.details.description}
</ReactMarkdown>
</div>
<div>
<h3 className="text-md font-semibold">Name</h3>
<p>{templateInfo.name}</p>
</div>
<div>
<h3 className="text-md font-semibold">Server</h3>
<p>{templateInfo.server || "localhost"}</p>
</div>
<div>
<h3 className="text-md font-semibold">Docker Compose</h3>
<CodeEditor
lineWrapping
value={templateInfo.details.dockerCompose}
disabled
className="font-mono"
/>
</div>
<div>
<h3 className="text-md font-semibold">Environment Variables</h3>
<ul className="list-disc pl-5">
{templateInfo.details.envVariables.map(
(
env: {
name: string;
value: string;
},
index: number,
) => (
<li key={index}>
<strong>{env.name}</strong>:
<span className="ml-2 font-mono">{env.value}</span>
</li>
),
)}
</ul>
</div>
</div>
</ScrollArea>
</div>
</div>
<div className="sticky bottom-0 bg-background pt-2 border-t">
<div className="flex justify-between">
<Button onClick={prevStep} variant="outline">
Back
</Button>
<Button onClick={handleSubmit}>Create</Button>
</div>
</div>
</div>
);
}
69 changes: 69 additions & 0 deletions apps/dokploy/components/dashboard/project/ai/step-one.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"use client";

import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { useState } from "react";

const examples = [
"Make a personal blog",
"Add a photo studio portfolio",
"Create a personal ad blocker",
"Build a social media dashboard",
"Sendgrid service opensource analogue",
];

export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => {
const [userInput, setUserInput] = useState(templateInfo.userInput);

const handleNext = () => {
setTemplateInfo({ ...templateInfo, userInput });
nextStep();
};

const handleExampleClick = (example: string) => {
setUserInput(example);
};

return (
<div className="flex flex-col h-full">
<div className="flex-grow overflow-auto">
<div className="space-y-4 pb-20">
<h2 className="text-lg font-semibold">Step 1: Describe Your Needs</h2>
<div className="space-y-2">
<Label htmlFor="user-needs">Describe your template needs</Label>
<Textarea
id="user-needs"
placeholder="Describe the type of template you need, its purpose, and any specific features you'd like to include."
value={userInput}
onChange={(e) => setUserInput(e.target.value)}
className="min-h-[100px]"
/>
</div>
<div className="space-y-2">
<Label>Examples:</Label>
<div className="flex flex-wrap gap-2">
{examples.map((example, index) => (
<Button
key={index}
variant="outline"
size="sm"
onClick={() => handleExampleClick(example)}
>
{example}
</Button>
))}
</div>
</div>
</div>
</div>
<div className="sticky bottom-0 bg-background pt-2 border-t">
<div className="flex justify-end">
<Button onClick={handleNext} disabled={!userInput.trim()}>
Next
</Button>
</div>
</div>
</div>
);
}
84 changes: 84 additions & 0 deletions apps/dokploy/components/dashboard/project/ai/step-three.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"use client";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { api } from "@/utils/api";
import { useState } from "react";

export const StepThree = ({
nextStep,
prevStep,
templateInfo,
setTemplateInfo,
}: any) => {
const [name, setName] = useState(templateInfo.name);
const [server, setServer] = useState(templateInfo.server);
const { data: servers } = api.server.withSSHKey.useQuery();

const handleNext = () => {
const updatedInfo = { ...templateInfo, name };
if (server?.trim()) {
updatedInfo.server = server;
}
setTemplateInfo(updatedInfo);
nextStep();
};

return (
<div className="flex flex-col h-full">
<div className="flex-grow overflow-auto">
<div className="space-y-4 pb-20">
<h2 className="text-lg font-semibold">Step 3: Additional Details</h2>
<div>
<Label htmlFor="name">App Name</Label>
<Input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter app name"
className="mt-1"
/>
</div>
<div>
<Label htmlFor="server">Server Attachment (Optional)</Label>
<Select value={server} onValueChange={setServer}>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem key={server.serverId} value={server.serverId}>
{server.name}
</SelectItem>
))}
<SelectLabel>Servers ({servers?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
</div>
</div>
</div>
<div className="sticky bottom-0 bg-background pt-2 border-t">
<div className="flex justify-between">
<Button onClick={prevStep} variant="outline">
Back
</Button>
<Button onClick={handleNext} disabled={!name.trim()}>
Next
</Button>
</div>
</div>
</div>
);
}
Loading