Add Tailwind CSS + container list page wired to the Go backend

Verified end-to-end against the real local Docker host.
This commit is contained in:
2026-09-17 17:57:01 +02:00
parent 2128fe9cac
commit a455302b80
8 changed files with 707 additions and 2 deletions
+1
View File
@@ -0,0 +1 @@
@import 'tailwindcss';
+18
View File
@@ -0,0 +1,18 @@
export const API_BASE = 'http://localhost:8090';
export type Container = {
id: string;
names: string[];
image: string;
state: string;
status: string;
created: number;
};
export async function fetchContainers(): Promise<Container[]> {
const res = await fetch(`${API_BASE}/api/containers`, { credentials: 'include' });
if (!res.ok) {
throw new Error(`API error: ${res.status}`);
}
return res.json();
}
+1
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import '../app.css';
import favicon from '$lib/assets/favicon.svg';
let { children } = $props();
+55 -2
View File
@@ -1,2 +1,55 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
<script lang="ts">
import { fetchContainers, type Container } from '$lib/api';
let containers = $state<Container[]>([]);
let error = $state<string | null>(null);
let loading = $state(true);
$effect(() => {
fetchContainers()
.then((data) => (containers = data))
.catch((err) => (error = err.message))
.finally(() => (loading = false));
});
function stateColor(state: string) {
if (state === 'running') return 'bg-green-500/15 text-green-400';
if (state === 'exited') return 'bg-zinc-500/15 text-zinc-400';
return 'bg-amber-500/15 text-amber-400';
}
</script>
<main class="min-h-screen bg-zinc-950 p-8 text-zinc-100">
<h1 class="mb-6 text-2xl font-semibold">Docker Manager — Host local</h1>
{#if loading}
<p class="text-zinc-400">Cargando contenedores…</p>
{:else if error}
<p class="text-red-400">Error al conectar con la API: {error}</p>
{:else}
<div class="overflow-hidden rounded-lg border border-zinc-800">
<table class="w-full text-left text-sm">
<thead class="bg-zinc-900 text-zinc-400">
<tr>
<th class="px-4 py-3">Nombre</th>
<th class="px-4 py-3">Imagen</th>
<th class="px-4 py-3">Estado</th>
<th class="px-4 py-3">Status</th>
</tr>
</thead>
<tbody class="divide-y divide-zinc-800">
{#each containers as c (c.id)}
<tr class="hover:bg-zinc-900/50">
<td class="px-4 py-3 font-medium">{c.names.join(', ').replace(/^\//, '')}</td>
<td class="px-4 py-3 text-zinc-400">{c.image}</td>
<td class="px-4 py-3">
<span class="rounded-full px-2 py-1 text-xs {stateColor(c.state)}">{c.state}</span>
</td>
<td class="px-4 py-3 text-zinc-400">{c.status}</td>
</tr>
{/each}
</tbody>
</table>
</div>
{/if}
</main>