Files
aza/AzA march 2026/web/empfang.html

229 lines
9.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AZA Empfang</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',system-ui,sans-serif;background:#f0f4f8;color:#1a2a3a;min-height:100vh}
header{background:linear-gradient(135deg,#5B8DB3,#3a6d93);color:#fff;padding:18px 24px;display:flex;align-items:center;justify-content:space-between;box-shadow:0 2px 8px rgba(0,0,0,.12)}
header h1{font-size:1.35rem;font-weight:600;letter-spacing:.3px}
.header-right{display:flex;align-items:center;gap:14px}
.badge{background:rgba(255,255,255,.2);border-radius:20px;padding:4px 14px;font-size:.85rem}
.sound-toggle{background:none;border:1px solid rgba(255,255,255,.35);color:#fff;border-radius:6px;padding:4px 10px;font-size:.82rem;cursor:pointer;transition:background .15s}
.sound-toggle:hover{background:rgba(255,255,255,.15)}
.sound-toggle.muted{opacity:.5}
.container{max-width:900px;margin:24px auto;padding:0 16px}
.empty{text-align:center;padding:60px 20px;color:#6a8a9a;font-size:1.05rem}
.card{background:#fff;border-radius:10px;box-shadow:0 1px 6px rgba(0,0,0,.08);margin-bottom:16px;overflow:hidden;border-left:4px solid #5B8DB3;transition:border-color .2s}
.card.done{border-left-color:#8bc49a;opacity:.7}
.card-header{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;background:#fafcfe;border-bottom:1px solid #eef2f6}
.card-header .meta{font-size:.82rem;color:#6a8a9a}
.card-header .patient{font-weight:600;font-size:1rem;color:#1a3a5a}
.card-header .status{font-size:.75rem;padding:3px 10px;border-radius:12px;font-weight:600}
.status-offen{background:#fff3cd;color:#856404}
.status-erledigt{background:#d4edda;color:#155724}
.card-body{padding:16px 18px}
.field{margin-bottom:10px}
.field-label{font-size:.78rem;font-weight:600;color:#5B8DB3;text-transform:uppercase;letter-spacing:.5px;margin-bottom:3px}
.field-value{font-size:.95rem;white-space:pre-wrap;line-height:1.5}
.card-actions{display:flex;gap:8px;padding:10px 18px;background:#fafcfe;border-top:1px solid #eef2f6;flex-wrap:wrap}
.btn{border:none;border-radius:6px;padding:7px 16px;font-size:.82rem;cursor:pointer;font-weight:500;transition:background .15s}
.btn-copy{background:#e8f0f8;color:#2a5a8a}.btn-copy:hover{background:#d4e4f0}
.btn-print{background:#e8f0e8;color:#2a5a2a}.btn-print:hover{background:#d4e8d4}
.btn-done{background:#d4edda;color:#155724}.btn-done:hover{background:#c3e6cb}
.btn-delete{background:#f8e8e8;color:#8a2a2a}.btn-delete:hover{background:#f0d4d4}
.refresh-info{text-align:center;padding:8px;font-size:.78rem;color:#8a9aaa}
@media print{header,.card-actions,.refresh-info{display:none}.card{break-inside:avoid;border-left-width:2px;box-shadow:none}}
</style>
</head>
<body>
<header>
<h1>AZA Empfang</h1>
<div class="header-right">
<button class="sound-toggle" id="sound-btn" onclick="toggleSound()" title="Benachrichtigungston an/aus">🔔 Ton an</button>
<span class="badge" id="count-badge"></span>
</div>
</header>
<div class="container" id="messages-container">
<div class="empty">Nachrichten werden geladen…</div>
</div>
<div class="refresh-info">Aktualisiert automatisch alle 10 Sekunden</div>
<script>
const API_BASE = window.location.origin + '/empfang';
let lastData = '';
let lastOpenCount = -1;
let soundEnabled = localStorage.getItem('empfang_sound') !== 'off';
let audioCtx = null;
function updateSoundBtn() {
const btn = document.getElementById('sound-btn');
if (soundEnabled) {
btn.textContent = '🔔 Ton an';
btn.classList.remove('muted');
} else {
btn.textContent = '🔕 Ton aus';
btn.classList.add('muted');
}
}
updateSoundBtn();
function toggleSound() {
soundEnabled = !soundEnabled;
localStorage.setItem('empfang_sound', soundEnabled ? 'on' : 'off');
updateSoundBtn();
if (soundEnabled) playNotification();
}
function playNotification() {
if (!soundEnabled) return;
try {
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const now = audioCtx.currentTime;
const g = audioCtx.createGain();
g.connect(audioCtx.destination);
g.gain.setValueAtTime(0.15, now);
g.gain.exponentialRampToValueAtTime(0.001, now + 0.6);
const o1 = audioCtx.createOscillator();
o1.type = 'sine';
o1.frequency.setValueAtTime(880, now);
o1.frequency.setValueAtTime(1100, now + 0.15);
o1.connect(g);
o1.start(now);
o1.stop(now + 0.6);
} catch(e) {}
}
async function loadMessages() {
try {
const r = await fetch(API_BASE + '/messages');
const d = await r.json();
const raw = JSON.stringify(d.messages || []);
if (raw === lastData) return;
lastData = raw;
const msgs = d.messages || [];
const openCount = msgs.filter(m => m.status === 'offen').length;
if (lastOpenCount >= 0 && openCount > lastOpenCount) {
playNotification();
}
lastOpenCount = openCount;
render(msgs);
} catch (e) {
document.getElementById('messages-container').innerHTML =
'<div class="empty">Backend nicht erreichbar.<br>Bitte Verbindung prüfen.</div>';
}
}
function render(msgs) {
const c = document.getElementById('messages-container');
const open = msgs.filter(m => m.status === 'offen');
document.getElementById('count-badge').textContent = open.length + ' offen';
if (!msgs.length) {
c.innerHTML = '<div class="empty">Keine Nachrichten vorhanden.</div>';
return;
}
c.innerHTML = msgs.map(m => {
const isDone = m.status === 'erledigt';
const fields = [];
if (m.medikamente) fields.push({l:'Medikamente', v:m.medikamente});
if (m.therapieplan) fields.push({l:'Therapieplan', v:m.therapieplan});
if (m.procedere) fields.push({l:'Procedere', v:m.procedere});
if (m.kommentar) fields.push({l:'Kommentar / Chat', v:m.kommentar});
return `
<div class="card ${isDone?'done':''}">
<div class="card-header">
<div>
<div class="patient">${esc(m.patient || 'Ohne Patientenangabe')}</div>
<div class="meta">${esc(m.absender || '')} · ${esc(m.zeitstempel || m.empfangen || '')}</div>
</div>
<span class="status ${isDone?'status-erledigt':'status-offen'}">${isDone?'Erledigt':'Offen'}</span>
</div>
<div class="card-body">
${fields.map(f => `
<div class="field">
<div class="field-label">${esc(f.l)}</div>
<div class="field-value">${esc(f.v)}</div>
</div>
`).join('')}
${!fields.length?'<div class="field"><div class="field-value" style="color:#999">Keine Inhalte</div></div>':''}
</div>
<div class="card-actions">
<button class="btn btn-copy" onclick="copyCard('${m.id}')">Kopieren</button>
<button class="btn btn-print" onclick="printCard('${m.id}')">Drucken</button>
${!isDone?`<button class="btn btn-done" onclick="markDone('${m.id}')">Erledigt</button>`:''}
<button class="btn btn-delete" onclick="deleteMsg('${m.id}')">Löschen</button>
</div>
</div>`;
}).join('');
}
function esc(s) {
const d = document.createElement('div');
d.textContent = s || '';
return d.innerHTML;
}
function copyCard(id) {
const msgs = JSON.parse(lastData);
const m = msgs.find(x => x.id === id);
if (!m) return;
const parts = [];
if (m.patient) parts.push('Patient: ' + m.patient);
if (m.medikamente) parts.push('Medikamente:\n' + m.medikamente);
if (m.therapieplan) parts.push('Therapieplan:\n' + m.therapieplan);
if (m.procedere) parts.push('Procedere:\n' + m.procedere);
if (m.kommentar) parts.push('Kommentar:\n' + m.kommentar);
parts.push('Absender: ' + (m.absender||'') + ' · ' + (m.zeitstempel||''));
navigator.clipboard.writeText(parts.join('\n\n')).then(() => {
const btn = event.target;
btn.textContent = '✓ Kopiert';
setTimeout(() => btn.textContent = 'Kopieren', 1500);
});
}
function printCard(id) {
const msgs = JSON.parse(lastData);
const m = msgs.find(x => x.id === id);
if (!m) return;
const w = window.open('', '_blank', 'width=600,height=600');
w.document.write(`<html><head><title>Empfang</title>
<style>body{font-family:'Segoe UI',sans-serif;padding:30px;color:#1a2a3a}
h2{color:#5B8DB3;margin-bottom:16px}
.f{margin-bottom:12px}.fl{font-size:.8rem;font-weight:bold;color:#5B8DB3;text-transform:uppercase}.fv{margin-top:2px;white-space:pre-wrap}
.meta{font-size:.8rem;color:#888;margin-top:20px;border-top:1px solid #ddd;padding-top:10px}
</style></head><body>
<h2>${esc(m.patient||'Empfangsnachricht')}</h2>
${m.medikamente?`<div class="f"><div class="fl">Medikamente</div><div class="fv">${esc(m.medikamente)}</div></div>`:''}
${m.therapieplan?`<div class="f"><div class="fl">Therapieplan</div><div class="fv">${esc(m.therapieplan)}</div></div>`:''}
${m.procedere?`<div class="f"><div class="fl">Procedere</div><div class="fv">${esc(m.procedere)}</div></div>`:''}
${m.kommentar?`<div class="f"><div class="fl">Kommentar / Chat</div><div class="fv">${esc(m.kommentar)}</div></div>`:''}
<div class="meta">Absender: ${esc(m.absender||'')} · ${esc(m.zeitstempel||m.empfangen||'')}</div>
</body></html>`);
w.document.close();
w.print();
}
async function markDone(id) {
await fetch(API_BASE + '/messages/' + id + '/done', {method:'POST'});
lastData = '';
loadMessages();
}
async function deleteMsg(id) {
if (!confirm('Nachricht wirklich löschen?')) return;
await fetch(API_BASE + '/messages/' + id, {method:'DELETE'});
lastData = '';
loadMessages();
}
loadMessages();
setInterval(loadMessages, 10000);
</script>
</body>
</html>