"use client"; import { useState, useEffect } from "react"; import { useToast } from "@/components/ui/use-toast"; import { useRouter, useParams } from "next/navigation"; import { v4 as uuidv4 } from "uuid"; import * as z from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm, useFieldArray } from "react-hook-form"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { ReloadIcon, TrashIcon, Pencil2Icon, PlusIcon, } from "@radix-ui/react-icons"; import { Textarea } from "@/components/ui/textarea"; import { Card, CardContent, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { ScrollArea } from "@/components/ui/scroll-area"; import { FeatureCard } from "./feature-card"; import { Input } from "./ui/input"; const DIALOG_PAGES = { WELCOME: 0, QUESTIONS: 1, GENERATE_FEATURES: 2, GENERATED_FEATURES: 3, GENERATE_TASKS: 4, GENERATING_TASKS: 5, GENERATED_TASKS: 6, }; const questionsFormSchema = z.object({ questions: z.array( z.object({ answer: z.string().optional(), }) ), }); type Question = { question: string; answer: string; }; type Feature = { uid: string; name: string; description: string; }; export function GenerateProject({ project_name, project_description, project_stack, }: { project_name: string; project_description: string; project_stack: string; }) { const [questions, setQuestions] = useState([]); const [features, setFeatures] = useState([]); const [isAddingFeature, setIsAddingFeature] = useState(false); const [newFeatureName, setNewFeatureName] = useState(""); const [newFeatureDescription, setNewFeatureDescription] = useState(""); const [tasks, setTasks] = useState([]); const [qa, setQA] = useState([]); const [dialogOpen, setDialogOpen] = useState(false); const [loading, setLoading] = useState(false); const [dialogPage, setDialogPage] = useState(DIALOG_PAGES.WELCOME); const { toast } = useToast(); const params = useParams(); const router = useRouter(); const form = useForm>({ resolver: zodResolver(questionsFormSchema), mode: "all", defaultValues: { questions: [], }, }); const { fields, append, remove } = useFieldArray({ name: "questions", control: form.control, }); useEffect(() => { async function fetchFeatures() { try { console.log(params.projectID); const res = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/features` ); const data = await res.json(); return data.features; } catch (err) { console.error(err); toast({ variant: "destructive", title: "Failed to fetch features.", description: `${err}`, }); } } fetchFeatures().then((features) => { if (features === undefined || features.length === 0) setDialogOpen(true); setFeatures(features); }); }, [params.projectID, params.workspaceID, toast]); async function generateQuestions() { setLoading(true); try { const res = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/gen` ); const data = await res.json(); console.log(data.questions); append(data.questions); setQuestions(data.questions); setDialogPage(DIALOG_PAGES.QUESTIONS); setLoading(false); return data.questions; } catch (err) { console.error(err); } setLoading(false); } async function generateFeatures() { setLoading(true); try { const res = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/features/gen`, { method: "POST", body: JSON.stringify({ project_name, project_description, project_stack, qa, }), } ); const data = await res.json(); console.log(data.features); setFeatures(data.features); setDialogPage(DIALOG_PAGES.GENERATED_FEATURES); setLoading(false); return data.features; } catch (err) { console.error(err); } setLoading(false); } async function generateTasks() { setLoading(true); try { const res = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/features/gen/tasks`, { method: "POST", body: JSON.stringify({ project_name, project_description, project_stack, }), } ); const data = await res.json(); console.log(data.tasks); setTasks(data.tasks); await addTasks(data.tasks); setDialogPage(DIALOG_PAGES.GENERATED_TASKS); setLoading(false); return data.tasks; } catch (err) { console.error(err); } setLoading(false); } async function addQuestions(values: z.infer) { setLoading(true); try { const answer_arr = values.questions.map((q) => q.answer); const q_data: Question[] = answer_arr.map((a, i) => ({ question: questions[i], answer: String(a), })); console.log(q_data); const res = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/gen`, { method: "PUT", body: JSON.stringify(q_data), } ); console.log(res); if (!res.ok) throw new Error(res.statusText); if (res.ok) { setQA(q_data); setDialogPage(DIALOG_PAGES.GENERATE_FEATURES); } } catch (err) { console.error(err); } setLoading(false); } async function addFeatures() { setLoading(true); try { const res = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/features/add`, { method: "POST", body: JSON.stringify({ features: features.map((feature) => ({ name: feature.name, description: feature.description, project_id: params.projectID, })), }), } ); if (!res.ok) throw new Error(res.statusText); const project_features = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/features` ); const data = await project_features.json(); const deps = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/features/gen/deps`, { method: "POST", body: JSON.stringify({ project_name, project_description, project_stack, features: data.features, }), } ); const deps_data = await deps.json(); console.log(deps_data); if (res.ok) { setDialogPage(DIALOG_PAGES.GENERATE_TASKS); } } catch (err) { console.error(err); } setLoading(false); } async function addTasks(tasks: any[]) { try { const res = await fetch( `/w/${params.workspaceID}/p/${params.projectID}/tasks/add`, { method: "POST", body: JSON.stringify({ tasks, }), } ); if (!res.ok) throw new Error(res.statusText); } catch (err) { console.error(err); } } return ( {dialogPage == DIALOG_PAGES.WELCOME && ( Welcome to your project! Skalara would like to learn more about your project. This will help in the rest of the project generation process. {!loading ? ( ) : ( )} )} {dialogPage == DIALOG_PAGES.QUESTIONS && ( Project Questions Answer any questions that you feel would help Skalara learn more about your project goals. All questions are optional.
{fields.map((field, index) => ( ( Question {index + 1} {questions[index]}