| 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> |
| ); |
| } |