Sprint 12+13: Tagebuch Day-One-Redesign, Notiz-Feature, Icon-Fixes, SW by-v405
Tagebuch:
- Day-One-Listenansicht: Wochentag + Tageszahl + Meta-Zeile (Zeit/Ort/Wetter)
- 4 Ansichten: Liste, Medien-Mosaik, Kalender (mit Sprungbuttons), Karte (GPS-Marker)
- Detail-Ansicht inline im Content-Bereich (kein Fullscreen-Overlay mehr)
- Hero-Bild vollständig sichtbar (object-fit:contain), Lightbox mit Safe-Area
- 2-Spalten-Layout Desktop: Text + Leaflet-Karte + POI-Liste
- EXIF-GPS-Extraktion bei Foto-Upload, historisches Wetter via Archive-API
- NoteStation-Import: Fotos in diary_media (80 Einträge migriert, 94 Medien)
- Stats-Endpoints: /diary/stats, /diary/calendar, /diary/locations
Notiz-Feature:
- Generische notes-Tabelle (parent_type + parent_id + meta_json)
- 📝-Button in 8 Bereichen, Notizblock-Seite mit KI-Analyse
- KI-Toggle in Einstellungen, notes_ki_enabled in User-Profil
Icons & Design:
- fill:currentColor Fix für welcome/onboarding/friends.js
- --c-icon Variable, --c-text-muted Dark Mode aufgehellt
- 15+ neue Phosphor-Icons aus lokaler Kopie
- CSS Network-First im SW, Cache-Control-Middleware
Infrastruktur:
- Wiki-Anreicherungs-Scheduler-Jobs entfernt (abgeschlossen)
- auth.py: notes_ki_enabled + is_social_media im User-Response
This commit is contained in:
parent
95f91fdc00
commit
553e9e7854
35 changed files with 4558 additions and 370 deletions
|
|
@ -895,6 +895,7 @@ window.Page_uebungen = (() => {
|
|||
_bindAccordions();
|
||||
_bindStatusButtons();
|
||||
_bindLogButtons();
|
||||
_bindNotizButtons();
|
||||
if (_activeTab === 'ki-trainer') _loadKiTrainerFeedback();
|
||||
}
|
||||
|
||||
|
|
@ -965,6 +966,19 @@ window.Page_uebungen = (() => {
|
|||
Einheit
|
||||
</button>
|
||||
${_sessionStatsChip(_activeTab, u.name)}
|
||||
<button class="ueb-notiz-btn"
|
||||
data-tab="${_esc(_activeTab)}"
|
||||
data-name="${_esc(u.name)}"
|
||||
title="Notiz hinzufügen"
|
||||
style="background:none;border:1px solid var(--c-border);cursor:pointer;
|
||||
padding:3px 7px;border-radius:var(--radius-sm);
|
||||
display:flex;align-items:center;gap:3px;
|
||||
font-size:var(--text-xs);color:var(--c-text-secondary)">
|
||||
<svg class="ph-icon" style="width:13px;height:13px;flex-shrink:0" aria-hidden="true">
|
||||
<use href="/icons/phosphor.svg#note-pencil"></use>
|
||||
</svg>
|
||||
Notiz
|
||||
</button>
|
||||
<button class="ueb-status-btn"
|
||||
data-tab="${_esc(_activeTab)}"
|
||||
data-name="${_esc(u.name)}"
|
||||
|
|
@ -1006,7 +1020,7 @@ window.Page_uebungen = (() => {
|
|||
background:#78350f22;border:1px solid #d9770644;border-radius:var(--radius-sm);
|
||||
font-size:var(--text-xs);color:var(--c-text);line-height:1.4;
|
||||
display:flex;align-items:flex-start;gap:var(--space-2)">
|
||||
<svg class="ph-icon" style="width:13px;height:13px;flex-shrink:0;margin-top:1px;color:#f59e0b" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg>
|
||||
<svg class="ph-icon" style="width:13px;height:13px;flex-shrink:0;margin-top:1px;color:var(--c-warning)" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg>
|
||||
<span>${_esc(u.hinweis)}</span>
|
||||
</div>
|
||||
` : ''}
|
||||
|
|
@ -1039,7 +1053,7 @@ window.Page_uebungen = (() => {
|
|||
${u.fehler.length ? `
|
||||
<p style="font-size:var(--text-xs);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text-secondary);margin-bottom:var(--space-2);text-transform:uppercase;letter-spacing:0.05em">
|
||||
<svg class="ph-icon" style="width:12px;height:12px;color:#f59e0b" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg>
|
||||
<svg class="ph-icon" style="width:12px;height:12px;color:var(--c-warning)" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg>
|
||||
Häufige Fehler
|
||||
</p>
|
||||
<ul style="margin:0 0 var(--space-4);padding-left:var(--space-5);display:flex;flex-direction:column;gap:var(--space-2)">
|
||||
|
|
@ -1100,6 +1114,252 @@ window.Page_uebungen = (() => {
|
|||
});
|
||||
}
|
||||
|
||||
function _bindNotizButtons() {
|
||||
_container.querySelectorAll('.ueb-notiz-btn').forEach(btn => {
|
||||
btn.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
const exerciseId = `${btn.dataset.tab}_${btn.dataset.name.replace(/[\s/]+/g, '_')}`;
|
||||
_openNotizModal(exerciseId, btn.dataset.name, btn);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function _openNotizModal(exerciseId, exerciseName, triggerBtn) {
|
||||
const modalId = 'ueb-notiz-modal';
|
||||
document.getElementById(modalId)?.remove();
|
||||
|
||||
// Lade bestehende Notiz
|
||||
let existingNote = null;
|
||||
if (_appState?.user) {
|
||||
try {
|
||||
const notes = await API.notes.get('training_session', exerciseId.length > 0 ? exerciseId : 0);
|
||||
if (notes && notes.length > 0) existingNote = notes[0];
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.id = modalId;
|
||||
overlay.style.cssText = `
|
||||
position:fixed;inset:0;z-index:9999;
|
||||
display:flex;align-items:flex-end;justify-content:center;
|
||||
background:rgba(0,0,0,0.45);
|
||||
`;
|
||||
|
||||
const noteText = existingNote?.text || '';
|
||||
const meta = existingNote?.meta_json || {};
|
||||
const currentErfolgsquote = meta.erfolgsquote || null;
|
||||
const currentUmgebung = meta.umgebung || null;
|
||||
const currentStimmung = meta.hund_stimmung || null;
|
||||
|
||||
overlay.innerHTML = `
|
||||
<div style="background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
|
||||
width:100%;max-width:480px;max-height:85vh;overflow-y:auto;
|
||||
padding:var(--space-5) var(--space-4) var(--space-6);box-shadow:0 -4px 24px rgba(0,0,0,0.15)">
|
||||
<div style="width:40px;height:4px;background:var(--c-border);border-radius:2px;margin:0 auto var(--space-4)"></div>
|
||||
|
||||
<h3 style="font-size:var(--text-base);font-weight:var(--weight-semibold);color:var(--c-text);
|
||||
margin:0 0 var(--space-4);text-align:center">
|
||||
Notiz: ${_esc(exerciseName)}
|
||||
</h3>
|
||||
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-4)">
|
||||
|
||||
<!-- Freitext -->
|
||||
<div>
|
||||
<label style="display:block;font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);margin-bottom:var(--space-2)">Notiz</label>
|
||||
<textarea id="ueb-notiz-text" rows="3"
|
||||
placeholder="Was ist dir aufgefallen? Tipps für nächstes Mal…"
|
||||
style="width:100%;padding:var(--space-3);border:1.5px solid var(--c-border);
|
||||
border-radius:var(--radius-md);font-size:var(--text-sm);
|
||||
font-family:var(--font-sans);background:var(--c-surface);
|
||||
color:var(--c-text);resize:vertical;outline:none;
|
||||
line-height:1.5">${_esc(noteText)}</textarea>
|
||||
</div>
|
||||
|
||||
<!-- Erfolgsquote -->
|
||||
<div>
|
||||
<label style="display:block;font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);margin-bottom:var(--space-2)">Bewertung (optional)</label>
|
||||
<div style="display:flex;gap:var(--space-2)">
|
||||
${[1,2,3,4,5].map(n => `
|
||||
<button type="button" class="ueb-notiz-pfote" data-val="${n}"
|
||||
style="font-size:1.4rem;border:1.5px solid var(--c-border);
|
||||
border-radius:var(--radius-md);padding:4px 10px;cursor:pointer;
|
||||
background:${currentErfolgsquote === n ? 'var(--c-primary-subtle)' : 'var(--c-surface-2)'};
|
||||
border-color:${currentErfolgsquote === n ? 'var(--c-primary)' : 'var(--c-border)'};
|
||||
transition:all 0.15s">🐾</button>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Umgebung -->
|
||||
<div>
|
||||
<label style="display:block;font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);margin-bottom:var(--space-2)">Umgebung (optional)</label>
|
||||
<div style="display:flex;gap:var(--space-2)">
|
||||
${[['🏠','zuhause'],['🌿','natur'],['🌆','stadt']].map(([emoji, val]) => `
|
||||
<button type="button" class="ueb-notiz-umgebung" data-val="${val}"
|
||||
style="font-size:1.2rem;border:1.5px solid var(--c-border);
|
||||
border-radius:var(--radius-md);padding:4px 12px;cursor:pointer;
|
||||
background:${currentUmgebung === val ? 'var(--c-primary-subtle)' : 'var(--c-surface-2)'};
|
||||
border-color:${currentUmgebung === val ? 'var(--c-primary)' : 'var(--c-border)'};
|
||||
transition:all 0.15s">${emoji}</button>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hund-Stimmung -->
|
||||
<div>
|
||||
<label style="display:block;font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);margin-bottom:var(--space-2)">Stimmung des Hundes (optional)</label>
|
||||
<div style="display:flex;gap:var(--space-2)">
|
||||
${[['😊','super'],['😐','ok'],['😔','mude']].map(([emoji, val]) => `
|
||||
<button type="button" class="ueb-notiz-stimmung" data-val="${val}"
|
||||
style="font-size:1.2rem;border:1.5px solid var(--c-border);
|
||||
border-radius:var(--radius-md);padding:4px 12px;cursor:pointer;
|
||||
background:${currentStimmung === val ? 'var(--c-primary-subtle)' : 'var(--c-surface-2)'};
|
||||
border-color:${currentStimmung === val ? 'var(--c-primary)' : 'var(--c-border)'};
|
||||
transition:all 0.15s">${emoji}</button>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div style="display:flex;gap:var(--space-3);margin-top:var(--space-5)">
|
||||
${existingNote ? `
|
||||
<button id="ueb-notiz-delete" type="button"
|
||||
style="padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);
|
||||
border:1.5px solid var(--c-danger);background:none;
|
||||
color:var(--c-danger);font-size:var(--text-sm);cursor:pointer">
|
||||
Löschen
|
||||
</button>
|
||||
` : ''}
|
||||
<button id="ueb-notiz-cancel" type="button"
|
||||
style="flex:1;padding:var(--space-3);border-radius:var(--radius-md);
|
||||
border:1.5px solid var(--c-border);background:none;
|
||||
color:var(--c-text-secondary);font-size:var(--text-sm);cursor:pointer">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button id="ueb-notiz-save" type="button"
|
||||
style="flex:2;padding:var(--space-3);border-radius:var(--radius-md);
|
||||
border:none;background:var(--c-primary);
|
||||
color:#fff;font-size:var(--text-sm);font-weight:var(--weight-semibold);cursor:pointer">
|
||||
${existingNote ? 'Aktualisieren' : 'Speichern'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// State
|
||||
let selectedErfolgsquote = currentErfolgsquote;
|
||||
let selectedUmgebung = currentUmgebung;
|
||||
let selectedStimmung = currentStimmung;
|
||||
|
||||
// Pfoten-Buttons
|
||||
overlay.querySelectorAll('.ueb-notiz-pfote').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const val = parseInt(btn.dataset.val, 10);
|
||||
selectedErfolgsquote = selectedErfolgsquote === val ? null : val;
|
||||
overlay.querySelectorAll('.ueb-notiz-pfote').forEach(b => {
|
||||
const active = parseInt(b.dataset.val, 10) === selectedErfolgsquote;
|
||||
b.style.background = active ? 'var(--c-primary-subtle)' : 'var(--c-surface-2)';
|
||||
b.style.borderColor = active ? 'var(--c-primary)' : 'var(--c-border)';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Umgebung-Buttons
|
||||
overlay.querySelectorAll('.ueb-notiz-umgebung').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
selectedUmgebung = selectedUmgebung === btn.dataset.val ? null : btn.dataset.val;
|
||||
overlay.querySelectorAll('.ueb-notiz-umgebung').forEach(b => {
|
||||
const active = b.dataset.val === selectedUmgebung;
|
||||
b.style.background = active ? 'var(--c-primary-subtle)' : 'var(--c-surface-2)';
|
||||
b.style.borderColor = active ? 'var(--c-primary)' : 'var(--c-border)';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Stimmung-Buttons
|
||||
overlay.querySelectorAll('.ueb-notiz-stimmung').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
selectedStimmung = selectedStimmung === btn.dataset.val ? null : btn.dataset.val;
|
||||
overlay.querySelectorAll('.ueb-notiz-stimmung').forEach(b => {
|
||||
const active = b.dataset.val === selectedStimmung;
|
||||
b.style.background = active ? 'var(--c-primary-subtle)' : 'var(--c-surface-2)';
|
||||
b.style.borderColor = active ? 'var(--c-primary)' : 'var(--c-border)';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function _closeNotizModal() {
|
||||
overlay.remove();
|
||||
}
|
||||
|
||||
overlay.addEventListener('click', e => { if (e.target === overlay) _closeNotizModal(); });
|
||||
overlay.querySelector('#ueb-notiz-cancel').addEventListener('click', _closeNotizModal);
|
||||
|
||||
// Speichern
|
||||
overlay.querySelector('#ueb-notiz-save').addEventListener('click', async () => {
|
||||
const text = overlay.querySelector('#ueb-notiz-text').value.trim();
|
||||
if (!text) { UI.toast.warning('Bitte gib eine Notiz ein.'); return; }
|
||||
|
||||
const saveBtn = overlay.querySelector('#ueb-notiz-save');
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.textContent = 'Speichern…';
|
||||
|
||||
const meta = {};
|
||||
if (selectedErfolgsquote) meta.erfolgsquote = selectedErfolgsquote;
|
||||
if (selectedUmgebung) meta.umgebung = selectedUmgebung;
|
||||
if (selectedStimmung) meta.hund_stimmung = selectedStimmung;
|
||||
|
||||
const payload = {
|
||||
text,
|
||||
meta_json: Object.keys(meta).length > 0 ? meta : null,
|
||||
};
|
||||
|
||||
try {
|
||||
if (existingNote) {
|
||||
await API.notes.update(existingNote.id, payload);
|
||||
} else {
|
||||
await API.notes.create('training_session', exerciseId, payload);
|
||||
}
|
||||
_closeNotizModal();
|
||||
UI.toast.success('Notiz gespeichert.');
|
||||
// Notiz-Button leicht hervorheben
|
||||
if (triggerBtn) {
|
||||
triggerBtn.style.borderColor = 'var(--c-primary)';
|
||||
triggerBtn.style.color = 'var(--c-primary)';
|
||||
}
|
||||
} catch (err) {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.textContent = existingNote ? 'Aktualisieren' : 'Speichern';
|
||||
UI.toast.error('Speichern fehlgeschlagen.');
|
||||
}
|
||||
});
|
||||
|
||||
// Löschen
|
||||
overlay.querySelector('#ueb-notiz-delete')?.addEventListener('click', async () => {
|
||||
if (!existingNote) return;
|
||||
try {
|
||||
await API.notes.delete(existingNote.id);
|
||||
_closeNotizModal();
|
||||
UI.toast.success('Notiz gelöscht.');
|
||||
if (triggerBtn) {
|
||||
triggerBtn.style.borderColor = '';
|
||||
triggerBtn.style.color = '';
|
||||
}
|
||||
} catch (_) {
|
||||
UI.toast.error('Löschen fehlgeschlagen.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _openLogModal(tab, exerciseName, initialReps) {
|
||||
// Build the modal HTML
|
||||
const modalId = 'ueb-log-modal';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue