Initial NUC Portal dashboard
- 17 internal services with live health status - 28 external bookmarks organized by category - Dark/light mode toggle with persistence - Cmd+K search filtering - Health API polling every 30 seconds Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
67
src/components/ServiceCard.tsx
Normal file
67
src/components/ServiceCard.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
'use client';
|
||||
|
||||
import { Service } from '@/lib/services';
|
||||
import { HealthStatus } from '@/lib/PortalContext';
|
||||
import { Icon } from './Icons';
|
||||
|
||||
interface ServiceCardProps {
|
||||
service: Service;
|
||||
status: HealthStatus;
|
||||
}
|
||||
|
||||
const statusColors: Record<HealthStatus, string> = {
|
||||
running: 'bg-emerald-500',
|
||||
stopped: 'bg-red-500',
|
||||
unknown: 'bg-slate-400 dark:bg-stone-500',
|
||||
loading: 'bg-amber-500 animate-pulse',
|
||||
};
|
||||
|
||||
const statusLabels: Record<HealthStatus, string> = {
|
||||
running: 'Running',
|
||||
stopped: 'Stopped',
|
||||
unknown: 'Unknown',
|
||||
loading: 'Checking...',
|
||||
};
|
||||
|
||||
export function ServiceCard({ service, status }: ServiceCardProps) {
|
||||
return (
|
||||
<a
|
||||
href={service.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group relative block p-4 bg-white dark:bg-stone-900 rounded-xl border border-slate-200 dark:border-stone-800 hover:border-slate-300 dark:hover:border-stone-700 hover:shadow-lg transition-all duration-200"
|
||||
>
|
||||
{/* Status indicator */}
|
||||
<div className="absolute top-3 right-3 flex items-center gap-1.5">
|
||||
<span
|
||||
className={`w-2 h-2 rounded-full ${statusColors[status]}`}
|
||||
title={statusLabels[status]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Icon */}
|
||||
<div className="w-10 h-10 flex items-center justify-center rounded-lg bg-slate-100 dark:bg-stone-800 mb-3 group-hover:bg-slate-200 dark:group-hover:bg-stone-700 transition-colors">
|
||||
<Icon
|
||||
name={service.icon}
|
||||
size={20}
|
||||
className="text-slate-600 dark:text-stone-400"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<h3 className="font-medium text-slate-900 dark:text-stone-100 mb-1">
|
||||
{service.name}
|
||||
</h3>
|
||||
{service.description && (
|
||||
<p className="text-sm text-slate-500 dark:text-stone-500 line-clamp-2">
|
||||
{service.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Port badge */}
|
||||
<div className="mt-3 text-xs text-slate-400 dark:text-stone-600 font-mono">
|
||||
:{service.port}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user