beta/components/generate-project.tsx
Christopher Arraya bcf3f113ad i'm done
2023-11-05 07:34:37 -05:00

376 lines
12 KiB
TypeScript

"use client";
import { v4 as uuidv4 } from "uuid";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTrigger,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Loader2, PencilRuler, Plus, Trash2 } from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { Feature, Task } from "@/types";
import { ScrollArea } from "./ui/scroll-area";
import FeatureCard from "./feature-card";
import { useRouter } from "next/navigation";
export function GenerateProject({
workspaceID,
projectID,
project_name,
project_description,
tech_stack,
}: {
workspaceID: string;
projectID: string;
project_name: string;
project_description: string;
tech_stack: string[];
}) {
const [features, setFeatures] = useState<Feature[]>([]);
const [tasks, setTasks] = useState<Task[]>([]);
const [isDialogOpen, setDialogOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [isAddingFeature, setIsAddingFeature] = useState(false);
const [addName, setAddName] = useState("");
const [addDescription, setAddDescription] = useState("");
const [page, setPage] = useState(0);
const router = useRouter();
async function generateFeatures() {
setLoading(true);
const res = await fetch(`/w/${workspaceID}/p/${projectID}/features/gen`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
project_name,
project_description,
tech_stack,
}),
});
setLoading(false);
setPage(1);
const data = await res.json();
console.log("CLIENT RAW RES ===>", data.features);
setFeatures(data.features);
}
async function generateTasks(features: Feature[]) {
console.log("GENERATING TASKS ===>", features);
let _tasks: Task[] = [];
features.forEach(async (feature, i) => {
const res = await fetch(`/w/${workspaceID}/p/${projectID}/tasks/gen`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
project_name,
project_description,
tech_stack,
related_features: [],
feature,
}),
});
const data = await res.json();
console.log("TASK RAW RES ===>", data.tasks);
_tasks.push(...data.tasks);
console.log("CURTASKS ===>", _tasks);
if (i === features.length - 1) {
console.log("TASKS ===>", _tasks);
setTasks(_tasks);
addTasks(_tasks);
setPage(3);
}
});
}
async function addTasks(tasks: Task[] = []) {
console.log("ADDING TASKS ===>", tasks);
const res = await fetch(`/w/${workspaceID}/p/${projectID}/tasks/add`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tasks),
});
const data = await res.json();
console.log("ADDTASKS RAW RES ===>", data.tasks);
}
async function addFeatures() {
const res = await fetch(`/w/${workspaceID}/p/${projectID}/features/add`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(features),
});
const data = await res.json();
console.log("addFeatures RAW RES ===>", data.features);
setFeatures(data.features);
setPage(2);
return data.features;
}
useEffect(() => {
async function getFeatures() {
const res = await fetch(`/w/${workspaceID}/p/${projectID}/features`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();
if (data.features.length === 0) setDialogOpen(true);
console.log("CLIENT RAW RES ===>", data.features);
return data.features;
}
getFeatures().then((features) => setFeatures(features));
}, [projectID, workspaceID]);
return (
<>
<Dialog open={isDialogOpen} onOpenChange={setDialogOpen}>
{page == 0 && (
<DialogContent>
<DialogHeader>
<DialogTitle>
Welcome to <i>{project_name}</i>.
</DialogTitle>
<DialogDescription>
Now it&apos;s time to generate some features for your new
project.
</DialogDescription>
{!loading ? (
<Button
size="lg"
onClick={() => {
generateFeatures();
}}
>
Let&apos;s get started.
</Button>
) : (
<Button disabled size="lg" className="cursor-progress">
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Generating features...
</Button>
)}
<DialogFooter>
<Button
variant="ghost"
onClick={() => setDialogOpen(false)}
size="sm"
>
Skip this for now.
</Button>
</DialogFooter>
</DialogHeader>
</DialogContent>
)}
{page == 1 && (
<DialogContent className="min-w-[800px]">
<DialogHeader>
<DialogTitle>
Here&apos;s what we&apos;ve generated for you.
</DialogTitle>
<DialogDescription>
Feel free to edit, delete, or add new features.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col space-y-2">
<ScrollArea className="w-full h-[600px]">
<div className="grid grid-cols-2 gap-4">
{features.map((feature) => (
<FeatureCard
key={feature.uid}
feature={feature}
features={features}
setFeatures={setFeatures}
/>
))}
{!isAddingFeature ? (
<Card
className="w-[350px] h-[200px] flex justify-center items-center border-green-400 border-dashed m-auto hover:bg-green-400 transition cursor-pointer"
onClick={() => setIsAddingFeature(true)}
>
<CardContent className="p-0 flex flex-col items-center gap-1">
<p>Create a new feature</p>
<Plus />
</CardContent>
</Card>
) : (
<Card className="w-[350px] h-min-[200px] m-auto flex justify-center items-center">
<CardContent className="p-4 gap-4 flex flex-col items-center">
<input
type="text"
className="w-full border-1 border-muted-foreground rounded-md p-2"
placeholder="Feature name"
value={addName}
onChange={(e) => {
setAddName(e.target.value);
}}
/>
<textarea
className="w-full border-1 border-muted-foreground rounded-md p-2"
placeholder="Feature description"
rows={4}
value={addDescription}
onChange={(e) => {
setAddDescription(e.target.value);
}}
/>
<div className="flex gap-2">
<Button
variant="ghost"
onClick={() => {
setIsAddingFeature(false);
setAddName("");
setAddDescription("");
}}
>
Cancel
</Button>
<Button
onClick={() => {
setFeatures([
...features,
{
name: addName,
description: addDescription,
uid: uuidv4(),
},
]);
setAddName("");
setAddDescription("");
setIsAddingFeature(false);
}}
>
Create
</Button>
</div>
</CardContent>
</Card>
)}
</div>
</ScrollArea>
</div>
<DialogFooter>
<Button
onClick={async () => {
setPage(2);
await addFeatures().then((features) => {
generateTasks(features);
});
}}
>
These look good, generate subtasks.
</Button>
<Button
variant="ghost"
onClick={() => {
setDialogOpen(false);
addFeatures();
setPage(0);
}}
size="sm"
>
Skip this for now.
</Button>
</DialogFooter>
</DialogContent>
)}
{page == 2 && (
<DialogContent>
<DialogHeader>
<DialogTitle>Generating tasks...</DialogTitle>
</DialogHeader>
</DialogContent>
)}
{page == 3 && (
<DialogContent className="min-w-[800px]">
<DialogHeader>
<DialogTitle>Here are your generated tasks.</DialogTitle>
</DialogHeader>
<div className="flex flex-col space-y-2">
<ScrollArea className="w-full h-[600px]">
<div className="grid grid-cols-2 gap-4">
{tasks.map((task) => (
<Card key={task.uid} className="w-[300px] m-auto">
<CardHeader>
<CardTitle>{task.name}</CardTitle>
</CardHeader>
<CardContent>
<p>Description: {task.description}</p>
<p>Priority: {task.priority}</p>
<p>Order: {task.order}</p>
</CardContent>
</Card>
))}
</div>
</ScrollArea>
</div>
</DialogContent>
)}
</Dialog>
<Button
onClick={() => {
setDialogOpen(true);
}}
>
Generate Features
</Button>
</>
);
}
/* <DialogContent className="min-w-[800px]">
<DialogHeader>
<DialogTitle>Features</DialogTitle>
<DialogDescription className="flex flex-col space-y-2">
<ScrollArea className="w-full h-[600px]">
<div className="grid grid-cols-2 gap-4">
{features.map((feature) => (
<Card key={feature.name} className="w-[300px]">
<CardHeader>
<CardTitle>{feature.name}</CardTitle>
</CardHeader>
<CardContent>
<p>Description: {feature.description}</p>
<p>ID: {feature.id}</p>
<p>Priority: {feature.priority}</p>
<p>Order: {feature.order}</p>
</CardContent>
</Card>
))}
</div>
</ScrollArea>
</DialogDescription>
<DialogFooter>
<Button className="w-full" onClick={generateTasks}>
Generate Tasks
</Button>
</DialogFooter>
</DialogHeader>
</DialogContent>; */