 f5f96ba505
			
		
	
	f5f96ba505
	
	
	
		
			
			- Updated configuration and deployment files - Improved system architecture and components - Enhanced documentation and testing - Fixed various issues and added new features 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			327 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			327 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 'use client'
 | |
| 
 | |
| import { useState, useEffect } from 'react'
 | |
| import { ChevronRightIcon, CheckCircleIcon } from '@heroicons/react/24/outline'
 | |
| import TermsAndConditions from './components/TermsAndConditions'
 | |
| import LicenseValidation from './components/LicenseValidation'
 | |
| import SystemDetection from './components/SystemDetection'
 | |
| import RepositoryConfiguration from './components/RepositoryConfiguration'
 | |
| import NetworkConfiguration from './components/NetworkConfiguration'
 | |
| import SecuritySetup from './components/SecuritySetup'
 | |
| import AIConfiguration from './components/AIConfiguration'
 | |
| import ServiceDeployment from './components/ServiceDeployment'
 | |
| import ClusterFormation from './components/ClusterFormation'
 | |
| import TestingValidation from './components/TestingValidation'
 | |
| 
 | |
| const SETUP_STEPS = [
 | |
|   {
 | |
|     id: 'terms',
 | |
|     title: 'Terms & Conditions',
 | |
|     description: 'Review and accept the software license agreement',
 | |
|     component: TermsAndConditions,
 | |
|   },
 | |
|   {
 | |
|     id: 'license',
 | |
|     title: 'License Validation',
 | |
|     description: 'Validate your CHORUS license key and email',
 | |
|     component: LicenseValidation,
 | |
|   },
 | |
|   {
 | |
|     id: 'detection',
 | |
|     title: 'System Detection',
 | |
|     description: 'Detect hardware and validate installation',
 | |
|     component: SystemDetection,
 | |
|   },
 | |
|   {
 | |
|     id: 'repository',
 | |
|     title: 'Repository Setup',
 | |
|     description: 'Configure Git repository for task management',
 | |
|     component: RepositoryConfiguration,
 | |
|   },
 | |
|   {
 | |
|     id: 'network',
 | |
|     title: 'Network Configuration',
 | |
|     description: 'Configure network and firewall settings',
 | |
|     component: NetworkConfiguration,
 | |
|   },
 | |
|   {
 | |
|     id: 'security',
 | |
|     title: 'Security Setup',
 | |
|     description: 'Configure authentication and SSH access',
 | |
|     component: SecuritySetup,
 | |
|   },
 | |
|   {
 | |
|     id: 'ai',
 | |
|     title: 'AI Integration',
 | |
|     description: 'Configure OpenAI and Ollama/Parallama',
 | |
|     component: AIConfiguration,
 | |
|   },
 | |
|   {
 | |
|     id: 'deployment',
 | |
|     title: 'Service Deployment',
 | |
|     description: 'Deploy and configure CHORUS agent services',
 | |
|     component: ServiceDeployment,
 | |
|   },
 | |
|   {
 | |
|     id: 'cluster',
 | |
|     title: 'Cluster Formation',
 | |
|     description: 'Join or create CHORUS agent cluster',
 | |
|     component: ClusterFormation,
 | |
|   },
 | |
|   {
 | |
|     id: 'testing',
 | |
|     title: 'Testing & Validation',
 | |
|     description: 'Validate configuration and test connectivity',
 | |
|     component: TestingValidation,
 | |
|   },
 | |
| ]
 | |
| 
 | |
| interface ConfigData {
 | |
|   [key: string]: any
 | |
| }
 | |
| 
 | |
