Notizblock: Notiz-Button für 6 neue Bereiche + RUBRIKEN + Datenschutz — SW by-v425

Neue Notiz-Buttons:
- Tagebuch: in der Detail-Ansicht (nicht Edit-Form)
- Trainingspläne: im Plan-Header pro Plan
- Freunde: in jedem Freund-Karten-Bereich
- Giftköder: in jedem Meldungs-Karten (private Umstände)
- Verlorener Hund: in jedem Eintrag

Notizblock:
- 4 neue RUBRIKEN: trainingsplan, friends, poison, lost
- Datenschutz-Hinweis: "Alle Notizen sind privat"
- lock-simple Icon zum Sprite hinzugefügt
This commit is contained in:
rene 2026-04-26 10:52:28 +02:00
parent b801571bf0
commit 02120bb532
9 changed files with 430 additions and 10 deletions

View file

@ -537,6 +537,16 @@ window.Page_trainingsplaene = (() => {
// BIND EVENTS
// ----------------------------------------------------------
function _bindEvents() {
// Notiz-Button
const dogId = _dogId();
_container.querySelector('#tp-note-btn')?.addEventListener('click', e => {
e.stopPropagation();
const planLabel = _activePlan === 'welpe' ? 'Welpe 06 Monate'
: _activePlan === 'junior' ? 'Junior 618 Monate'
: `Erwachsener Hund ${_activeAdultTab}`;
_openNoteModal('trainingsplan', dogId, planLabel, null);
});
// Plan selector
_container.querySelectorAll('[data-plan]').forEach(btn => {
btn.addEventListener('click', () => {
@ -596,11 +606,21 @@ window.Page_trainingsplaene = (() => {
else if (_activePlan === 'junior') planContent = _renderJunior();
else planContent = _renderErwachsen();
const dogId = _dogId();
const planLabel = _activePlan === 'welpe' ? 'Welpe 06 Monate'
: _activePlan === 'junior' ? 'Junior 618 Monate'
: `Erwachsener Hund ${_activeAdultTab}`;
_container.innerHTML = `
<div style="padding-bottom:var(--space-8)">
<h2 style="font-size:var(--text-lg);font-weight:700;margin:var(--space-4) 0 var(--space-4)">
${_icon('clipboard-text')} Trainingspläne
</h2>
<div style="display:flex;align-items:center;justify-content:space-between;margin:var(--space-4) 0 var(--space-4)">
<h2 style="font-size:var(--text-lg);font-weight:700;margin:0">
${_icon('clipboard-text')} Trainingspläne
</h2>
${dogId ? `<button class="btn btn-ghost btn-xs" id="tp-note-btn" title="Notiz zum Plan">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz
</button>` : ''}
</div>
${_renderPlanSelector()}
${planContent}
<!-- Trainingskalender -->
@ -750,6 +770,88 @@ window.Page_trainingsplaene = (() => {
`;
}
// ----------------------------------------------------------
// NOTIZ-MODAL
// ----------------------------------------------------------
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
// Vorhandenes Modal entfernen falls noch offen
document.getElementById('by-note-modal')?.remove();
const overlay = document.createElement('div');
overlay.id = 'by-note-modal';
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
overlay.innerHTML = `
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
padding-bottom:env(safe-area-inset-bottom,0px)">
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
<div>
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${_esc(parentLabel)}</div>
</div>
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
</div>
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
<form id="by-note-form">
<textarea id="by-note-text" class="form-control" rows="5"
placeholder="Notiz eingeben…"
style="width:100%;resize:vertical"></textarea>
</form>
</div>
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
display:flex;gap:var(--space-2);flex-shrink:0">
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
</div>
</div>
`;
document.body.appendChild(overlay);
const textarea = document.getElementById('by-note-text');
const saveBtn = document.getElementById('by-note-save');
const cancelBtn = document.getElementById('by-note-cancel');
const closeBtn = document.getElementById('by-note-close');
let existingNoteId = null;
try {
const existing = await API.notes.get(parentType, String(parentId));
if (existing?.id) {
existingNoteId = existing.id;
textarea.value = existing.text || '';
}
} catch (_) { /* keine Notiz vorhanden — ok */ }
setTimeout(() => textarea.focus(), 100);
const _close = () => overlay.remove();
closeBtn.addEventListener('click', _close);
cancelBtn.addEventListener('click', _close);
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
document.getElementById('by-note-form').addEventListener('submit', async e => {
e.preventDefault();
const text = textarea.value.trim();
UI.setLoading(saveBtn, true);
try {
const payload = { text, parent_label: parentLabel, location_name: locationName };
if (existingNoteId) {
await API.notes.update(existingNoteId, payload);
} else {
await API.notes.create(parentType, String(parentId), payload);
}
UI.toast.success('Notiz gespeichert.');
_close();
} catch (err) {
UI.toast.error(err.message || 'Fehler beim Speichern.');
UI.setLoading(saveBtn, false);
}
});
}
// ----------------------------------------------------------
// PUBLIC API
// ----------------------------------------------------------