baustelle-pwa/share.html
Eduard Wisch 02ffdca57c
All checks were successful
Deploy baustelle-pwa / deploy (push) Successful in 1s
feat: Phase 4 Block 1 — Seite bearbeiten, PDF-Vorschau, Unterschrift, Share Target
Bericht-Detail-Ansicht:
- Tap auf Seiten-Thumb → openPageActionsModal mit:
  - Vorschaubild
  - Textarea für Seiten-Notiz (wird im PDF unter der Seite gedruckt)
  - '💾 Notiz speichern' → api.updatePageNote
  - 🗑️ im Header → api.deletePage mit Confirm
- Seiten-Thumbs haben Nummer-Badge oben links (1, 2, 3…)
- Neuer '👁 PDF-Vorschau'-Button öffnet openPdfModal (iframe mit
  Blob-URL) für Final-PDF oder on-the-fly Preview
- Neuer '✍️ Kunden-Unterschrift hinzufügen'-Button öffnet
  openSignatureModal: Touch-Canvas 2:1, Clear, Save → PNG wird als
  neue Bericht-Seite mit note='Unterschrift Kunde' angelegt

Web Share Target API:
- manifest.webmanifest: share_target mit photos array
- share.html: empfängt geteilte Fotos aus IDB (vom SW befüllt),
  zeigt Auftragsliste, Tap → Upload aller Fotos
- Service Worker v5: fängt POST /share.html ab, schreibt Files in
  IDB Key 'shared_files', redirected 303

Cache-Version bumpt damit neue Files geladen werden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
2026-04-09 01:00:24 +02:00

82 lines
3.1 KiB
HTML

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Foto teilen — Baustelle</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<div id="app">
<header id="topbar">
<button id="back-btn" class="icon-btn" onclick="location.href='./'"></button>
<h1 id="page-title">📤 Foto teilen</h1>
<span></span>
</header>
<main id="main">
<div id="share-content">
<div class="loader">Foto wird empfangen…</div>
</div>
</main>
</div>
<script src="lib/idb.js"></script>
<script src="lib/api.js"></script>
<script>
/* Web Share Target Handler.
* Browser POST'et das geteilte Foto an diese URL.
* Wir zeigen eine Auftragsliste, User wählt einen Auftrag → Upload.
*/
(async function () {
const ct = document.getElementById('share-content');
const t = await api.getToken();
if (!t) {
ct.innerHTML = '<div class="empty-state"><div class="icon">🔐</div>Bitte zuerst anmelden<br><button class="btn" onclick="location.href=\'./\'">Zur App</button></div>';
return;
}
// Service Worker hat die geteilten Files unter 'shared_files' in IDB abgelegt
const sharedFiles = await idb.get('shared_files');
if (!sharedFiles || !sharedFiles.length) {
ct.innerHTML = '<div class="empty-state"><div class="icon">📭</div>Keine geteilten Fotos gefunden<br><button class="btn" onclick="location.href=\'./\'">Zur App</button></div>';
return;
}
try {
const data = await api.listOrders({ open: 1 });
const orders = data.orders || [];
ct.innerHTML = `
<p class="label" style="padding:0 16px;">${sharedFiles.length} Foto(s) geteilt. Wähle den Auftrag für den Upload:</p>
${orders.map(o => `
<div class="order-card" data-id="${o.id}">
<div class="ref">${escapeHtml(o.ref)}</div>
<div class="name">${escapeHtml(o.customer.name || '')}</div>
<div class="meta"><span>${escapeHtml((o.customer.zip || '') + ' ' + (o.customer.town || ''))}</span></div>
</div>
`).join('')}
`;
document.querySelectorAll('.order-card').forEach(c => {
c.addEventListener('click', async () => {
const orderId = c.dataset.id;
ct.innerHTML = '<div class="loader">Lade hoch…</div>';
for (const f of sharedFiles) {
try {
// f.data ist ein File/Blob
await api.uploadOrderPhoto(orderId, f.data, f.name);
} catch (err) { console.warn('Upload failed', err); }
}
await idb.del('shared_files');
location.href = './#/orders/' + orderId;
});
});
} catch (e) {
ct.innerHTML = '<div class="empty-state"><div class="icon">⚠</div>' + e.message + '</div>';
}
})();
function escapeHtml(s) {
return String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
</script>
</body>
</html>