| export default function SetupPage() {
 | |
|   const [currentStep, setCurrentStep] = useState(0)
 | |
|   const [completedSteps, setCompletedSteps] = useState(new Set<number>())
 | |
|   const [configData, setConfigData] = useState<ConfigData>({})
 | |
|   const [systemInfo, setSystemInfo] = useState<any>(null)
 | |
| 
 | |
|   // Load persisted data and system information on mount
 | |
|   useEffect(() => {
 | |
|     loadPersistedData()
 | |
|     fetchSystemInfo()
 | |
|   }, [])
 | |
| 
 | |
|   // Save setup state to localStorage whenever it changes
 | |
|   useEffect(() => {
 | |
|     saveSetupState()
 | |
|   }, [currentStep, completedSteps, configData])
 | |
| 
 | |
|   const loadPersistedData = () => {
 | |
|     try {
 | |
|       const savedState = localStorage.getItem('chorus-setup-state')
 | |
|       if (savedState) {
 | |
|         const state = JSON.parse(savedState)
 | |
|         setCurrentStep(state.currentStep || 0)
 | |
|         setCompletedSteps(new Set(state.completedSteps || []))
 | |
|         setConfigData(state.configData || {})
 | |
|       }
 | |
|     } catch (error) {
 | |
|       console.error('Failed to load persisted setup data:', error)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const saveSetupState = () => {
 | |
|     try {
 | |
|       const state = {
 | |
|         currentStep,
 | |
|         completedSteps: Array.from(completedSteps),
 | |
|         configData,
 | |
|         timestamp: new Date().toISOString()
 | |
|       }
 | |
|       localStorage.setItem('chorus-setup-state', JSON.stringify(state))
 | |
|     } catch (error) {
 | |
|       console.error('Failed to save setup state:', error)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const clearPersistedData = () => {
 | |
|     try {
 | |
|       localStorage.removeItem('chorus-setup-state')
 | |
|       // Reset state to initial values
 | |
|       setCurrentStep(0)
 | |
|       setCompletedSteps(new Set<number>())
 | |
|       setConfigData({})
 | |
|     } catch (error) {
 | |
|       console.error('Failed to clear persisted data:', error)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const fetchSystemInfo = async () => {
 | |
|     try {
 | |
|       const response = await fetch('/api/setup/system')
 | |
|       if (response.ok) {
 | |
|         const result = await response.json()
 | |
|         setSystemInfo(result.system_info)
 | |
|       }
 | |
|     } catch (error) {
 | |
|       console.error('Failed to fetch system info:', error)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const handleStepComplete = (stepIndex: number, data: any) => {
 | |
|     console.log('Setup Page: Step complete', { stepIndex, data, currentConfigData: configData })
 | |
|     setCompletedSteps(prev => new Set([...prev, stepIndex]))
 | |
|     setConfigData(prev => {
 | |
|       const newConfigData = { ...prev, ...data }
 | |
|       console.log('Setup Page: Updated configData', { prev, data, newConfigData })
 | |
|       return newConfigData
 | |
|     })
 | |
|     
 | |
|     // Auto-advance to next step
 | |
|     if (stepIndex < SETUP_STEPS.length - 1) {
 | |
|       setCurrentStep(stepIndex + 1)
 | |
|     } else {
 | |
|       // Setup is complete, clear persisted data after a delay
 | |
|       setTimeout(() => {
 | |
|         clearPersistedData()
 | |
|       }, 2000)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const handleStepBack = () => {
 | |
|     if (currentStep > 0) {
 | |
|       setCurrentStep(currentStep - 1)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const CurrentStepComponent = SETUP_STEPS[currentStep].component
 | |
| 
 | |
|   // Check if we're resuming from saved data
 | |
|   const isResuming = currentStep > 0 || completedSteps.size > 0 || Object.keys(configData).length > 0
 | |
| 
 | |
|   return (
 | |
|     <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
 | |
|       <div className="mb-8">
 | |
|         <h1 className="heading-hero mb-3">
 | |
|           CHORUS Agent Setup
 | |
|         </h1>
 | |
|         <p className="text-body">
 | |
|           Configure your distributed agent orchestration platform in {SETUP_STEPS.length} simple steps.
 | |
|         </p>
 | |
|       </div>
 | |
| 
 | |
|       {/* Resume Setup Notification (Info Panel) */}
 | |
|       {isResuming && (
 | |
|         <div className="mb-8 panel panel-info p-6">
 | |
|           <div className="flex items-start justify-between">
 | |
|             <div className="flex items-start">
 | |
|               <div className="flex-shrink-0">
 | |
|                 <svg className="h-5 w-5 text-ocean-600 dark:text-ocean-300 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 | |
|                   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
 | |
|                 </svg>
 | |
|               </div>
 | |
|               <div className="ml-3">
 | |
|                 <h3 className="text-sm font-medium panel-title">
 | |
|                   Setup Progress Restored
 | |
|                 </h3>
 | |
|                 <p className="text-small panel-body mt-1">
 | |
|                   Your previous setup progress has been restored. You're currently on step {currentStep + 1} of {SETUP_STEPS.length}.
 | |
|                   {completedSteps.size > 0 && ` You've completed ${completedSteps.size} step${completedSteps.size !== 1 ? 's' : ''}.`}
 | |
|                 </p>
 | |
|               </div>
 | |
|             </div>
 | |
|             <button
 | |
|               onClick={clearPersistedData}
 | |
|               className="btn-text"
 | |
|             >
 | |
|               Start Over
 | |
|             </button>
 | |
|           </div>
 | |
|         </div>
 | |
|       )}
 | |
| 
 | |
|       <div className="grid grid-cols-1 lg:grid-cols-4 gap-12">
 | |
|         {/* Progress Sidebar */}
 | |
|         <div className="lg:col-span-1">
 | |
|           <div className="card sticky top-8 setup-progress">
 | |
|             <h2 className="heading-subsection mb-6">
 | |
|               Setup Progress
 | |
|             </h2>
 | |
|             <nav className="space-y-2">
 | |
|               {SETUP_STEPS.map((step, index) => {
 | |
|                 const isCompleted = completedSteps.has(index)
 | |
|                 const isCurrent = index === currentStep
 | |
|                 const isAccessible = index <= currentStep || completedSteps.has(index)
 | |
| 
 | |
|                 return (
 | |
|                   <button
 | |
|                     key={step.id}
 | |
|                     onClick={() => isAccessible && setCurrentStep(index)}
 | |
|                     disabled={!isAccessible}
 | |
|                     className={`w-full text-left progress-step ${
 | |
|                       isCurrent
 | |
|                         ? 'progress-step-current'
 | |
|                         : isCompleted
 | |
|                         ? 'progress-step-completed'
 | |
|                         : isAccessible
 | |
|                         ? 'progress-step-accessible'
 | |
|                         : 'progress-step-disabled'
 | |
|                     }`}
 | |
|                   >
 | |
|                     <div className="flex items-center">
 | |
|                       <div className="flex-shrink-0 mr-3">
 | |
|                         {isCompleted ? (
 | |
|                           <CheckCircleIcon className="h-5 w-5 text-eucalyptus-600" />
 | |
|                         ) : (
 | |
|                           <div className={`w-5 h-5 rounded-full border-2 flex items-center justify-center text-xs font-medium ${
 | |
|                             isCurrent
 | |
|                               ? 'border-chorus-secondary bg-chorus-secondary text-white'
 | |
|                               : 'border-gray-600 text-gray-500'
 | |
|                           }`}>
 | |
|                             {index + 1}
 | |
|                           </div>
 | |
|                         )}
 | |
|                       </div>
 | |
|                       <div className="flex-1 min-w-0">
 | |
|                         <div className="text-sm font-medium truncate">
 | |
|                           {step.title}
 | |
|                         </div>
 | |
|                         <div className="text-xs opacity-75 truncate">
 | |
|                           {step.description}
 | |
|                         </div>
 | |
|                       </div>
 | |
|                       {isAccessible && !isCompleted && (
 | |
|                         <ChevronRightIcon className="h-4 w-4 opacity-50" />
 | |
|                       )}
 | |
|                     </div>
 | |
|                   </button>
 | |
|                 )
 | |
|               })}
 | |
|             </nav>
 | |
|             
 | |
|             <div className="mt-8 pt-6 border-t border-chorus-border-defined">
 | |
|               <div className="text-small mb-3">
 | |
|                 Progress: {completedSteps.size} of {SETUP_STEPS.length} steps
 | |
|               </div>
 | |
|               <div className="w-full bg-chorus-border-invisible rounded-sm h-2">
 | |
|                 <div 
 | |
|                   className="bg-chorus-secondary h-2 rounded-sm transition-all duration-500"
 | |
|                   style={{ width: `${(completedSteps.size / SETUP_STEPS.length) * 100}%` }}
 | |
|                 />
 | |
|               </div>
 | |
|             </div>
 | |
|           </div>
 | |
|         </div>
 | |
| 
 | |
|         {/* Main Content */}
 | |
|         <div className="lg:col-span-3">
 | |
|           <div className="card">
 | |
|             <div className="mb-8">
 | |
|               <div className="flex items-center justify-between mb-3">
 | |
|                 <h2 className="heading-section">
 | |
|                   {SETUP_STEPS[currentStep].title}
 | |
|                 </h2>
 | |
|                 <div className="text-ghost">
 | |
|                   Step {currentStep + 1} of {SETUP_STEPS.length}
 | |
|                 </div>
 | |
|               </div>
 | |
|               <p className="text-body">
 | |
|                 {SETUP_STEPS[currentStep].description}
 | |
|               </p>
 | |
|             </div>
 | |
| 
 | |
|             <CurrentStepComponent
 | |
|               systemInfo={systemInfo}
 | |
|               configData={configData}
 | |
|               onComplete={(data: any) => handleStepComplete(currentStep, data)}
 | |
|               onBack={currentStep > 0 ? handleStepBack : undefined}
 | |
|               isCompleted={completedSteps.has(currentStep)}
 | |
|             />
 | |
|           </div>
 | |
|         </div>
 | |
|       </div>
 | |
|     </div>
 | |
|   )
 | |
| }
 |