| import Link from 'next/link'; | |
| import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Badge } from '@/components/ui/badge'; | |
| import type { Deployment, DeploymentStatus } from '@/lib/types'; | |
| import { ExternalLink, Zap, AlertTriangle, CheckCircle2, Hourglass, PowerOff, Layers } from 'lucide-react'; | |
| import { cn } from '@/lib/utils'; | |
| interface DeploymentCardProps { | |
| deployment: Deployment; | |
| } | |
| function getStatusBadgeVariant(status: DeploymentStatus) { | |
| switch (status) { | |
| case 'succeeded': | |
| return 'default'; | |
| case 'deploying': | |
| return 'secondary'; | |
| case 'pending': | |
| return 'outline'; | |
| case 'failed': | |
| return 'destructive'; | |
| case 'stopped': | |
| return 'outline'; | |
| default: | |
| return 'outline'; | |
| } | |
| } | |
| function getStatusIcon(status: DeploymentStatus) { | |
| switch (status) { | |
| case 'succeeded': | |
| return <CheckCircle2 className="h-5 w-5 text-green-500" />; | |
| case 'deploying': | |
| return <Hourglass className="h-5 w-5 text-blue-500 animate-spin" />; | |
| case 'pending': | |
| return <Hourglass className="h-5 w-5 text-yellow-500" />; | |
| case 'failed': | |
| return <AlertTriangle className="h-5 w-5 text-red-500" />; | |
| case 'stopped': | |
| return <PowerOff className="h-5 w-5 text-gray-500" />; | |
| default: | |
| return <Zap className="h-5 w-5 text-muted-foreground" />; | |
| } | |
| } | |
| export function DeploymentCard({ deployment }: DeploymentCardProps) { | |
| return ( | |
| <Card className="flex flex-col h-full shadow-md hover:shadow-xl transition-shadow duration-300 ease-in-out"> | |
| <CardHeader className="pb-3"> | |
| <div className="flex justify-between items-start gap-2"> | |
| <div className="flex-1"> | |
| <CardTitle className="text-xl font-semibold text-primary flex items-center"> | |
| <Layers className="mr-2.5 h-5 w-5" /> | |
| {deployment.appName} | |
| </CardTitle> | |
| <CardDescription className="text-xs mt-1">Deployed: {new Date(deployment.createdAt).toLocaleDateString()}</CardDescription> | |
| </div> | |
| <Badge variant={getStatusBadgeVariant(deployment.status)} className="capitalize flex items-center gap-1.5 px-3 py-1 text-xs shadow-sm"> | |
| {getStatusIcon(deployment.status)} | |
| <span>{deployment.status}</span> | |
| </Badge> | |
| </div> | |
| </CardHeader> | |
| <CardContent className="space-y-3 flex-grow"> | |
| <p className="text-sm text-muted-foreground"> | |
| Region: <span className="font-medium text-foreground">{deployment.region || 'N/A'}</span> | |
| </p> | |
| {deployment.lastDeployedAt && ( | |
| <p className="text-sm text-muted-foreground"> | |
| Last Activity: <span className="font-medium text-foreground">{new Date(deployment.lastDeployedAt).toLocaleDateString()}</span> | |
| </p> | |
| )} | |
| {deployment.url && ( | |
| <div className="mt-2"> | |
| <a | |
| href={deployment.url} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-sm text-accent hover:underline flex items-center font-medium" | |
| > | |
| Visit App <ExternalLink className="ml-1.5 h-4 w-4" /> | |
| </a> | |
| </div> | |
| )} | |
| </CardContent> | |
| <CardFooter className="flex justify-end pt-4 border-t mt-auto"> | |
| <Button asChild variant="outline" size="sm" className="shadow-sm hover:shadow-md"> | |
| <Link href={`/dashboard/deployments/${deployment.id}`}>Manage</Link> | |
| </Button> | |
| </CardFooter> | |
| </Card> | |
| ); | |
| } |