From 2551b34b26375683d6e4e396936446fb45363857 Mon Sep 17 00:00:00 2001 From: akhisud3195 Date: Mon, 21 Jul 2025 13:13:23 +0530 Subject: [PATCH] Move import json inside compose box and make it a picker --- .../select/components/create-project.tsx | 265 ++++++++++-------- 1 file changed, 146 insertions(+), 119 deletions(-) diff --git a/apps/rowboat/app/projects/select/components/create-project.tsx b/apps/rowboat/app/projects/select/components/create-project.tsx index 21bf280e..11bdb973 100644 --- a/apps/rowboat/app/projects/select/components/create-project.tsx +++ b/apps/rowboat/app/projects/select/components/create-project.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import { createProjectFromPrompt, createProjectFromWorkflowJson } from "@/app/actions/project_actions"; import { useRouter } from 'next/navigation'; import clsx from 'clsx'; @@ -13,7 +13,7 @@ import { Tooltip } from "@heroui/react"; import { BillingUpgradeModal } from "@/components/common/billing-upgrade-modal"; import { Workflow } from '@/app/lib/types/workflow_types'; import { Modal } from '@/components/ui/modal'; -import { FileDown, Send } from "lucide-react"; +import { Upload, Send, X } from "lucide-react"; // Add glow animation styles const glowStyles = ` @@ -136,9 +136,11 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe const [name, setName] = useState(defaultName); const [promptError, setPromptError] = useState(null); const [billingError, setBillingError] = useState(null); - const [importJson, setImportJson] = useState(""); + const [importedJson, setImportedJson] = useState(null); + const [importedFilename, setImportedFilename] = useState(null); const [importError, setImportError] = useState(null); const [importModalOpen, setImportModalOpen] = useState(false); + const fileInputRef = useRef(null); const router = useRouter(); const [importLoading, setImportLoading] = useState(false); @@ -165,13 +167,88 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe setImportError(null); if (tab === TabType.Describe) { setCustomPrompt(''); - } else if (tab === TabType.Import) { - setImportJson(''); + setImportedJson(null); + setImportedFilename(null); } }; + // Open file chooser when Import JSON is clicked + const handleImportJsonClick = () => { + if (fileInputRef.current) fileInputRef.current.value = ''; + setSelectedTab(TabType.Import); + setTimeout(() => { + fileInputRef.current?.click(); + }, 0); + }; + + // Handle file selection + const handleFileChange = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) { + // If no file selected, revert to describe view + setSelectedTab(TabType.Describe); + return; + } + setImportLoading(true); + setImportError(null); + try { + const text = await file.text(); + let parsed = Workflow.safeParse(JSON.parse(text)); + if (!parsed.success) { + setImportError('Invalid workflow JSON: ' + JSON.stringify(parsed.error.issues)); + setImportModalOpen(true); + setImportLoading(false); + setImportedJson(null); + setImportedFilename(null); + setSelectedTab(TabType.Describe); + return; + } + setImportedJson(text); + setImportedFilename(file.name); + setSelectedTab(TabType.Import); + } catch (err) { + setImportError('Invalid JSON: ' + (err instanceof Error ? err.message : String(err))); + setImportModalOpen(true); + setImportedJson(null); + setImportedFilename(null); + setSelectedTab(TabType.Describe); + } finally { + setImportLoading(false); + } + }; + + // Allow user to pick another file + const handleChooseAnother = () => { + if (fileInputRef.current) fileInputRef.current.value = ''; + setImportedJson(null); + setImportedFilename(null); + setTimeout(() => { + fileInputRef.current?.click(); + }, 0); + }; + + // Remove imported file with X button + const handleRemoveImportedFile = () => { + if (fileInputRef.current) fileInputRef.current.value = ''; + setImportedJson(null); + setImportedFilename(null); + setSelectedTab(TabType.Describe); + }; + async function handleSubmit() { try { + if (importedJson) { + // Use imported JSON + const formData = new FormData(); + formData.append('workflowJson', importedJson); + const response = await createProjectFromWorkflowJson(formData); + if ('id' in response) { + router.push(`/projects/${response.id}/workflow`); + } else { + setBillingError(response.billingError); + } + return; + } if (!customPrompt.trim()) { setPromptError("Prompt cannot be empty"); return; @@ -193,45 +270,15 @@ export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpe } } - async function handleImportSubmit(e?: React.FormEvent) { - if (e) e.preventDefault(); - setImportError(null); - setImportLoading(true); - let parsed; - try { - const json = JSON.parse(importJson); - parsed = Workflow.safeParse(json); - if (!parsed.success) { - setImportError('Invalid workflow JSON: ' + JSON.stringify(parsed.error.issues)); - setImportModalOpen(true); - setImportLoading(false); - return; - } - } catch (err) { - setImportError('Invalid JSON: ' + (err instanceof Error ? err.message : String(err))); - setImportModalOpen(true); - setImportLoading(false); - return; - } - try { - const formData = new FormData(); - formData.append('workflowJson', importJson); - const response = await createProjectFromWorkflowJson(formData); - if ('id' in response) { - router.push(`/projects/${response.id}/workflow`); - } else { - setBillingError(response.billingError); - } - } catch (err) { - setImportError('Failed to import: ' + (err instanceof Error ? err.message : String(err))); - setImportModalOpen(true); - } finally { - setImportLoading(false); - } - } - return ( <> +
{ e.preventDefault(); handleSubmit(); }} > {/* Main Section: What do you want to build? and Import JSON */}
-
-
- - -
+
+
- {selectedTab === TabType.Describe && ( -
-
-
-

- In the next step, our AI copilot will create agents for you, complete with mock-tools. -

- If you already know the specific agents and tools you need, mention them below.

Specify 'internal agents' for task agents that will not interact with the user and 'user-facing agents' for conversational agents that will interact with users.
} className="max-w-[560px]"> - - +
+
+
+

+ In the next step, our AI copilot will create agents for you, complete with mock-tools. +

+ If you already know the specific agents and tools you need, mention them below.

Specify 'internal agents' for task agents that will not interact with the user and 'user-facing agents' for conversational agents that will interact with users.
} className="max-w-[560px]"> + + +
+ {/* If a file is imported, show filename, cross button, and create button. Otherwise, show compose box. */} + {importedJson ? ( +
+
+ + {importedFilename} + +
+
- {/* Compose box with send button */} + ) : (