Auftragsliste nach Kunde+Ort, IP-Scanner mit Adapter-Erkennung
All checks were successful
Build APK / build-apk (push) Has been skipped
All checks were successful
Build APK / build-apk (push) Has been skipped
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) <noreply@anthropic.com>
This commit is contained in:
parent
26eb418c21
commit
27dae2ce50
3 changed files with 79 additions and 13 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<Customer>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,24 @@
|
|||
|
||||
let searchTimer: ReturnType<typeof setTimeout>;
|
||||
|
||||
/** 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)}
|
||||
<button
|
||||
class="flex w-full items-center gap-3 border-b border-zinc-800/60 px-3 py-3 text-left active:bg-zinc-800"
|
||||
class="flex w-full items-start gap-3 border-b border-zinc-800/60 px-3 py-3 text-left active:bg-zinc-800"
|
||||
onclick={() => openOrder(order)}
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<!-- Kunde = Überschrift, das was man wiedererkennt -->
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium">{order.ref}</span>
|
||||
<span class="truncate font-medium">{order.customer?.name || order.ref}</span>
|
||||
{#if !order.open}
|
||||
<span class="rounded bg-zinc-700 px-1.5 py-0.5 text-[10px] text-zinc-300"
|
||||
<span class="shrink-0 rounded bg-zinc-700 px-1.5 py-0.5 text-[10px] text-zinc-300"
|
||||
>abgeschlossen</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="truncate text-sm text-zinc-400">{order.customer?.name ?? ''}</div>
|
||||
<div class="truncate text-xs text-zinc-500">
|
||||
{order.customer?.zip ?? ''}
|
||||
{order.customer?.town ?? ''}
|
||||
<!-- Ort/Adresse -->
|
||||
{#if orderLocation(order)}
|
||||
<div class="truncate text-sm text-zinc-400">{orderLocation(order)}</div>
|
||||
{/if}
|
||||
<!-- Auftragsnotiz, falls vorhanden -->
|
||||
{#if order.note}
|
||||
<div class="truncate text-xs text-zinc-400">{order.note}</div>
|
||||
{/if}
|
||||
<!-- Auftragsnummer + Datum: nur Kleingedrucktes -->
|
||||
<div class="truncate text-[11px] text-zinc-500">
|
||||
{order.ref}{order.refClient ? ' · ' + order.refClient : ''}{order.date
|
||||
? ' · ' + fmtDate(order.date)
|
||||
: ''}
|
||||
</div>
|
||||
</div>
|
||||
{#if order.protocolCount && order.protocolCount > 0}
|
||||
<span class="flex items-center gap-1 text-xs text-sky-400">
|
||||
<span class="flex shrink-0 items-center gap-1 text-xs text-sky-400">
|
||||
<FileStack size={14} />{order.protocolCount}
|
||||
</span>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Reference in a new issue