// src/components/onboarding/configure-step/configure-step.tsx 'use client' import { useState, useEffect } from 'react' import { PlusCircle, Loader2, AlertTriangle, Info } from 'lucide-react' import { useTheme } from 'next-themes' import { useOnboarding } from '@/components/onboarding/store' import { useGQLClient } from '@/context' import { useWallet } from '@/context/WalletContext' import { Button } from '@workspace/ui/components/button' import { Input } from '@workspace/ui/components/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@workspace/ui/components/select' import { Checkbox } from '@workspace/ui/components/checkbox' import { Label } from '@workspace/ui/components/label' import { Alert, AlertDescription } from '@workspace/ui/components/alert' import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card' import { Badge } from '@workspace/ui/components/badge' import { toast } from 'sonner' import { adaptDeployers } from '../../../utils/typeAdapters'; interface Deployer { deployerLrn: string deployerApiUrl: string minimumPayment?: string baseDomain: string } interface Organization { id: string name: string slug: string } export function ConfigureStep() { const { nextStep, previousStep, setFormData, formData } = useOnboarding() const { resolvedTheme } = useTheme() const [mounted, setMounted] = useState(false) // Backend data const [deployers, setDeployers] = useState([]) const [organizations, setOrganizations] = useState([]) const [isLoadingDeployers, setIsLoadingDeployers] = useState(true) const [isLoadingOrgs, setIsLoadingOrgs] = useState(true) // Form state const [deployOption, setDeployOption] = useState<'auction' | 'lrn'>( formData.deploymentType as ('auction' | 'lrn') || 'lrn' // Default to LRN for simplicity ) const [numberOfDeployers, setNumberOfDeployers] = useState( formData.deployerCount || "1" ) const [maxPrice, setMaxPrice] = useState( formData.maxPrice || "1000" ) const [selectedLrn, setSelectedLrn] = useState( formData.selectedLrn || "" ) const [selectedOrg, setSelectedOrg] = useState( formData.selectedOrg || "" ) const [envVars, setEnvVars] = useState<{ key: string; value: string; environments: string[] }[]>([ { key: '', value: '', environments: ['Production'] } ]) // Contexts const gqlClient = useGQLClient() const { wallet } = useWallet() // Handle hydration mismatch by waiting for mount useEffect(() => { setMounted(true) }, []) // Fetch deployers and organizations on mount useEffect(() => { if (mounted) { fetchDeployers() fetchOrganizations() } }, [mounted]) // Initialize environment variables from formData if available useEffect(() => { if (formData.environmentVariables && Array.isArray(formData.environmentVariables)) { setEnvVars(formData.environmentVariables.length > 0 ? formData.environmentVariables : [ { key: '', value: '', environments: ['Production'] } ]) } }, [formData.environmentVariables]) // Fetch deployers from backend const fetchDeployers = async () => { try { setIsLoadingDeployers(true) const deployersData = await gqlClient.getDeployers() console.log('Available deployers:', deployersData) setDeployers(adaptDeployers(deployersData.deployers || [])); // Auto-select first deployer if available and none selected if (deployersData.deployers && deployersData.deployers.length > 0 && !selectedLrn) { setSelectedLrn(deployersData.deployers[0]!.deployerLrn) } } catch (error) { console.error('Error fetching deployers:', error) toast.error('Failed to load deployers') } finally { setIsLoadingDeployers(false) } } // Fetch organizations from backend const fetchOrganizations = async () => { try { setIsLoadingOrgs(true) const orgsData = await gqlClient.getOrganizations() console.log('Available organizations:', orgsData) setOrganizations(orgsData.organizations || []) // Auto-select first organization if available and none selected if (orgsData.organizations && orgsData.organizations.length > 0 && !selectedOrg) { setSelectedOrg(orgsData.organizations[0]!.slug) } } catch (error) { console.error('Error fetching organizations:', error) toast.error('Failed to load organizations') } finally { setIsLoadingOrgs(false) } } // Add an empty environment variable row const addEnvVar = () => { setEnvVars([...envVars, { key: '', value: '', environments: ['Production'] }]) } // Remove environment variable row const removeEnvVar = (index: number) => { if (envVars.length > 1) { setEnvVars(envVars.filter((_, i) => i !== index)) } } // Update environment variable const updateEnvVar = (index: number, field: 'key' | 'value', value: string) => { const newEnvVars = [...envVars] if (newEnvVars[index]) { newEnvVars[index][field] = value } setEnvVars(newEnvVars) } // Toggle environment for variable const toggleEnvironment = (index: number, environment: string) => { const newEnvVars = [...envVars] if (newEnvVars[index]?.environments) { const currentEnvs = newEnvVars[index].environments if (currentEnvs.includes(environment)) { newEnvVars[index].environments = currentEnvs.filter(env => env !== environment) } else { newEnvVars[index].environments = [...currentEnvs, environment] } // Ensure at least one environment is selected if (newEnvVars[index].environments.length === 0) { newEnvVars[index].environments = ['Production'] } setEnvVars(newEnvVars) } } // Toggle deployment option const toggleDeployOption = (option: 'auction' | 'lrn') => { setDeployOption(option) } // Get selected deployer details const selectedDeployer = deployers.find(d => d.deployerLrn === selectedLrn) // Validate form const canProceed = () => { if (deployOption === 'lrn' && !selectedLrn) return false if (!selectedOrg) return false if (!wallet?.address) return false return true } // Handle next step const handleNext = () => { if (!canProceed()) { toast.error('Please complete all required fields') return } // Filter out empty environment variables const validEnvVars = envVars.filter(env => env.key.trim() && env.value.trim()) // Save configuration to form data setFormData({ deploymentType: deployOption, deployerCount: numberOfDeployers, maxPrice: maxPrice, selectedLrn: selectedLrn, selectedOrg: selectedOrg, paymentAddress: wallet?.address, environmentVariables: validEnvVars }) nextStep() } // Don't render UI until after mount to prevent hydration mismatch if (!mounted) { return null } // Determine if dark mode is active const isDarkMode = resolvedTheme === 'dark' // Get deployment mode info const isTemplateMode = formData.deploymentMode === 'template' const selectedItem = isTemplateMode ? formData.template?.name : formData.githubRepo return (
{/* Configure icon and header */}

