import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Play, RotateCcw, Clock, CheckCircle, XCircle, Loader2, ChevronDown, Terminal } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { formatDistanceToNow } from 'date-fns'; import { deploymentsApi } from '@/lib/api'; interface Deployment { id: string; service_id: string; commit_hash: string | null; status: 'pending' | 'building' | 'deploying' | 'deployed' | 'failed' | 'rolling_back'; image_name: string; image_tag: string; build_log: string; runtime_log: string; error: string | null; started_at: string | null; completed_at: string | null; created_at: string; } interface DeploymentsPanelProps { serviceId: string; serviceName: string; } interface StatusConfig { color: string; icon: typeof Clock; label: string; animate?: boolean; } const statusConfig: Record = { pending: { color: 'bg-gray-500', icon: Clock, label: 'Pending' }, building: { color: 'bg-blue-500', icon: Loader2, label: 'Building', animate: true }, deploying: { color: 'bg-yellow-500', icon: Loader2, label: 'Deploying', animate: true }, deployed: { color: 'bg-green-500', icon: CheckCircle, label: 'Deployed' }, failed: { color: 'bg-red-500', icon: XCircle, label: 'Failed' }, rolling_back: { color: 'bg-orange-500', icon: RotateCcw, label: 'Rolling Back', animate: true }, }; function _DeploymentsPanel({ serviceId, serviceName: _serviceName }: DeploymentsPanelProps) { const [expandedDeployment, setExpandedDeployment] = useState(null); const queryClient = useQueryClient(); const { data: deployments, isLoading } = useQuery({ queryKey: ['deployments', serviceId], queryFn: async () => { const response = await deploymentsApi.getDeployments(serviceId); return response.deployments as Deployment[]; }, refetchInterval: 5000, }); const createDeployment = useMutation({ mutationFn: async (data: { commit_hash?: string; branch?: string }) => { const response = await deploymentsApi.createDeployment(serviceId, { trigger: 'manual', ...data, }); return response; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['deployments', serviceId] }); }, }); const rollbackDeployment = useMutation({ mutationFn: async (deploymentId: string) => { const response = await deploymentsApi.rollbackDeployment(deploymentId); return response; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['deployments', serviceId] }); }, }); if (isLoading) { return (
); } return (

Deployments

{!deployments || deployments.length === 0 ? ( No deployments yet. Click "Deploy" to create your first deployment. ) : (
{deployments.map((deployment) => { const config = statusConfig[deployment.status] || statusConfig.pending; const StatusIcon = config.icon; const isExpanded = expandedDeployment === deployment.id; return ( setExpandedDeployment(isExpanded ? null : deployment.id)} >
{deployment.commit_hash ? deployment.commit_hash.slice(0, 7) : 'Manual Deploy'} {config.label}
{formatDistanceToNow(new Date(deployment.created_at), { addSuffix: true, })}
{deployment.status === 'deployed' && ( )}
Image: {deployment.image_name}:{deployment.image_tag}
{deployment.commit_hash && (
Commit: {deployment.commit_hash}
)} {deployment.started_at && (
Started: {new Date(deployment.started_at).toLocaleString()}
)} {deployment.completed_at && (
Completed: {new Date(deployment.completed_at).toLocaleString()}
)}
{deployment.error && (

Error:

{deployment.error}

)}
); })}
)}
); } function DeploymentLogs({ deploymentId }: { deploymentId: string }) { const [activeTab, setActiveTab] = useState<'build' | 'runtime'>('build'); const { data: logs, isLoading } = useQuery({ queryKey: ['deployment-logs', deploymentId], queryFn: async () => { const response = await deploymentsApi.getDeployment(deploymentId); return response.deployment; }, }); if (isLoading) { return (
); } const currentLogs = activeTab === 'build' ? logs?.build_log : logs?.runtime_log; return (
          {currentLogs || 'No logs available'}
        
); }