fix: add labels to container API, null-safe ContainerGroup, error boundary

- containers/route.ts: include c.Labels in response (was missing, caused null crash)
- ContainerGroup.tsx: null-safe getTraefikUrl with optional chaining
- error.tsx: add Next.js error boundary for graceful client-side error handling
This commit is contained in:
2026-02-13 13:15:36 -05:00
parent b14489ff59
commit e00c6efcda
3 changed files with 35 additions and 3 deletions

View File

@@ -47,6 +47,7 @@ export async function GET() {
state: c.State, state: c.State,
status: c.Status, status: c.Status,
ports: c.Ports || [], ports: c.Ports || [],
labels: c.Labels || {},
cpu, cpu,
memory, memory,
stats: statsText, stats: statsText,

30
src/app/error.tsx Normal file
View File

@@ -0,0 +1,30 @@
"use client";
import { useEffect } from "react";
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error("Dashboard error:", error);
}, [error]);
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="bg-gray-800 rounded-lg border border-gray-700 p-8 max-w-md text-center">
<h2 className="text-xl font-bold text-white mb-4">Something went wrong</h2>
<p className="text-gray-400 mb-6 text-sm">{error.message}</p>
<button
onClick={reset}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm transition-colors"
>
Try again
</button>
</div>
</div>
);
}

View File

@@ -28,7 +28,8 @@ export default function ContainerGroup({
} }
}; };
const getTraefikUrl = (labels: Record<string, string>) => { const getTraefikUrl = (labels?: Record<string, string> | null) => {
if (!labels || typeof labels !== "object") return null;
for (const key of Object.keys(labels)) { for (const key of Object.keys(labels)) {
if (key.includes("traefik.http.routers") && key.endsWith(".rule")) { if (key.includes("traefik.http.routers") && key.endsWith(".rule")) {
const match = labels[key].match(/Host\(`([^`]+)`\)/); const match = labels[key].match(/Host\(`([^`]+)`\)/);
@@ -96,7 +97,7 @@ export default function ContainerGroup({
<span className="text-gray-300">{container.status}</span> <span className="text-gray-300">{container.status}</span>
</div> </div>
{container.ports.length > 0 && ( {container.ports && container.ports.length > 0 && (
<div className="flex items-center justify-between text-xs"> <div className="flex items-center justify-between text-xs">
<span className="text-gray-400">Ports</span> <span className="text-gray-400">Ports</span>
<span className="text-gray-300"> <span className="text-gray-300">