Konsolidiere Spec-Kurzbeschreibung in ein gemeinsames Modul
main.js, result-overlay.js und session-list.js loesten dieselbe Aufgabe
(Spec-Felder zu einer Lesehilfe zusammenfassen) bislang mit drei leicht
unterschiedlichen Implementierungen - unterschiedliche Feldauswahl und
unterschiedlicher Umgang mit fehlenden Werten. Neues src/ui/describe-spec.js
buendelt das in describeSpec(spec, { placeholders }): ohne Platzhalter fuer
die kurze Treffer-Rueckmeldung und die "Zuletzt"-Zeile, mit Platzhalter fuer
die Sitzungsliste, wo feste Feldreihenfolge Wiedererkennung schafft.
Nutzt bei main.js zugleich die bereits vorhandene, jetzt exportierte
nextStackId aus session.js statt einer Zeile-fuer-Zeile-Kopie derselben
Buchstaben-Vergabe (nextFreeStackId entfaellt komplett).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+4
-18
@@ -6,7 +6,7 @@ import { runOcr, isOcrAvailable } from './ocr.js';
|
|||||||
import { recognize } from './pipeline.js';
|
import { recognize } from './pipeline.js';
|
||||||
import {
|
import {
|
||||||
createSession, proposeAssignment, commitAssignment,
|
createSession, proposeAssignment, commitAssignment,
|
||||||
undoLast, moveEntry, removeEntry,
|
undoLast, moveEntry, removeEntry, nextStackId,
|
||||||
} from './session.js';
|
} from './session.js';
|
||||||
import { saveSession, loadSession, clearSession } from './storage.js';
|
import { saveSession, loadSession, clearSession } from './storage.js';
|
||||||
import { renderScanView } from './ui/scan-view.js';
|
import { renderScanView } from './ui/scan-view.js';
|
||||||
@@ -14,6 +14,7 @@ import { showResult } from './ui/result-overlay.js';
|
|||||||
import { askForStack } from './ui/ambiguous-dialog.js';
|
import { askForStack } from './ui/ambiguous-dialog.js';
|
||||||
import { askResumeSession } from './ui/resume-dialog.js';
|
import { askResumeSession } from './ui/resume-dialog.js';
|
||||||
import { renderSessionList } from './ui/session-list.js';
|
import { renderSessionList } from './ui/session-list.js';
|
||||||
|
import { describeSpec } from './ui/describe-spec.js';
|
||||||
|
|
||||||
const app = document.querySelector('#app');
|
const app = document.querySelector('#app');
|
||||||
const store = window.localStorage;
|
const store = window.localStorage;
|
||||||
@@ -64,22 +65,7 @@ function syncView() {
|
|||||||
|
|
||||||
/** Kurzbeschreibung fuer die "Zuletzt"-Zeile. */
|
/** Kurzbeschreibung fuer die "Zuletzt"-Zeile. */
|
||||||
function describeEntry(spec, stackId) {
|
function describeEntry(spec, stackId) {
|
||||||
const parts = [
|
return `${describeSpec(spec)} → Stapel ${stackId}`;
|
||||||
spec.capacityGb ? `${spec.capacityGb}GB` : '?',
|
|
||||||
spec.speed ?? '?',
|
|
||||||
spec.formFactor ?? '',
|
|
||||||
].filter(Boolean);
|
|
||||||
return `${parts.join(' ')} → Stapel ${stackId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Naechster noch nicht vergebener Stapel-Buchstabe - fuer den Fall, dass der Nutzer im Rot-Dialog ausdruecklich einen neuen Stapel waehlt. */
|
|
||||||
function nextFreeStackId(targetSession) {
|
|
||||||
const used = new Set(targetSession.stacks.map((stack) => stack.id));
|
|
||||||
for (const letter of 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') {
|
|
||||||
if (!used.has(letter)) return letter;
|
|
||||||
}
|
|
||||||
// Nach Z weiter mit A2, B2, ... - in der Praxis nie erreicht (siehe session.js).
|
|
||||||
return `A${targetSession.stacks.length}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,7 +110,7 @@ async function processCapture(getFrame) {
|
|||||||
stackId = answer.stackId;
|
stackId = answer.stackId;
|
||||||
} else {
|
} else {
|
||||||
// "neuer Stapel": unabhaengig von jeder Vermutung der naechste freie Buchstabe.
|
// "neuer Stapel": unabhaengig von jeder Vermutung der naechste freie Buchstabe.
|
||||||
stackId = nextFreeStackId(targetSession);
|
stackId = nextStackId(targetSession);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Rein darstellend: baut aus Spec-Feldern eine kurze, deutsche Lesehilfe.
|
||||||
|
// Kein window/document noetig - liefert nur eine Zeichenkette.
|
||||||
|
|
||||||
|
const PLACEHOLDER = '?';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fasst Kapazitaet, Rank, Geschwindigkeit und Bauform eines Specs zu einer
|
||||||
|
* kurzen Lesehilfe zusammen. Frueher gab es davon drei leicht
|
||||||
|
* unterschiedliche Fassungen (main.js, result-overlay.js, session-list.js) -
|
||||||
|
* mit unterschiedlichen Feldern und unterschiedlichem Umgang mit fehlenden
|
||||||
|
* Werten. Diese eine Fassung deckt beide Beduerfnisse ueber einen Schalter ab:
|
||||||
|
*
|
||||||
|
* - `placeholders: false` (Vorgabe): fehlende Felder werden weggelassen,
|
||||||
|
* ein vollstaendig leeres Spec ergibt "ohne Angaben". Passend fuer die
|
||||||
|
* kurz eingeblendete Treffer-Rueckmeldung und die "Zuletzt"-Zeile, wo ein
|
||||||
|
* Platzhalter pro fehlendem Feld mehr verwirrt als hilft.
|
||||||
|
* - `placeholders: true`: jedes Feld erscheint, fehlende als "?". Passend
|
||||||
|
* fuer die Sitzungsliste, wo die feste Reihenfolge der vier Felder ueber
|
||||||
|
* viele Stapel hinweg Wiedererkennung schafft.
|
||||||
|
*
|
||||||
|
* @param {object} spec
|
||||||
|
* @param {{placeholders?: boolean}} [options]
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function describeSpec(spec, { placeholders = false } = {}) {
|
||||||
|
const fields = [
|
||||||
|
spec.capacityGb ? `${spec.capacityGb}GB` : null,
|
||||||
|
spec.rank ?? null,
|
||||||
|
spec.speed ?? null,
|
||||||
|
spec.formFactor ?? null,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (placeholders) {
|
||||||
|
return fields.map((value) => value ?? PLACEHOLDER).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const present = fields.filter(Boolean);
|
||||||
|
return present.length > 0 ? present.join(' ') : 'ohne Angaben';
|
||||||
|
}
|
||||||
+13
-21
@@ -1,27 +1,19 @@
|
|||||||
function beschreibung(spec) {
|
import { describeSpec } from './describe-spec.js';
|
||||||
const teile = [
|
|
||||||
spec.capacityGb ? `${spec.capacityGb}GB` : null,
|
|
||||||
spec.rank,
|
|
||||||
spec.speed,
|
|
||||||
spec.formFactor,
|
|
||||||
].filter(Boolean);
|
|
||||||
return teile.join(' ') || 'ohne Angaben';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Haelt pro Root die aktuell sichtbare Rueckmeldung samt Zeitgeber fest. */
|
/** Haelt pro Root die aktuell sichtbare Rueckmeldung samt Zeitgeber fest. */
|
||||||
const aktuelleRueckmeldung = new WeakMap();
|
const currentFeedback = new WeakMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blendet die Treffer-Rueckmeldung ein und nach kurzer Zeit wieder aus.
|
* Blendet die Treffer-Rueckmeldung ein und nach kurzer Zeit wieder aus.
|
||||||
* Gruen bei Barcode, gelb bei OCR. Keine Eingabe noetig.
|
* Gruen bei Barcode, gelb bei OCR. Keine Eingabe noetig.
|
||||||
*/
|
*/
|
||||||
export function showResult(root, { stackId, spec, confidence }, dauerMs = 1200) {
|
export function showResult(root, { stackId, spec, confidence }, durationMs = 1200) {
|
||||||
const vorherige = aktuelleRueckmeldung.get(root);
|
const previous = currentFeedback.get(root);
|
||||||
if (vorherige) {
|
if (previous) {
|
||||||
clearTimeout(vorherige.timer);
|
clearTimeout(previous.timer);
|
||||||
vorherige.overlay.remove();
|
previous.overlay.remove();
|
||||||
vorherige.resolve();
|
previous.resolve();
|
||||||
aktuelleRueckmeldung.delete(root);
|
currentFeedback.delete(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
const overlay = document.createElement('div');
|
||||||
@@ -31,16 +23,16 @@ export function showResult(root, { stackId, spec, confidence }, dauerMs = 1200)
|
|||||||
<div class="detail"></div>
|
<div class="detail"></div>
|
||||||
<div class="pn"></div>
|
<div class="pn"></div>
|
||||||
`;
|
`;
|
||||||
overlay.querySelector('.detail').textContent = beschreibung(spec);
|
overlay.querySelector('.detail').textContent = describeSpec(spec);
|
||||||
overlay.querySelector('.pn').textContent = spec.partNumber ?? '';
|
overlay.querySelector('.pn').textContent = spec.partNumber ?? '';
|
||||||
root.appendChild(overlay);
|
root.appendChild(overlay);
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
overlay.remove();
|
overlay.remove();
|
||||||
aktuelleRueckmeldung.delete(root);
|
currentFeedback.delete(root);
|
||||||
resolve();
|
resolve();
|
||||||
}, dauerMs);
|
}, durationMs);
|
||||||
aktuelleRueckmeldung.set(root, { overlay, timer, resolve });
|
currentFeedback.set(root, { overlay, timer, resolve });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-12
@@ -3,16 +3,7 @@
|
|||||||
// Sitzung. Kennt weder Kamera noch Erkennung - alles kommt ueber die
|
// Sitzung. Kennt weder Kamera noch Erkennung - alles kommt ueber die
|
||||||
// Rueckrufe, genau wie bei scan-view.js und ambiguous-dialog.js.
|
// Rueckrufe, genau wie bei scan-view.js und ambiguous-dialog.js.
|
||||||
|
|
||||||
/** Kurzbeschreibung eines Specs fuer Stapel-Kopfzeilen und Eintrags-Zeilen ohne Teilenummer. */
|
import { describeSpec } from './describe-spec.js';
|
||||||
function describeSpec(spec) {
|
|
||||||
const parts = [
|
|
||||||
spec.capacityGb ? `${spec.capacityGb}GB` : '?',
|
|
||||||
spec.rank ?? '?',
|
|
||||||
spec.speed ?? '?',
|
|
||||||
spec.formFactor ?? '?',
|
|
||||||
];
|
|
||||||
return parts.join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kurzbezeichnung der Erkennungsquelle eines Eintrags - beim Sortieren
|
* Kurzbezeichnung der Erkennungsquelle eines Eintrags - beim Sortieren
|
||||||
@@ -52,7 +43,7 @@ export function renderSessionList(root, session, { onMove, onRemove, onEndSessio
|
|||||||
header.className = 'detail';
|
header.className = 'detail';
|
||||||
header.style.textAlign = 'left';
|
header.style.textAlign = 'left';
|
||||||
header.style.marginTop = '12px';
|
header.style.marginTop = '12px';
|
||||||
header.textContent = `Stapel ${stack.id} — ${stack.count}x ${describeSpec(stack.spec)}`;
|
header.textContent = `Stapel ${stack.id} — ${stack.count}x ${describeSpec(stack.spec, { placeholders: true })}`;
|
||||||
body.appendChild(header);
|
body.appendChild(header);
|
||||||
|
|
||||||
for (const entry of session.entries.filter((candidate) => candidate.stackId === stack.id)) {
|
for (const entry of session.entries.filter((candidate) => candidate.stackId === stack.id)) {
|
||||||
@@ -65,7 +56,7 @@ export function renderSessionList(root, session, { onMove, onRemove, onEndSessio
|
|||||||
label.style.flex = '1';
|
label.style.flex = '1';
|
||||||
label.style.fontSize = '14px';
|
label.style.fontSize = '14px';
|
||||||
label.style.textAlign = 'left';
|
label.style.textAlign = 'left';
|
||||||
label.textContent = entry.spec.partNumber ?? describeSpec(entry.spec);
|
label.textContent = entry.spec.partNumber ?? describeSpec(entry.spec, { placeholders: true });
|
||||||
row.appendChild(label);
|
row.appendChild(label);
|
||||||
|
|
||||||
const sourceLabel = document.createElement('span');
|
const sourceLabel = document.createElement('span');
|
||||||
|
|||||||
Reference in New Issue
Block a user