feat(hosts): Schlagworte und Notizen sichtbar machen (#040)

Gleichnamige Hosts in verschiedenen Kundennetzen liessen sich in der Liste
nicht auseinanderhalten. Schlagworte und Notizen gab es zwar schon, aber nur
im Reiter "Notizen und Tags" - und damit nutzlos zum Unterscheiden.

Jetzt stehen die Schlagworte als anklickbare Abzeichen in der Liste und oben
im Host-Detail, es gibt einen Filter dafuer, eine Notiz zeigt ein
Zettel-Symbol mit dem Text beim Ueberfahren, und beides wird mitdurchsucht -
auch von der globalen Suche.

Bewusst kein zweites Feld "Kommentar": Das haette dieselbe Angabe an zwei
Orten gehalten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-03 17:43:55 +02:00
co-authored by Claude Opus 5
parent dfb2118bac
commit b2664de62f
25 changed files with 243 additions and 9 deletions
+17 -1
View File
@@ -6,6 +6,8 @@ import { sichtbarerKunde } from "@/server/auth/sitzungspflicht";
export interface HostFilter {
q?: string;
kunde?: string;
/** Schlagwort, nach dem gefiltert wird (#040). */
tag?: string;
status?: string;
produkt?: string;
ablauf?: "30" | "14" | "abgelaufen";
@@ -20,6 +22,10 @@ export const SEITENGROESSE = 25;
function bedingung(filter: HostFilter, jetzt: Date): Prisma.HostWhereInput {
const und: Prisma.HostWhereInput[] = [];
if (filter.tag) {
und.push({ tags: { some: { name: filter.tag } } });
}
if (filter.q && filter.q.trim().length > 0) {
const suche = filter.q.trim();
und.push({
@@ -30,6 +36,8 @@ function bedingung(filter: HostFilter, jetzt: Date): Prisma.HostWhereInput {
{ subKey: { contains: suche, mode: "insensitive" } },
{ ips: { has: suche } },
{ kunde: { name: { contains: suche, mode: "insensitive" } } },
{ notizen: { contains: suche, mode: "insensitive" } },
{ tags: { some: { name: { contains: suche, mode: "insensitive" } } } },
// Suche nach einer Proxmox-Rechnungsnummer führt ebenfalls zum Host.
{
laufzeiten: {
@@ -113,6 +121,7 @@ export async function hostListe(filter: HostFilter, jetzt: Date = new Date()) {
include: {
kunde: { select: { id: true, name: true } },
standort: { select: { name: true } },
tags: { orderBy: { name: "asc" }, select: { id: true, name: true } },
},
}),
]);
@@ -181,5 +190,12 @@ export async function filterOptionen() {
orderBy: { name: "asc" },
select: { id: true, name: true },
});
return { kunden };
// Nur Schlagworte, die auch an einem sichtbaren Host hängen – sonst stünden leere zur Auswahl.
const tags = await prisma.tag.findMany({
where: { hosts: { some: kundeId ? { kundeId } : {} } },
orderBy: { name: "asc" },
select: { name: true },
});
return { kunden, tags: tags.map((tag) => tag.name) };
}
+12 -2
View File
@@ -103,7 +103,9 @@ export async function suchIndex() {
subKey: true,
ips: true,
produkt: true,
notizen: true,
kunde: { select: { name: true } },
tags: { select: { name: true } },
},
}),
prisma.kunde.findMany({
@@ -128,9 +130,17 @@ export async function suchIndex() {
titel: host.hostname,
untertitel: `${host.produkt} · ${host.kunde?.name ?? "Nicht zugeordnet"}${
host.subKey ? ` · ${host.subKey}` : ""
}`,
}${host.tags.length > 0 ? ` · ${host.tags.map((tag) => tag.name).join(", ")}` : ""}`,
href: `/hosts/${host.id}`,
begriffe: [host.hostname, host.fqdn, host.serverId, host.subKey, ...host.ips]
begriffe: [
host.hostname,
host.fqdn,
host.serverId,
host.subKey,
host.notizen,
...host.tags.map((tag) => tag.name),
...host.ips,
]
.filter((w): w is string => Boolean(w))
.map((w) => w.toLowerCase()),
})),