Protokollseite: Kopfdaten einklappbar, Netzbereich-Vorschlag

Phase 6 (Bedienung unter Arbeitsbedingungen). Standort/Netzbereich/Notiz
standen bisher immer offen und schoben die Werkzeuge unter die Bildkante -
beim Kunden braucht man aber zuerst die Werkzeuge, die Kopfdaten meist nur
einmal.

- Abschnitt einklappbar, mit Zusammenfassung in der Kopfzeile. Offen genau
  dann, wenn noch nichts eingetragen ist - dann ist Ausfuellen der naechste
  sinnvolle Schritt.
- Erkanntes Subnetz als Chip zum Uebernehmen ("10.0.2.0/24 uebernehmen")
  statt Abtippen von der Statusseite.
- inputmode="decimal" auf dem Netzbereich - Zifferntastatur statt Buchstaben.

Im Emulator geprueft.
This commit is contained in:
Eduard Wisch 2026-08-16 17:48:53 +02:00
parent 9eef6177ba
commit e25b909d18

View file

@ -20,6 +20,7 @@
saveScan,
deleteScan,
} from '$lib/protocols';
import { scanner } from '$lib/scanner';
import { resumeFinishedStressSessions } from '$lib/stresstest';
import { sync } from '$lib/sync.svelte';
import { toast } from '$lib/toast.svelte';
@ -39,6 +40,20 @@
let saveScanOpen = $state(false);
let expandedScan = $state<string | null>(null);
let deleteScanId = $state<string | null>(null);
/** Kopfdaten sind zugeklappt, solange nichts eingetragen ist (siehe Kommentar im Markup) */
let kopfdatenOffen = $state(false);
/** vom Adapter erkanntes Subnetz — als antippbarer Vorschlag im Eingabefeld */
let erkanntesSubnetz = $state('');
/** Menü hinter den drei Punkten (selten gebrauchte, gefährliche Aktionen) */
let ueberlaufOffen = $state(false);
/** Kompakte Zusammenfassung für die zugeklappte Kopfzeile */
const kopfZusammenfassung = $derived(
[protocol?.location, protocol?.subnet, protocol?.note?.split('\n')[0]]
.map((t) => (t ?? '').trim())
.filter(Boolean)
.join(' · '),
);
let appStateListener: PluginListenerHandle | null = null;
@ -102,6 +117,19 @@
await persist();
}
// Kopfdaten offen lassen, solange noch nichts eingetragen ist — dann ist
// das Ausfüllen der nächste sinnvolle Schritt. Sind sie gefüllt, bleibt
// der Platz für die Werkzeuge frei.
kopfdatenOffen = !(p.location || p.subnet || p.note);
// Erkanntes Subnetz als Eingabevorschlag bereitstellen (best effort)
try {
const local = await scanner.getLocalSubnet();
erkanntesSubnetz = String(local.subnet ?? '').trim();
} catch {
/* kein Adapter aktiv — dann gibt es eben keinen Vorschlag */
}
// App wechselt in den Hintergrund (anderer App-Wechsel, Display aus) →
// sofort sichern, bevor Android den Prozess evtl. beendet.
appStateListener = await App.addListener('appStateChange', ({ isActive }) => {
@ -284,35 +312,81 @@
/>
<div class="flex-1 overflow-y-auto pb-24">
<!-- Stammdaten -->
<section class="flex flex-col gap-2 border-b border-zinc-800 p-3">
<label class="flex flex-col gap-1 text-xs text-zinc-400">
Standort
<input
class="rounded border border-zinc-700 bg-zinc-800 px-2 py-1.5 text-sm text-zinc-100"
bind:value={protocol.location}
onblur={persist}
placeholder="Gebäude / Raum"
<!--
Kopfdaten eingeklappt, Werkzeuge zuerst.
Vor Ort wird zuerst gemessen und erst danach (wenn überhaupt) beschriftet.
Vorher füllten Standort/Netzbereich/Notiz den ganzen ersten Bildschirm und
man musste an ihnen vorbeiscrollen, um überhaupt an ein Werkzeug zu
kommen. Sind bereits Daten eingetragen, zeigt die Kopfzeile sie kompakt.
-->
<section class="border-b border-zinc-800">
<button
class="flex w-full items-center gap-2 px-3 py-2.5 text-left active:bg-zinc-900"
onclick={() => (kopfdatenOffen = !kopfdatenOffen)}
>
<Icons.FileText size={15} class="shrink-0 text-zinc-500" />
<span class="min-w-0 flex-1 text-xs text-zinc-400">
{#if kopfZusammenfassung}
<span class="text-zinc-300">{kopfZusammenfassung}</span>
{:else}
Standort, Netzbereich, Notiz
{/if}
</span>
<Icons.ChevronDown
size={16}
class="shrink-0 text-zinc-500 {kopfdatenOffen ? 'rotate-180' : ''}"
/>
</label>
<label class="flex flex-col gap-1 text-xs text-zinc-400">
Netzbereich
<input
class="rounded border border-zinc-700 bg-zinc-800 px-2 py-1.5 text-sm text-zinc-100"
bind:value={protocol.subnet}
onblur={persist}
placeholder="192.168.1.0/24"
/>
</label>
<label class="flex flex-col gap-1 text-xs text-zinc-400">
Notiz
<textarea
class="rounded border border-zinc-700 bg-zinc-800 px-2 py-1.5 text-sm text-zinc-100"
rows="2"
bind:value={protocol.note}
onblur={persist}
></textarea>
</label>
</button>
{#if kopfdatenOffen}
<div class="flex flex-col gap-2 px-3 pb-3">
<label class="flex flex-col gap-1 text-xs text-zinc-400">
Standort
<input
class="rounded border border-zinc-700 bg-zinc-800 px-2 py-1.5 text-sm text-zinc-100"
bind:value={protocol.location}
onblur={persist}
placeholder="Gebäude / Raum"
/>
</label>
<label class="flex flex-col gap-1 text-xs text-zinc-400">
Netzbereich
<input
class="rounded border border-zinc-700 bg-zinc-800 px-2 py-1.5 text-sm text-zinc-100"
bind:value={protocol.subnet}
onblur={persist}
placeholder="192.168.1.0/24"
inputmode="decimal"
autocapitalize="off"
autocorrect="off"
spellcheck="false"
/>
<!-- Vorschlag aus dem tatsächlich erkannten Netz: spart das
fehleranfällige Eintippen einer CIDR-Angabe auf dem Handy -->
{#if erkanntesSubnetz && erkanntesSubnetz !== protocol.subnet}
<button
class="mt-0.5 w-fit rounded bg-zinc-800 px-2 py-1 text-[11px] text-sky-300 active:bg-zinc-700"
onclick={() => {
if (protocol) protocol.subnet = erkanntesSubnetz;
void persist();
}}
>
{erkanntesSubnetz} übernehmen
</button>
{/if}
</label>
<label class="flex flex-col gap-1 text-xs text-zinc-400">
Notiz
<textarea
class="rounded border border-zinc-700 bg-zinc-800 px-2 py-1.5 text-sm text-zinc-100"
rows="2"
bind:value={protocol.note}
onblur={persist}
></textarea>
</label>
</div>
{/if}
</section>
<!-- Werkzeuge -->