feat: restyle dashboard — card grid (desktop), row list (mobile), status badges
This commit is contained in:
@@ -1,17 +1,31 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useTransition } from 'react';
|
||||
import { Server, ServiceStatus, getStatusColor, serviceStatusToString } from '@/lib/schemas';
|
||||
import { Server, ServiceStatus, serviceStatusToString } from '@/lib/schemas';
|
||||
import {
|
||||
startServerEventAction,
|
||||
restartServerEventAction,
|
||||
stopServerEventAction
|
||||
} from '@/lib/actions/servers';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { StatusDot } from '@/components/ui/StatusDot';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { GhButton } from '@/components/ui/GhButton';
|
||||
|
||||
interface ServerCardProps {
|
||||
server: Server;
|
||||
}
|
||||
|
||||
function statusBadgeVariant(status: ServiceStatus): 'green' | 'yellow' | 'gray' {
|
||||
if (status === ServiceStatus.Running) return 'green';
|
||||
if (
|
||||
[ServiceStatus.Starting, ServiceStatus.Restarting, ServiceStatus.Stopping].includes(status)
|
||||
)
|
||||
return 'yellow';
|
||||
return 'gray';
|
||||
}
|
||||
|
||||
export function ServerCard({ server }: ServerCardProps) {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const router = useRouter();
|
||||
@@ -36,82 +50,68 @@ export function ServerCard({ server }: ServerCardProps) {
|
||||
ServiceStatus.Restarting,
|
||||
ServiceStatus.Starting,
|
||||
ServiceStatus.Stopping,
|
||||
ServiceStatus.Unknown
|
||||
ServiceStatus.Unknown,
|
||||
].includes(server.status);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="overflow-hidden rounded-lg border border-gray-700 bg-gray-800 shadow-lg">
|
||||
<Link href={`/dashboard/server/${server.id}`} className="block">
|
||||
<div className="p-6">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-white">{server.name}</h3>
|
||||
<div className="mt-2 flex items-center">
|
||||
<span
|
||||
className={`inline-block h-2 w-2 rounded-full ${getStatusColor(server.status)} mr-2`}
|
||||
/>
|
||||
<span className="text-sm text-gray-300 capitalize">
|
||||
{serviceStatusToString(server.status)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="text-gray-400 hover:text-white">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-hidden rounded-lg border border-gh-border bg-gh-canvas transition-colors hover:border-gh-blue/40">
|
||||
<Link href={`/dashboard/server/${server.id}`} className="block p-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<span className="text-sm font-semibold text-gh-primary">{server.name}</span>
|
||||
<StatusDot status={server.status} />
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-gh-muted">
|
||||
{server.state?.track || 'No track'} · {server.state?.playerCount ?? 0} players
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid grid-cols-2 gap-4 text-sm text-gray-300">
|
||||
<div>
|
||||
<span className="text-gray-500">Track:</span>
|
||||
<span className="ml-2">{server.state?.track || 'N/A'}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Players:</span>
|
||||
<span className="ml-2">{server.state?.playerCount || 0}</span>
|
||||
</div>
|
||||
<div className="mt-3 grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<div className="text-xs text-gh-subtle">Players</div>
|
||||
<div className="text-sm font-semibold text-gh-primary">
|
||||
{server.state?.playerCount ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-gh-subtle">Track</div>
|
||||
<div className="truncate text-sm font-semibold text-gh-primary">
|
||||
{server.state?.track || 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<div className="flex justify-between gap-2 bg-gray-900 px-4 py-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={startServer}
|
||||
disabled={server.status === ServiceStatus.Running || isPending || disabled}
|
||||
className="rounded bg-green-600 px-3 py-2 text-xs font-medium text-white transition-colors hover:bg-green-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
Start
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={restartServer}
|
||||
disabled={server.status === ServiceStatus.Stopped || isPending || disabled}
|
||||
className="rounded bg-yellow-600 px-3 py-2 text-xs font-medium text-white transition-colors hover:bg-yellow-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
Restart
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={stopServer}
|
||||
disabled={server.status === ServiceStatus.Stopped || isPending || disabled}
|
||||
className="rounded bg-red-600 px-3 py-2 text-xs font-medium text-white transition-colors hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<Badge variant={statusBadgeVariant(server.status)}>
|
||||
{serviceStatusToString(server.status)}
|
||||
</Badge>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<div className="flex gap-1.5 border-t border-gh-border-muted bg-gh-base px-4 py-2">
|
||||
<GhButton
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={startServer}
|
||||
disabled={server.status === ServiceStatus.Running || isPending || disabled}
|
||||
>
|
||||
Start
|
||||
</GhButton>
|
||||
<GhButton
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={restartServer}
|
||||
disabled={server.status === ServiceStatus.Stopped || isPending || disabled}
|
||||
>
|
||||
Restart
|
||||
</GhButton>
|
||||
<GhButton
|
||||
variant="danger-outline"
|
||||
size="sm"
|
||||
onClick={stopServer}
|
||||
disabled={server.status === ServiceStatus.Stopped || isPending || disabled}
|
||||
>
|
||||
Stop
|
||||
</GhButton>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user