// Rein darstellend: Vollbild-Liste aller Stapel der laufenden Sitzung, zum // Umsortieren und Entfernen einzelner Eintraege sowie zum Beenden der // Sitzung. Kennt weder Kamera noch Erkennung - alles kommt ueber die // Rueckrufe, genau wie bei scan-view.js und ambiguous-dialog.js. /** Kurzbeschreibung eines Specs fuer Stapel-Kopfzeilen und Eintrags-Zeilen ohne Teilenummer. */ function describeSpec(spec) { const parts = [ spec.capacityGb ? `${spec.capacityGb}GB` : '?', spec.rank ?? '?', spec.speed ?? '?', spec.formFactor ?? '?', ]; return parts.join(' '); } /** * Vollbild-Liste aller Stapel der laufenden Sitzung. * @param {HTMLElement} root * @param {{stacks: Array<{id: string, count: number, spec: object}>, entries: Array<{entryId: number, spec: object, stackId: string}>}} session * @param {{onMove: (entryId: number, stackId: string) => void, onRemove: (entryId: number) => void, onEndSession: () => void, onClose: () => void}} callbacks */ export function renderSessionList(root, session, { onMove, onRemove, onEndSession, onClose }) { const overlay = document.createElement('div'); overlay.className = 'overlay'; overlay.style.background = 'var(--bg)'; overlay.style.justifyContent = 'flex-start'; overlay.style.overflowY = 'auto'; overlay.innerHTML = `
Sitzung
`; const body = overlay.querySelector('#body'); for (const stack of session.stacks) { const header = document.createElement('div'); header.className = 'detail'; header.style.textAlign = 'left'; header.style.marginTop = '12px'; header.textContent = `Stapel ${stack.id} — ${stack.count}x ${describeSpec(stack.spec)}`; body.appendChild(header); for (const entry of session.entries.filter((candidate) => candidate.stackId === stack.id)) { const row = document.createElement('div'); row.style.display = 'flex'; row.style.gap = '8px'; row.style.alignItems = 'center'; const label = document.createElement('span'); label.style.flex = '1'; label.style.fontSize = '14px'; label.style.textAlign = 'left'; label.textContent = entry.spec.partNumber ?? describeSpec(entry.spec); row.appendChild(label); // Bedienflaechen muessen mindestens 56px hoch sein - auch die Auswahl, // nicht nur die Schaltflaechen. const select = document.createElement('select'); select.style.minHeight = '56px'; select.setAttribute('aria-label', `Stapel fuer ${label.textContent}`); for (const target of session.stacks) { const option = document.createElement('option'); option.value = target.id; option.textContent = target.id; option.selected = target.id === entry.stackId; select.appendChild(option); } select.addEventListener('change', () => onMove(entry.entryId, select.value)); row.appendChild(select); const removeButton = document.createElement('button'); removeButton.className = 'action secondary'; removeButton.style.minWidth = '56px'; removeButton.textContent = '✕'; removeButton.setAttribute('aria-label', `${label.textContent} entfernen`); removeButton.addEventListener('click', () => onRemove(entry.entryId)); row.appendChild(removeButton); body.appendChild(row); } } if (session.stacks.length === 0) { body.textContent = 'Noch nichts erfasst.'; } overlay.querySelector('#close').addEventListener('click', () => { overlay.remove(); onClose(); }); overlay.querySelector('#end').addEventListener('click', () => { overlay.remove(); onEndSession(); }); root.appendChild(overlay); }