Feat: Praxis-Auswahl bei Impfung/Entwurmung/Medikament + Dokumente funktional

- Praxis-Dropdown als wiederverwendbare Funktion _praxisSelectField()
- Impfung, Entwurmung, Medikament: Praxis auswählbar statt Freitext
- Impfpass-Karte: zeigt Praxisname mit 🏥 Icon
- Dokumente: Öffnen-Button direkt auf der Karte (stopPropagation),
  Bild-Vorschau 64px, "Noch keine Datei" wenn kein Upload
- SW-Cache → by-v15
This commit is contained in:
rene 2026-04-13 20:38:30 +02:00
parent 40fa47efca
commit 5c178f812b
2 changed files with 45 additions and 22 deletions

View file

@ -207,7 +207,9 @@ window.Page_health = (() => {
}); });
const items = entries.map(e => { const items = entries.map(e => {
const ampel = _impfAmpel(e.naechstes); const ampel = _impfAmpel(e.naechstes);
const praxis = _praxen.find(p => p.id === e.tierarzt_id);
const vetName = praxis?.name || e.tierarzt_name || '';
return ` return `
<div class="health-card" data-id="${e.id}" data-action="open-entry"> <div class="health-card" data-id="${e.id}" data-action="open-entry">
<div class="health-card-ampel ampel-${ampel.color}" title="${ampel.label}"></div> <div class="health-card-ampel ampel-${ampel.color}" title="${ampel.label}"></div>
@ -215,9 +217,9 @@ window.Page_health = (() => {
<div class="health-card-title">${_esc(e.bezeichnung)}</div> <div class="health-card-title">${_esc(e.bezeichnung)}</div>
<div class="health-card-meta"> <div class="health-card-meta">
${UI.time.format(e.datum + 'T00:00:00')} ${UI.time.format(e.datum + 'T00:00:00')}
${e.tierarzt_name ? ` · ${_esc(e.tierarzt_name)}` : ''}
${e.charge_nr ? ` · Ch.-Nr: ${_esc(e.charge_nr)}` : ''} ${e.charge_nr ? ` · Ch.-Nr: ${_esc(e.charge_nr)}` : ''}
</div> </div>
${vetName ? `<div style="font-size:var(--text-sm);color:var(--c-text-secondary);margin-top:var(--space-1)">🏥 ${_esc(vetName)}</div>` : ''}
${e.naechstes ? `<div class="health-card-next ampel-text-${ampel.color}"> ${e.naechstes ? `<div class="health-card-next ampel-text-${ampel.color}">
Nächste Impfung: ${UI.time.format(e.naechstes + 'T00:00:00')} ${ampel.icon} Nächste Impfung: ${UI.time.format(e.naechstes + 'T00:00:00')} ${ampel.icon}
</div>` : ''} </div>` : ''}
@ -481,22 +483,31 @@ window.Page_health = (() => {
icon: '📄', title: 'Noch keine Dokumente', text: 'Lade Impfpässe, Befunde und mehr hoch.', action: addBtn icon: '📄', title: 'Noch keine Dokumente', text: 'Lade Impfpässe, Befunde und mehr hoch.', action: addBtn
}); });
const items = entries.map(e => ` const items = entries.map(e => {
<div class="health-card" data-id="${e.id}" data-action="open-entry"> const isPdf = e.datei_typ === 'pdf';
<div class="health-card-body" style="display:flex;gap:var(--space-3);align-items:center"> const hasFile = !!e.datei_url;
${e.datei_url return `
? (e.datei_typ === 'pdf' <div class="health-card" data-id="${e.id}" data-action="open-entry">
? `<div class="health-doc-icon">📄</div>` ${hasFile && !isPdf
: `<img src="${e.datei_url}" class="health-doc-thumb" alt="Dokument">`) ? `<img src="${e.datei_url}" class="health-doc-thumb" alt="Vorschau"
: `<div class="health-doc-icon">📎</div>`} style="width:64px;height:64px;object-fit:cover;border-radius:var(--radius-md);flex-shrink:0">`
<div> : `<div style="width:48px;height:48px;display:flex;align-items:center;justify-content:center;
font-size:2rem;flex-shrink:0">${isPdf ? '📄' : '📎'}</div>`}
<div class="health-card-body">
<div class="health-card-title">${_esc(e.bezeichnung)}</div> <div class="health-card-title">${_esc(e.bezeichnung)}</div>
<div class="health-card-meta">${UI.time.format(e.datum + 'T00:00:00')}</div> <div class="health-card-meta">${UI.time.format(e.datum + 'T00:00:00')}</div>
${e.notiz ? `<div class="health-card-note">${_esc(e.notiz)}</div>` : ''} ${e.notiz ? `<div class="health-card-note">${_esc(e.notiz)}</div>` : ''}
${hasFile
? `<a href="${e.datei_url}" target="_blank" rel="noopener"
class="btn btn-secondary btn-sm" style="margin-top:var(--space-2);display:inline-flex"
onclick="event.stopPropagation()">
${isPdf ? '📄 PDF öffnen' : '🖼️ Bild öffnen'}
</a>`
: `<span style="font-size:var(--text-xs);color:var(--c-text-muted)">Noch keine Datei hochgeladen</span>`}
</div> </div>
</div> </div>
</div> `;
`).join(''); }).join('');
return `<div class="health-list">${items}</div> return `<div class="health-list">${items}</div>
<div style="text-align:center;padding:var(--space-4)">${addBtn}</div>`; <div style="text-align:center;padding:var(--space-4)">${addBtn}</div>`;
@ -728,6 +739,23 @@ window.Page_health = (() => {
return ph[typ] || ''; return ph[typ] || '';
} }
// Wiederverwendbares Praxis-Dropdown für alle Formulare
function _praxisSelectField(entry) {
const aktivePraxen = _praxen.filter(p => p.aktiv);
if (!aktivePraxen.length) return '';
return `
<div class="form-group">
<label class="form-label">Behandelnde Praxis</label>
<select class="form-control" name="tierarzt_id">
<option value=""> optional </option>
${aktivePraxen.map(p => `
<option value="${p.id}" ${entry?.tierarzt_id === p.id ? 'selected' : ''}>
${_esc(p.name)}${p.ort ? ` · ${_esc(p.ort)}` : ''}
</option>`).join('')}
</select>
</div>`;
}
function _extraFormFields(entry, typ) { function _extraFormFields(entry, typ) {
switch (typ) { switch (typ) {
case 'impfung': return ` case 'impfung': return `
@ -735,10 +763,7 @@ window.Page_health = (() => {
<label class="form-label">Nächste Impfung (optional)</label> <label class="form-label">Nächste Impfung (optional)</label>
<input class="form-control" type="date" name="naechstes" value="${entry?.naechstes || ''}"> <input class="form-control" type="date" name="naechstes" value="${entry?.naechstes || ''}">
</div> </div>
<div class="form-group"> ${_praxisSelectField(entry)}
<label class="form-label">Tierarzt</label>
<input class="form-control" type="text" name="tierarzt_name" value="${_esc(entry?.tierarzt_name || '')}">
</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Chargen-Nr.</label> <label class="form-label">Chargen-Nr.</label>
<input class="form-control" type="text" name="charge_nr" value="${_esc(entry?.charge_nr || '')}"> <input class="form-control" type="text" name="charge_nr" value="${_esc(entry?.charge_nr || '')}">
@ -749,10 +774,7 @@ window.Page_health = (() => {
<label class="form-label">Nächste Behandlung (optional)</label> <label class="form-label">Nächste Behandlung (optional)</label>
<input class="form-control" type="date" name="naechstes" value="${entry?.naechstes || ''}"> <input class="form-control" type="date" name="naechstes" value="${entry?.naechstes || ''}">
</div> </div>
<div class="form-group"> ${_praxisSelectField(entry)}
<label class="form-label">Tierarzt</label>
<input class="form-control" type="text" name="tierarzt_name" value="${_esc(entry?.tierarzt_name || '')}">
</div>
`; `;
case 'tierarzt': { case 'tierarzt': {
const aktivePraxen = _praxen.filter(p => p.aktiv); const aktivePraxen = _praxen.filter(p => p.aktiv);
@ -818,6 +840,7 @@ window.Page_health = (() => {
<label class="form-label">Gabe bis (optional)</label> <label class="form-label">Gabe bis (optional)</label>
<input class="form-control" type="date" name="bis_datum" value="${entry?.bis_datum || ''}"> <input class="form-control" type="date" name="bis_datum" value="${entry?.bis_datum || ''}">
</div> </div>
${_praxisSelectField(entry)}
<div class="form-group"> <div class="form-group">
<label class="form-label" style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer"> <label class="form-label" style="display:flex;align-items:center;gap:var(--space-2);cursor:pointer">
<input type="checkbox" name="aktiv" ${entry?.aktiv !== 0 ? 'checked' : ''}> <input type="checkbox" name="aktiv" ${entry?.aktiv !== 0 ? 'checked' : ''}>

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications Offline-Cache + Push Notifications
============================================================ */ ============================================================ */
const CACHE_VERSION = 'by-v14'; const CACHE_VERSION = 'by-v15';
const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_STATIC = `${CACHE_VERSION}-static`;
// Diese Dateien werden beim Install gecacht (App Shell) // Diese Dateien werden beim Install gecacht (App Shell)