Ersetzt die Platzhalter: Hostliste mit Suche und Gruppenfilter per GET (teilbare Links), Formulare fuer Hosts und Gruppen, Hostdetail als Geruest und Gruppendetail mit Mitgliederauswahl. Bearbeiten-Knoepfe entscheidet der Server per darf(); jede Seite prueft die Sitzung selbst. Die Detailseite rendert HostKeyAktionen (#009) und BootstrapAktionen (#010) als feste Platzhalter mit fertigen Props, damit die Folge-Issues nur ihre eigene Datei ersetzen. Formulare senden per onSubmit, weil React ein <form action> nach jeder Antwort zuruecksetzt und Eingaben bei Feldfehlern sonst verloren gingen. Unter md zeigt die Hostliste Karten statt Tabelle, damit Status und Host-Key auf 390 px ohne Wischen sichtbar bleiben. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
3.8 KiB
TypeScript
96 lines
3.8 KiB
TypeScript
import { Server } from "lucide-react";
|
|
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { adresseMitPort } from "@/components/hosts/Anzeige";
|
|
import { GruppenFormular } from "@/components/hosts/GruppenFormular";
|
|
import { Breadcrumb } from "@/components/ui/Breadcrumb";
|
|
import { Karte } from "@/components/ui/Karte";
|
|
import { Leerzustand } from "@/components/ui/Leerzustand";
|
|
import { Seitenkopf } from "@/components/ui/Seitenkopf";
|
|
import { Statuspunkt } from "@/components/ui/Statuspunkt";
|
|
import { Tabelle, TabellenZeile, TabellenZelle } from "@/components/ui/Tabelle";
|
|
import { datum } from "@/lib/format";
|
|
import { darf } from "@/server/auth/rechte";
|
|
import { sitzungErforderlich } from "@/server/auth/sitzungspflicht";
|
|
import { gruppeDetail, hostAuswahl } from "@/server/queries/hosts";
|
|
|
|
export const metadata: Metadata = { title: "Hostgruppe" };
|
|
|
|
/** Gruppendetail mit Mitgliedern; wer Hosts verwalten darf, bearbeitet hier auch (#008). */
|
|
export default async function HostgruppeSeite({ params }: { params: Promise<{ id: string }> }) {
|
|
const { id } = await params;
|
|
const { benutzer } = await sitzungErforderlich("lesen", `/hostgruppen/${id}`);
|
|
const gruppe = await gruppeDetail(id);
|
|
if (!gruppe) notFound();
|
|
const verwalten = darf(benutzer.rolle, "hostsVerwalten");
|
|
const hosts = verwalten ? await hostAuswahl() : [];
|
|
const anzahl = gruppe.mitglieder.length;
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6 sm:gap-7">
|
|
<Seitenkopf
|
|
oben={<Breadcrumb pfad={[{ titel: "Hostgruppen", href: "/hostgruppen" }, { titel: gruppe.name }]} />}
|
|
titel={gruppe.name}
|
|
untertitel={
|
|
<>
|
|
{gruppe.beschreibung && <span className="mb-1 block">{gruppe.beschreibung}</span>}
|
|
{anzahl === 1 ? "1 Mitglied" : `${anzahl} Mitglieder`} · angelegt am {datum(gruppe.angelegtAm)}
|
|
</>
|
|
}
|
|
/>
|
|
|
|
<div className="grid items-start gap-6 xl:grid-cols-[minmax(0,1.2fr)_minmax(0,1fr)]">
|
|
<Karte titel="Mitglieder">
|
|
{anzahl === 0 ? (
|
|
<Leerzustand
|
|
icon={<Server className="size-5" />}
|
|
titel="Keine Mitglieder"
|
|
text={
|
|
verwalten
|
|
? "Hosts lassen sich unter „Bearbeiten“ auswählen."
|
|
: "Dieser Gruppe ist noch kein Host zugeordnet."
|
|
}
|
|
/>
|
|
) : (
|
|
<Tabelle beschriftung="Mitglieder" spalten={[{ titel: "Host" }, { titel: "Status" }]}>
|
|
{gruppe.mitglieder.map((h) => (
|
|
<TabellenZeile key={h.id}>
|
|
<TabellenZelle className="min-w-0">
|
|
<Link
|
|
href={`/hosts/${h.id}`}
|
|
className="text-text hover:text-accent -my-2 inline-flex min-h-11 items-center font-mono text-[13.5px] font-medium break-all transition-colors"
|
|
>
|
|
{h.name}
|
|
</Link>
|
|
<p className="text-muted font-mono text-[12.5px] break-all">
|
|
{adresseMitPort(h.adresse, h.port)}
|
|
</p>
|
|
</TabellenZelle>
|
|
<TabellenZelle className="whitespace-nowrap">
|
|
<Statuspunkt status={h.status} />
|
|
</TabellenZelle>
|
|
</TabellenZeile>
|
|
))}
|
|
</Tabelle>
|
|
)}
|
|
</Karte>
|
|
|
|
{verwalten && (
|
|
<Karte titel="Bearbeiten" untertitel="Name, Beschreibung und Mitglieder">
|
|
<GruppenFormular
|
|
vorgabe={{
|
|
id: gruppe.id,
|
|
name: gruppe.name,
|
|
beschreibung: gruppe.beschreibung ?? "",
|
|
hostIds: gruppe.mitglieder.map((m) => m.id),
|
|
}}
|
|
hosts={hosts}
|
|
/>
|
|
</Karte>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|