diff --git a/app.css b/app.css index 8268f6f..774e159 100644 --- a/app.css +++ b/app.css @@ -405,3 +405,36 @@ body { width: 100px; flex-shrink: 0; } + +/* Audio-Items in Auftrags-Detail */ +.audio-list { display: flex; flex-direction: column; gap: 8px; } +.audio-item { + background: #2a2a30; + border-radius: 6px; + padding: 10px 12px; + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} +.audio-item .audio-name { + flex: 1; + font-size: 13px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.audio-item .audio-play { + background: #337ab7; + color: #fff; + border: none; + border-radius: 50%; + width: 36px; + height: 36px; + font-size: 14px; + cursor: pointer; + flex-shrink: 0; +} +.audio-item audio { width: 100%; flex-basis: 100%; } + +.btn[disabled] { opacity: 0.5; cursor: not-allowed; } diff --git a/app.js b/app.js index acb5267..a3f3d43 100644 --- a/app.js +++ b/app.js @@ -124,9 +124,13 @@ router.on('/orders/:id', async (args) => { const photos = await api.listOrderPhotos(args.id).catch(() => ({ photos: [] })); title(data.order.ref); - // Nur Bilder in der Foto-Grid zeigen; alles andere (PDFs etc.) unten auflisten + // Nach MIME-Type aufteilen const imagePhotos = photos.photos.filter(p => (p.mime || '').startsWith('image/')); - const otherDocs = photos.photos.filter(p => !(p.mime || '').startsWith('image/')); + const audioFiles = photos.photos.filter(p => (p.mime || '').startsWith('audio/') || /\.(webm|mp3|ogg|m4a|wav)$/i.test(p.filename)); + const otherDocs = photos.photos.filter(p => + !(p.mime || '').startsWith('image/') && + !(p.mime || '').startsWith('audio/') && + !/\.(webm|mp3|ogg|m4a|wav)$/i.test(p.filename)); main().innerHTML = `
@@ -155,6 +159,14 @@ router.on('/orders/:id', async (args) => { ${imagePhotos.map(p => `
`).join('')}
+ ${audioFiles.length ? ` +
+

🎙 Sprachnotizen (${audioFiles.length})

+
+ ${audioFiles.map(a => `
${escapeHtml(a.filename)}
`).join('')} +
+
` : ''} + ${otherDocs.length ? `

Weitere Dokumente (${otherDocs.length})

@@ -170,6 +182,44 @@ router.on('/orders/:id', async (args) => { // Sprachnotiz document.getElementById('btn-voice').onclick = () => openVoiceModal(args.id); + // Audio-Files abspielen + document.querySelectorAll('.audio-item .audio-play').forEach(btn => { + btn.addEventListener('click', async (e) => { + const item = e.target.closest('.audio-item'); + const rel = item.dataset.relpath; + const mime = item.dataset.mime || 'audio/webm'; + // Existierenden Player toggeln + let player = item.querySelector('audio'); + if (player) { + if (player.paused) player.play(); + else player.pause(); + return; + } + btn.textContent = '⏳'; + try { + const t = await api.getToken(); + const params = new URLSearchParams({ relpath: rel, jwt: t }); + const r = await fetch(window.location.origin + '/custom/bericht/api/photo.php?' + params.toString()); + if (!r.ok) throw new Error('Load failed'); + const blob = await r.blob(); + const url = URL.createObjectURL(new Blob([blob], { type: mime })); + player = document.createElement('audio'); + player.controls = true; + player.src = url; + player.style.width = '100%'; + player.style.marginTop = '8px'; + item.appendChild(player); + player.play(); + btn.textContent = '⏸'; + player.onplay = () => btn.textContent = '⏸'; + player.onpause = () => btn.textContent = '▶'; + } catch (err) { + showToast('Audio laden fehlgeschlagen: ' + err.message, 'error'); + btn.textContent = '▶'; + } + }); + }); + loadThumbs(); const camInput = document.getElementById('camera-input'); @@ -302,7 +352,8 @@ router.on('/reports/:id', async (args) => { title(data.report.ref); const statusLabel = data.report.status === 1 ? 'Final' : 'Entwurf'; - const canFinalize = data.report.status !== 1 && data.pages.length > 0; + const hasPages = data.pages.length > 0; + const finalizeLabel = data.report.status === 1 ? '🔄 PDF neu erzeugen' : '📑 Bericht finalisieren (PDF)'; main().innerHTML = `
@@ -314,19 +365,24 @@ router.on('/reports/:id', async (args) => {

Status: ${statusLabel}

+ ${hasPages ? `
${data.pages.map(p => `
`).join('')} -
+
` : '
📭
Dieser Bericht hat noch keine Seiten. Fotos aufnehmen, um Seiten hinzuzufügen.
'} - ${canFinalize ? `` : ''} + `; loadThumbs(); - if (canFinalize) { - document.getElementById('btn-finalize').onclick = async () => { - if (!confirm('Bericht jetzt finalisieren und PDF erzeugen?')) return; + const finalizeBtn = document.getElementById('btn-finalize'); + if (finalizeBtn) { + finalizeBtn.onclick = async () => { + if (!hasPages) { showToast('Bericht hat keine Seiten', 'warn'); return; } + if (!confirm(data.report.status === 1 + ? 'PDF neu erzeugen und unter den verknüpften Dokumenten ablegen?' + : 'Bericht jetzt finalisieren und PDF erzeugen?')) return; showToast('PDF wird erzeugt…'); try { const r = await api.finalizeReport(args.id); diff --git a/sw.js b/sw.js index 071e390..9c7e7f8 100644 --- a/sw.js +++ b/sw.js @@ -4,7 +4,7 @@ * - API-Calls: network-first, kein offline-cache (da auth-pflichtig) */ -const CACHE = 'baustelle-v2'; +const CACHE = 'baustelle-v3'; const SHELL = [ './', './index.html',