Refactor ClusterServicesComponent: enhance service card layout with improved responsiveness and action button arrangement
This commit is contained in:
@@ -200,89 +200,86 @@ export function ClusterServicesComponent() {
|
||||
<p className="text-muted-foreground">Loading services...</p>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{services.map((service) => (
|
||||
<Card key={service.name} className="p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="p-2 bg-muted rounded-lg">
|
||||
{getServiceIcon(service.name)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className="font-medium truncate">{service.name}</h3>
|
||||
{service.version && (
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{service.version}
|
||||
</Badge>
|
||||
)}
|
||||
{getStatusBadge(service)}
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mb-2">{service.description}</p>
|
||||
{typeof service.status === 'object' && service.status?.message && (
|
||||
<p className="text-xs text-muted-foreground mb-2">{service.status.message}</p>
|
||||
<Card key={service.name} className="p-4 hover:shadow-lg hover:border-primary/50 transition-all flex flex-col">
|
||||
<div className="mb-3">
|
||||
<div className="flex items-center justify-between gap-2 mb-2">
|
||||
<h3 className="font-medium truncate">{service.name}</h3>
|
||||
{service.version && (
|
||||
<Badge variant="outline" className="text-xs flex-shrink-0">
|
||||
{service.version}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
{getStatusBadge(service)}
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mb-2">{service.description}</p>
|
||||
{typeof service.status === 'object' && service.status?.message && (
|
||||
<p className="text-xs text-muted-foreground">{service.status.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Action buttons - horizontal layout with responsive icons */}
|
||||
<div className="flex flex-wrap gap-2 mt-3">
|
||||
{(
|
||||
(typeof service.status === 'string' && service.status === 'not-deployed') ||
|
||||
(typeof service.status === 'object' && String(service.status?.status) === 'not-deployed') ||
|
||||
service.deployed === false
|
||||
) && (
|
||||
{/* Action buttons */}
|
||||
<div className="flex flex-col gap-2 mt-auto pt-2 border-t">
|
||||
{(
|
||||
(typeof service.status === 'string' && service.status === 'not-deployed') ||
|
||||
(typeof service.status === 'object' && String(service.status?.status) === 'not-deployed') ||
|
||||
service.deployed === false
|
||||
) && (
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => handleInstallService(service.name)}
|
||||
disabled={isInstalling}
|
||||
className="w-full"
|
||||
>
|
||||
{isInstalling ? <Loader2 className="h-4 w-4 animate-spin mr-2" /> : <Download className="h-4 w-4 mr-2" />}
|
||||
Install
|
||||
</Button>
|
||||
)}
|
||||
{((typeof service.status === 'string' && ['deployed', 'degraded', 'progressing'].includes(service.status)) ||
|
||||
(typeof service.status === 'object' && ['deployed', 'degraded', 'progressing'].includes(service.status?.status || ''))) && (
|
||||
<>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => handleInstallService(service.name)}
|
||||
disabled={isInstalling}
|
||||
variant="outline"
|
||||
onClick={() => setStatusService(service.name)}
|
||||
className="aspect-square p-0"
|
||||
>
|
||||
{isInstalling ? <Loader2 className="h-4 w-4 animate-spin sm:mr-1" /> : <Download className="h-4 w-4 sm:mr-1" />}
|
||||
<span className="hidden sm:inline">Install</span>
|
||||
<Activity className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
{((typeof service.status === 'string' && ['deployed', 'degraded', 'progressing'].includes(service.status)) ||
|
||||
(typeof service.status === 'object' && ['deployed', 'degraded', 'progressing'].includes(service.status?.status || ''))) && (
|
||||
<>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setLogsService(service.name)}
|
||||
className="aspect-square p-0"
|
||||
>
|
||||
<FileText className="h-4 w-4" />
|
||||
</Button>
|
||||
{service.hasConfig && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setStatusService(service.name)}
|
||||
title="Status"
|
||||
onClick={() => setConfigService(service.name)}
|
||||
className="aspect-square p-0"
|
||||
>
|
||||
<Activity className="h-4 w-4 sm:mr-1" />
|
||||
<span className="hidden sm:inline">Status</span>
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setLogsService(service.name)}
|
||||
title="Logs"
|
||||
>
|
||||
<FileText className="h-4 w-4 sm:mr-1" />
|
||||
<span className="hidden sm:inline">Logs</span>
|
||||
</Button>
|
||||
{service.hasConfig && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setConfigService(service.name)}
|
||||
title="Configure"
|
||||
>
|
||||
<Settings className="h-4 w-4 sm:mr-1" />
|
||||
<span className="hidden sm:inline">Configure</span>
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
onClick={() => handleDeleteService(service.name)}
|
||||
disabled={isDeleting}
|
||||
title="Remove"
|
||||
>
|
||||
{isDeleting ? <Loader2 className="h-4 w-4 animate-spin" /> : <Trash2 className="h-4 w-4" />}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
onClick={() => handleDeleteService(service.name)}
|
||||
disabled={isDeleting}
|
||||
className="aspect-square p-0"
|
||||
>
|
||||
{isDeleting ? <Loader2 className="h-4 w-4 animate-spin" /> : <Trash2 className="h-4 w-4" />}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user