round15: manuelle Aenderungen nicht mehr nachfragen, 7-Tage-Schwelle fuer verschwundene Geraete, Icon-Buttons mit Tooltip, fixierte mobile Kopfleiste, Scroll-to-top-Button

This commit is contained in:
2026-07-23 12:32:19 +02:00
parent 70edeeed38
commit 7035d4ebda
12 changed files with 168 additions and 186 deletions

View File

@@ -127,57 +127,30 @@ function NameChangesReview({
if (nameChanges.length === 0) return null;
const fieldLabel = (field: ScanNameChange["field"]) => (field === "displayName" ? "Name" : "Kategorie");
const manual = nameChanges.filter((c) => c.manualOverride);
const real = nameChanges.filter((c) => !c.manualOverride);
function renderList(list: ScanNameChange[]) {
return (
return (
<div className="mt-3 rounded-xl border border-blue-500/30 bg-blue-500/5 p-3 text-xs">
<p className="mb-2 font-medium text-blue-700 dark:text-blue-400">
{nameChanges.length} erkannte Änderung(en) weichen vom gespeicherten Wert ab:
</p>
<ul className="max-h-60 space-y-1 overflow-y-auto">
{list.map((c) => (
{nameChanges.map((c) => (
<li key={nameChangeKey(c)} className="flex items-center justify-between gap-2">
<span className="text-black/70 dark:text-white/70">
{c.serviceHostname}:{c.servicePort} {fieldLabel(c.field)}: {c.current ?? ""}" → „
{c.suggested}"
</span>
<div className="flex gap-1">
<Button size="sm" variant="ghost" onClick={() => onResolve(nameChangeKey(c))}>
Behalten
</Button>
<Button size="icon" variant="ghost" onClick={() => onResolve(nameChangeKey(c))} title="Behalten (Vorschlag verwerfen)" aria-label="Behalten (Vorschlag verwerfen)"></Button>
<Button
size="sm"
size="icon"
variant="primary"
onClick={() => applyMutation.mutate(c)}
disabled={applyMutation.isPending}
>
Übernehmen
</Button>
disabled={applyMutation.isPending} title="Übernehmen" aria-label="Übernehmen"></Button>
</div>
</li>
))}
</ul>
);
}
return (
<div className="mt-3 space-y-3">
{real.length > 0 ? (
<div className="rounded-xl border border-blue-500/30 bg-blue-500/5 p-3 text-xs">
<p className="mb-2 font-medium text-blue-700 dark:text-blue-400">
{real.length} erkannte Änderung(en) weichen vom gespeicherten Wert ab:
</p>
{renderList(real)}
</div>
) : null}
{manual.length > 0 ? (
<div className="rounded-xl border border-black/10 bg-black/[0.02] p-3 text-xs dark:border-white/10 dark:bg-white/[0.02]">
<p className="mb-2 font-medium text-black/60 dark:text-white/60">
{manual.length} Abweichung(en), weil du diesen Wert bereits manuell angepasst hast der
Scan schlägt lediglich erneut den automatisch erkannten Wert vor, es hat sich nichts am
Dienst selbst geändert:
</p>
{renderList(manual)}
</div>
) : null}
</div>
);
}
@@ -214,17 +187,12 @@ function StaleServicesReview({
{s.displayName} ({s.hostname}:{s.port})
</span>
<div className="flex gap-1">
<Button size="sm" variant="ghost" onClick={() => onResolve(s.id)}>
Behalten
</Button>
<Button size="icon" variant="ghost" onClick={() => onResolve(s.id)} title="Behalten (Vorschlag verwerfen)" aria-label="Behalten (Vorschlag verwerfen)"></Button>
<Button
size="sm"
size="icon"
variant="danger"
onClick={() => deleteMutation.mutate(s.id)}
disabled={deleteMutation.isPending}
>
Entfernen
</Button>
disabled={deleteMutation.isPending} title="Entfernen" aria-label="Entfernen">🗑</Button>
</div>
</li>
))}
@@ -266,17 +234,12 @@ function DeviceChangesReview({
{c.deviceHostname} {fieldLabel(c.field)}: {c.current}" → „{c.suggested}"
</span>
<div className="flex gap-1">
<Button size="sm" variant="ghost" onClick={() => onResolve(`${c.deviceId}:${c.field}`)}>
Behalten
</Button>
<Button size="icon" variant="ghost" onClick={() => onResolve(`${c.deviceId}:${c.field}`)} title="Behalten (Vorschlag verwerfen)" aria-label="Behalten (Vorschlag verwerfen)"></Button>
<Button
size="sm"
size="icon"
variant="primary"
onClick={() => applyMutation.mutate(c)}
disabled={applyMutation.isPending}
>
Übernehmen
</Button>
disabled={applyMutation.isPending} title="Übernehmen" aria-label="Übernehmen"></Button>
</div>
</li>
))}
@@ -317,17 +280,12 @@ function StaleDevicesReview({
{d.hostname} ({d.ip})
</span>
<div className="flex gap-1">
<Button size="sm" variant="ghost" onClick={() => onResolve(d.id)}>
Behalten
</Button>
<Button size="icon" variant="ghost" onClick={() => onResolve(d.id)} title="Behalten (Vorschlag verwerfen)" aria-label="Behalten (Vorschlag verwerfen)"></Button>
<Button
size="sm"
size="icon"
variant="danger"
onClick={() => deleteMutation.mutate(d.id)}
disabled={deleteMutation.isPending}
>
Löschen
</Button>
disabled={deleteMutation.isPending} title="Löschen" aria-label="Löschen">🗑</Button>
</div>
</li>
))}