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

@@ -162,12 +162,8 @@ function EditDeviceForm({ device, onDone }: { device: DeviceWithServices; onDone
/>
</div>
<div className="flex gap-2">
<Button size="sm" variant="primary" onClick={() => mutation.mutate()} disabled={mutation.isPending}>
Speichern
</Button>
<Button size="sm" variant="ghost" onClick={onDone}>
Abbrechen
</Button>
<Button size="icon" variant="primary" onClick={() => mutation.mutate()} disabled={mutation.isPending} title="Speichern" aria-label="Speichern">💾</Button>
<Button size="icon" variant="ghost" onClick={onDone} title="Abbrechen" aria-label="Abbrechen"></Button>
</div>
</div>
</td>
@@ -203,59 +199,36 @@ function NameChangesReview({
const fieldLabel = (field: ScanNameChange["field"]) =>
field === "displayName" ? "Name" : "Kategorie";
const manual = remaining.filter((c) => c.manualOverride);
const real = remaining.filter((c) => !c.manualOverride);
function renderList(list: ScanNameChange[]) {
return (
return (
<div className="mt-2 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">
{remaining.length} erkannte Änderung(en) weichen vom gespeicherten Wert ab:
</p>
<ul className="space-y-1">
{list.map((c) => (
{remaining.map((c) => (
<li key={`${c.serviceId}:${c.field}`} 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"
size="icon"
variant="ghost"
onClick={() => setHandled((prev) => new Set(prev).add(`${c.serviceId}:${c.field}`))}
>
Behalten
</Button>
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-2 space-y-2">
{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:
</p>
{renderList(manual)}
</div>
) : null}
<button onClick={onDone} className="text-black/40 underline dark:text-white/40">
<button onClick={onDone} className="mt-2 text-black/40 underline dark:text-white/40">
Hinweis schließen
</button>
</div>
@@ -288,12 +261,8 @@ function DeviceNameSuggestionBanner({
Per Reverse-DNS gefunden: {suggestion}" als Gerätename?
</span>
<div className="flex gap-1">
<Button size="sm" variant="ghost" onClick={onDone}>
Behalten
</Button>
<Button size="sm" variant="primary" onClick={() => applyMutation.mutate()} disabled={applyMutation.isPending}>
Übernehmen
</Button>
<Button size="icon" variant="ghost" onClick={onDone} title="Behalten (Vorschlag verwerfen)" aria-label="Behalten (Vorschlag verwerfen)">✕</Button>
<Button size="icon" variant="primary" onClick={() => applyMutation.mutate()} disabled={applyMutation.isPending} title="Übernehmen" aria-label="Übernehmen">✓</Button>
</div>
</div>
</div>
@@ -327,17 +296,12 @@ function StaleServicesReview({ staleServices, onDone }: { staleServices: Service
{s.displayName} ({s.hostname}:{s.port})
</span>
<div className="flex gap-1">
<Button size="sm" variant="ghost" onClick={() => setHandled((prev) => new Set(prev).add(s.id))}>
Behalten
</Button>
<Button size="icon" variant="ghost" onClick={() => setHandled((prev) => new Set(prev).add(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>
))}
@@ -424,9 +388,7 @@ function DeviceRow({ device }: { device: DeviceWithServices }) {
<td className="px-4 py-3 text-black/60 dark:text-white/60">{device.services.length}</td>
<td className="px-4 py-3">
<div className="flex items-center justify-end gap-2">
<Button size="sm" onClick={() => setEditing(true)}>
Bearbeiten
</Button>
<Button size="icon" onClick={() => setEditing(true)} title="Bearbeiten" aria-label="Bearbeiten">✏️</Button>
<Button
size="sm"
onClick={() => {
@@ -438,13 +400,10 @@ function DeviceRow({ device }: { device: DeviceWithServices }) {
{scanMutation.isPending ? "Scanne " : "Jetzt scannen"}
</Button>
<Button
size="sm"
size="icon"
variant="danger"
onClick={() => deleteMutation.mutate()}
disabled={deleteMutation.isPending}
>
Löschen
</Button>
disabled={deleteMutation.isPending} title="Löschen" aria-label="Löschen">🗑️</Button>
</div>
</td>
</tr>