'use client'; import { useEffect, useState, useRef } from "react"; import { createProjectWithOptions, createProjectFromJsonWithOptions } from "../lib/project-creation-utils"; import { useRouter, useSearchParams } from 'next/navigation'; import clsx from 'clsx'; import { Textarea } from "@/components/ui/textarea"; import { Button } from "@/components/ui/button"; import { FolderOpenIcon, InformationCircleIcon } from "@heroicons/react/24/outline"; import { USE_MULTIPLE_PROJECTS } from "@/app/lib/feature_flags"; import { HorizontalDivider } from "@/components/ui/horizontal-divider"; import { Tooltip } from "@heroui/react"; import { BillingUpgradeModal } from "@/components/common/billing-upgrade-modal"; import { Workflow } from '@/app/lib/types/workflow_types'; import { loadSharedWorkflow } from '@/app/actions/shared-workflow.actions'; import { Modal } from '@/components/ui/modal'; import { Upload, Send, X } from "lucide-react"; // Add glow animation styles const glowStyles = ` @keyframes glow { 0% { border-color: rgba(99, 102, 241, 0.3); box-shadow: 0 0 8px 1px rgba(99, 102, 241, 0.2); } 50% { border-color: rgba(99, 102, 241, 0.6); box-shadow: 0 0 12px 2px rgba(99, 102, 241, 0.4); } 100% { border-color: rgba(99, 102, 241, 0.3); box-shadow: 0 0 8px 1px rgba(99, 102, 241, 0.2); } } @keyframes glow-dark { 0% { border-color: rgba(129, 140, 248, 0.3); box-shadow: 0 0 8px 1px rgba(129, 140, 248, 0.2); } 50% { border-color: rgba(129, 140, 248, 0.6); box-shadow: 0 0 12px 2px rgba(129, 140, 248, 0.4); } 100% { border-color: rgba(129, 140, 248, 0.3); box-shadow: 0 0 8px 1px rgba(129, 140, 248, 0.2); } } .animate-glow { animation: glow 2s ease-in-out infinite; border-width: 2px; } .dark .animate-glow { animation: glow-dark 2s ease-in-out infinite; border-width: 2px; } `; const TabType = { Describe: 'describe', Import: 'import', } as const; type TabState = typeof TabType[keyof typeof TabType]; const isNotBlankTemplate = (tab: TabState): boolean => true; const tabStyles = clsx( "px-4 py-2 text-sm font-medium", "rounded-lg", "focus:outline-none focus:ring-2 focus:ring-indigo-500/20 dark:focus:ring-indigo-400/20", "transition-colors duration-150" ); const activeTabStyles = clsx( "bg-white dark:bg-gray-800", "text-gray-900 dark:text-gray-100", "shadow-sm", "border border-gray-200 dark:border-gray-700" ); const inactiveTabStyles = clsx( "text-gray-600 dark:text-gray-400", "hover:bg-gray-50 dark:hover:bg-gray-750" ); const largeSectionHeaderStyles = clsx( "text-lg font-medium", "text-gray-900 dark:text-gray-100" ); const textareaStyles = clsx( "w-full", "rounded-lg p-3", "border border-gray-200 dark:border-gray-700", "bg-white dark:bg-gray-800", "hover:bg-gray-50 dark:hover:bg-gray-750", "focus:shadow-inner focus:ring-2 focus:ring-indigo-500/20 dark:focus:ring-indigo-400/20", "placeholder:text-gray-400 dark:placeholder:text-gray-500", "transition-all duration-200" ); const emptyTextareaStyles = clsx( "animate-glow", "border-indigo-500/40 dark:border-indigo-400/40", "shadow-[0_0_8px_1px_rgba(99,102,241,0.2)] dark:shadow-[0_0_8px_1px_rgba(129,140,248,0.2)]" ); const tabButtonStyles = clsx( "border border-gray-200 dark:border-gray-700" ); const selectedTabStyles = clsx( tabButtonStyles, "text-gray-900 dark:text-gray-100", "text-base" ); const unselectedTabStyles = clsx( tabButtonStyles, "text-gray-900 dark:text-gray-100", "text-sm" ); interface CreateProjectProps { defaultName: string; onOpenProjectPane: () => void; isProjectPaneOpen: boolean; hideHeader?: boolean; } export function CreateProject({ defaultName, onOpenProjectPane, isProjectPaneOpen, hideHeader = false }: CreateProjectProps) { const [selectedTab, setSelectedTab] = useState(TabType.Describe); const [customPrompt, setCustomPrompt] = useState(""); const [name, setName] = useState(defaultName); const [promptError, setPromptError] = useState(null); const [billingError, setBillingError] = useState(null); 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); const [autoCreateLoading, setAutoCreateLoading] = useState(false); const searchParams = useSearchParams(); const urlPrompt = searchParams.get('prompt'); const urlTemplate = searchParams.get('template'); const sharedId = searchParams.get('shared'); // Add this effect to update name when defaultName changes useEffect(() => { setName(defaultName); }, [defaultName]); // Pre-populate prompt from URL if available useEffect(() => { if (urlPrompt && !customPrompt) { setCustomPrompt(urlPrompt); } }, [urlPrompt, customPrompt]); // Add effect to handle URL parameters for auto-creation useEffect(() => { const handleAutoCreate = async () => { // Auto-create from template/prompt, or import from shared id if ((urlPrompt || urlTemplate || sharedId) && !importLoading && !autoCreateLoading) { setAutoCreateLoading(true); try { if (sharedId) { // Load workflow via server action (by id) const workflowObj = await loadSharedWorkflow(sharedId); await createProjectFromJsonWithOptions({ workflowJson: JSON.stringify(workflowObj), router, onError: (error) => { setBillingError(error instanceof Error ? error.message : String(error)); } }); } else { await createProjectWithOptions({ template: urlTemplate || undefined, prompt: urlPrompt || undefined, router, onError: (error) => { // Auto-creation failed, show the form instead setBillingError(error instanceof Error ? error.message : String(error)); setAutoCreateLoading(false); } }); } } catch (error) { console.error('Error auto-creating project:', error); setBillingError(error instanceof Error ? error.message : String(error)); setAutoCreateLoading(false); } } }; handleAutoCreate(); }, [urlPrompt, urlTemplate, sharedId, importLoading, autoCreateLoading, router]); // Inject glow animation styles useEffect(() => { const styleSheet = document.createElement("style"); styleSheet.innerText = glowStyles; document.head.appendChild(styleSheet); return () => { document.head.removeChild(styleSheet); }; }, []); // Removed dropdownRef and isExamplesDropdownOpen effect const handleTabChange = (tab: TabState) => { setSelectedTab(tab); setImportError(null); if (tab === TabType.Describe) { setCustomPrompt(''); 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 await createProjectFromJsonWithOptions({ workflowJson: importedJson, router, onError: (error) => { setBillingError(error instanceof Error ? error.message : String(error)); } }); return; } if (!customPrompt.trim()) { setPromptError("Prompt cannot be empty"); return; } await createProjectWithOptions({ template: urlTemplate || undefined, prompt: customPrompt, router, onError: (error) => { setBillingError(error instanceof Error ? error.message : String(error)); } }); } catch (error) { console.error('Error creating project:', error); } } async function handleSubmitWithTemplate(template: string) { await createProjectWithOptions({ template, router, onError: (error) => { setBillingError(error instanceof Error ? error.message : String(error)); } }); } return ( <>
{USE_MULTIPLE_PROJECTS && !hideHeader && ( <>

Create new assistant

{!isProjectPaneOpen && ( )}
)} {/* Show loading state when auto-creating */} {autoCreateLoading && (

Creating your assistant...

)} {/* Show form if not auto-creating */} {!autoCreateLoading && (
{ e.preventDefault(); handleSubmit(); }} > {/* Main Section: What do you want to build? and Import JSON */}

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}
) : ( <>