'use client'; import { useState, useEffect } from 'react'; import { CheckCircle, XCircle, AlertCircle, Loader2, HardDrive, Database, Activity, Clock } from 'lucide-react'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent } from '@/components/ui/card'; import { systemApi } from '@/lib/api'; import toast from 'react-hot-toast'; import { UpdateProgress } from './UpdateProgress'; interface UpdateInfo { current_version: string; latest_version: string; update_type: 'major' | 'minor' | 'patch'; release_notes: string; released_at: string; } interface ValidationCheck { name: string; backendName: string; // Backend uses snake_case names status: 'pending' | 'checking' | 'passed' | 'failed'; message?: string; icon: React.ReactNode; } interface UpdateModalProps { updateInfo: UpdateInfo; open: boolean; onClose: () => void; } // Map backend check names to display names const CHECK_NAME_MAP: Record = { 'disk_space': 'Disk Space', 'container_health': 'Container Health', 'database_connectivity': 'Database Connectivity', 'recent_backup': 'Last Backup Age' }; export function UpdateModal({ updateInfo, open, onClose }: UpdateModalProps) { const [validationChecks, setValidationChecks] = useState([ { name: 'Disk Space', backendName: 'disk_space', status: 'pending', icon: }, { name: 'Container Health', backendName: 'container_health', status: 'pending', icon: }, { name: 'Database Connectivity', backendName: 'database_connectivity', status: 'pending', icon: }, { name: 'Last Backup Age', backendName: 'recent_backup', status: 'pending', icon: } ]); const [createBackup, setCreateBackup] = useState(true); const [isValidating, setIsValidating] = useState(false); const [validationComplete, setValidationComplete] = useState(false); const [updateStarted, setUpdateStarted] = useState(false); const [updateId, setUpdateId] = useState(null); useEffect(() => { if (open) { runValidation(); } }, [open]); const runValidation = async () => { setIsValidating(true); try { const response = await systemApi.validateUpdate(updateInfo.latest_version); const validationResults = response.data; // Update checks based on API response - match by backendName const updatedChecks = validationChecks.map(check => { const result = validationResults.checks.find((c: any) => c.name === check.backendName); if (result) { return { ...check, status: result.passed ? 'passed' : 'failed', message: result.message }; } return check; }); setValidationChecks(updatedChecks); setValidationComplete(true); } catch (error) { console.error('Validation failed:', error); toast.error('Failed to validate system for update'); // Mark all checks as failed const failedChecks = validationChecks.map(check => ({ ...check, status: 'failed' as const, message: 'Validation check failed' })); setValidationChecks(failedChecks); setValidationComplete(true); } finally { setIsValidating(false); } }; const handleStartUpdate = async () => { if (!allChecksPassed) { toast.error('Cannot start update: validation checks failed'); return; } try { const response = await systemApi.startUpdate(updateInfo.latest_version, createBackup); const data = response.data; setUpdateId(data.update_id); setUpdateStarted(true); toast.success('Update started successfully'); } catch (error) { console.error('Failed to start update:', error); toast.error('Failed to start update'); } }; const handleUpdateComplete = () => { toast.success('Update completed successfully!'); setTimeout(() => { window.location.reload(); }, 2000); }; const handleUpdateFailed = () => { toast.error('Update failed. Check logs for details.'); }; const getCheckIcon = (status: string) => { switch (status) { case 'passed': return ; case 'failed': return ; case 'checking': return ; default: return ; } }; const getCheckClass = (status: string) => { switch (status) { case 'passed': return 'bg-green-50 border-green-200'; case 'failed': return 'bg-red-50 border-red-200'; case 'checking': return 'bg-blue-50 border-blue-200'; default: return 'bg-gray-50 border-gray-200'; } }; const getUpdateTypeBadge = () => { switch (updateInfo.update_type) { case 'major': return Major Update; case 'minor': return Minor Update; case 'patch': return Patch Update; default: return Update; } }; const allChecksPassed = validationComplete && validationChecks.every(check => check.status === 'passed'); return ( Software Update Available {getUpdateTypeBadge()} Update GT 2.0 from v{updateInfo.current_version} to v{updateInfo.latest_version} {!updateStarted ? (
{/* Release Notes */}

Release Notes

{/* Pre-update Validation */}

Pre-Update Validation

{isValidating && ( Validating... )}
{validationChecks.map((check, index) => (
{check.icon}
{check.name}
{getCheckIcon(check.status)}
{check.message && (

{check.message}

)}
))}
{/* Backup Option */}
setCreateBackup(e.target.checked)} className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-600" />
{/* Action Buttons */}
) : ( )}
); }