Configure

Define the deployment type

{/* Project Summary */} Project Summary
Type: {isTemplateMode ? 'Template' : 'Repository'}
Source: {selectedItem}
Project Name: {formData.projectName}
{/* Organization Selection */}
{isLoadingOrgs ? (
Loading organizations...
) : organizations.length === 0 ? ( No organizations found. You need to be part of at least one organization. ) : ( )}
{/* Deployment options */}
{deployOption === 'lrn' ? ( /* LRN Deployment Settings */
{isLoadingDeployers ? (
Loading deployers...
) : deployers.length === 0 ? ( No deployers available. Please contact support. ) : ( <> {/* Deployer Details */} {selectedDeployer && (
API URL: {selectedDeployer.deployerApiUrl}
Base Domain: {selectedDeployer.baseDomain}
{selectedDeployer.minimumPayment && (
Minimum Payment: {selectedDeployer.minimumPayment}
)}
)} )}
) : ( /* Auction Settings */
)} {/* Payment Address */}
{wallet?.address || 'No wallet connected'}
{/* Environment Variables */}
{envVars.map((envVar, index) => (
updateEnvVar(index, 'key', e.target.value)} className={`bg-transparent ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`} /> updateEnvVar(index, 'value', e.target.value)} className={`bg-transparent ${isDarkMode ? 'border-zinc-700' : 'border-zinc-300'}`} />
Environments: {['Production', 'Preview', 'Development'].map((env) => (
toggleEnvironment(index, env)} />
))} {envVars.length > 1 && ( )}
))}
{/* Navigation buttons */}
) }