homelab-dashboard/app/components/ServiceCard.tsx
2026-04-15 21:41:48 +02:00

111 lines
3.7 KiB
TypeScript

'use client';
import { Service } from '@/lib/data';
interface ServiceCardProps {
service: Service;
}
const STATUS_CONFIG = {
online: {
bgColor: 'bg-green-50 dark:bg-green-950',
textColor: 'text-green-700 dark:text-green-300',
dotColor: 'bg-green-500',
label: 'Online',
},
warning: {
bgColor: 'bg-amber-50 dark:bg-amber-950',
textColor: 'text-amber-700 dark:text-amber-300',
dotColor: 'bg-amber-500',
label: 'Warnung',
},
offline: {
bgColor: 'bg-red-50 dark:bg-red-950',
textColor: 'text-red-700 dark:text-red-300',
dotColor: 'bg-red-500',
label: 'Offline',
},
} as const;
/**
* ServiceCard - Zeigt einen einzelnen Service mit Status, Icon und Öffnen-Button
* @param service - Das Service-Objekt mit Name, Beschreibung, Status, etc.
*/
export function ServiceCard({ service }: ServiceCardProps) {
const config = STATUS_CONFIG[service.status];
return (
<a
href={service.url}
target="_blank"
rel="noopener noreferrer"
className="group block"
>
<div className="h-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg shadow-sm hover:shadow-md transition-all duration-300 overflow-hidden hover:border-slate-300 dark:hover:border-slate-600">
<div className="p-6 flex flex-col gap-4 h-full">
{/* Header mit Icon, Name, Kategorie und Status */}
<div className="flex items-start justify-between gap-4">
<div className="flex-1 flex items-center gap-3">
{service.icon && (
<span className="text-2xl flex-shrink-0" aria-hidden="true">
{service.icon}
</span>
)}
<div className="min-w-0 flex-1">
<h3 className="text-lg font-semibold text-slate-900 dark:text-white group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors truncate">
{service.name}
</h3>
<p className="text-xs text-slate-500 dark:text-slate-400 truncate">
{service.category}
</p>
</div>
</div>
{/* Status Badge */}
<div
className={`flex items-center gap-2 px-3 py-1 rounded-full whitespace-nowrap flex-shrink-0 ${config.bgColor}`}
>
<span
className={`w-2 h-2 rounded-full ${config.dotColor} animate-pulse`}
/>
<span className={`text-xs font-medium ${config.textColor}`}>
{config.label}
</span>
</div>
</div>
{/* Beschreibung */}
<p className="text-sm text-slate-600 dark:text-slate-400 flex-grow line-clamp-2">
{service.description}
</p>
{/* Öffnen Button */}
<button
onClick={(e) => {
e.preventDefault();
window.open(service.url, '_blank');
}}
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200 flex items-center justify-center gap-2 group/btn"
aria-label={`${service.name} öffnen`}
>
Öffnen
<svg
className="w-4 h-4 group-hover/btn:translate-x-1 transition-transform"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</button>
</div>
</div>
</a>
);
}