From 27dae2ce50c9eb59e08cac35e4177b0485c9dbfb Mon Sep 17 00:00:00 2001 From: Eduard Wisch Date: Tue, 19 May 2026 12:57:19 +0200 Subject: [PATCH] Auftragsliste nach Kunde+Ort, IP-Scanner mit Adapter-Erkennung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auftragsliste: Kundenname + Adresse sind jetzt die Überschrift, die Auftragsnummer nur noch Kleingedrucktes — Aufträge sind so ohne Nummer-im-Kopf wiederzufinden. Auftragsnotiz wird mit angezeigt. IP-Scanner: ist kein Netzbereich angegeben, nutzt das Tool den im Protokoll hinterlegten; ist auch der leer, fragt es den aktiven WLAN-/LAN-Adapter ab und scannt dessen Subnetz. Der ermittelte Netzbereich wird ins Protokoll übernommen. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/lib/tools/netzwerk/ipscan.ts | 46 +++++++++++++++++++++++++++---- src/lib/types.ts | 2 ++ src/routes/auftraege/+page.svelte | 44 +++++++++++++++++++++++------ 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/lib/tools/netzwerk/ipscan.ts b/src/lib/tools/netzwerk/ipscan.ts index 33ff9d8..de3f40a 100644 --- a/src/lib/tools/netzwerk/ipscan.ts +++ b/src/lib/tools/netzwerk/ipscan.ts @@ -1,6 +1,11 @@ /** * Tool: IP-Scanner — findet Geräte im Subnetz. * Die gefundenen Geräte werden ins Protokoll übernommen. + * + * Netzbereich-Logik: ist im Dialog nichts angegeben, wird der im + * Protokoll hinterlegte Netzbereich genutzt; ist auch der leer, + * fragt das Tool den aktiven WLAN-/LAN-Adapter ab und scannt + * dessen Subnetz direkt. */ import { scanner } from '../../scanner'; @@ -16,17 +21,48 @@ export const ipScanTool: Tool = { params: [ { key: 'subnet', - label: 'Netzbereich (CIDR)', + label: 'Netzbereich (CIDR) — leer = aktiver Adapter', type: 'text', - placeholder: '192.168.1.0/24', + placeholder: 'leer lassen → automatisch über WLAN/LAN', }, ], async run(ctx) { - const subnet = String(ctx.params.subnet || ctx.protocol.subnet || '192.168.1.0/24'); + // 1. Dialog-Eingabe 2. im Protokoll hinterlegter Netzbereich + let subnet = String(ctx.params.subnet ?? '').trim() || String(ctx.protocol.subnet ?? '').trim(); + let source: 'eingabe' | 'protokoll' | 'adapter' = subnet + ? String(ctx.params.subnet ?? '').trim() + ? 'eingabe' + : 'protokoll' + : 'adapter'; + + // 3. Nichts angegeben → aktiven Adapter abfragen und dessen Subnetz scannen + if (!subnet) { + try { + const local = await scanner.getLocalSubnet(); + subnet = String(local.subnet ?? '').trim(); + } catch { + /* kein Adapter ermittelbar — unten abgefangen */ + } + } + + if (!subnet) { + return { + label: 'Kein Netzbereich — WLAN/LAN nicht aktiv?', + result: { error: 'Netzbereich konnte nicht ermittelt werden' }, + measureStatus: 2, + }; + } + + // Ermittelten Netzbereich ins Protokoll übernehmen, wenn dort noch leer + if (!String(ctx.protocol.subnet ?? '').trim()) { + ctx.protocol.subnet = subnet; + } + const { devices } = await scanner.ipScan({ subnet }); + const via = source === 'adapter' ? ' (Adapter erkannt)' : ''; return { - label: `${devices.length} Geräte im Netz ${subnet}`, - result: { subnet, count: devices.length }, + label: `${devices.length} Geräte im Netz ${subnet}${via}`, + result: { subnet, count: devices.length, quelle: source }, measureStatus: devices.length > 0 ? 0 : 1, devices: devices.map((d) => ({ ip: d.ip, diff --git a/src/lib/types.ts b/src/lib/types.ts index e55cbe4..06cd22c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -34,6 +34,8 @@ export interface Order { status: number; /** true wenn Status 0/1/2 (aktiver Auftrag) */ open: boolean; + /** Öffentliche Auftragsnotiz — oft die eigentliche Beschreibung */ + note?: string; protocolCount?: number; customer?: Partial; } diff --git a/src/routes/auftraege/+page.svelte b/src/routes/auftraege/+page.svelte index b3c4030..d1ee624 100644 --- a/src/routes/auftraege/+page.svelte +++ b/src/routes/auftraege/+page.svelte @@ -18,6 +18,24 @@ let searchTimer: ReturnType; + /** Kunde + Ort als eine Zeile — das, was man im Kopf hat */ + function orderLocation(order: Order): string { + const c = order.customer; + if (!c) return ''; + const ort = [c.zip, c.town].filter(Boolean).join(' '); + return [c.address, ort].filter(Boolean).join(', '); + } + + /** Datum kurz (akzeptiert Sekunden- wie Millisekunden-Timestamp) */ + function fmtDate(ts: number): string { + const ms = ts < 1e11 ? ts * 1000 : ts; + return new Date(ms).toLocaleDateString('de-DE', { + day: '2-digit', + month: '2-digit', + year: '2-digit', + }); + } + async function load() { loading = true; loadError = ''; @@ -102,26 +120,36 @@ {:else} {#each orders as order (order.id)}