Pflege-System: Pflegetipps im Hundeprofil + Rassen-Autocomplete
- GET /api/dogs/{id}/pflege: rassenspezifische Pflegetipps
- pflege_tipps DB-Tabelle (43 Tipps, 10 Kategorien) geseedet
- dogs.rasse_id für Wiki-Verknüpfung (Migration)
- Hundeprofil: Tipp des Tages + alle Tipps aufklappbar
- Hundeprofil-Edit: Rassen-Autocomplete mit Wiki-Match-Badge
- Social: Post-Bestätigung (Gepostet!-Button, Quick-Mark, Pending-Banner)
- Social: Pflegetipp-Button (allg. + rassenspezifisch)
- Social: Diversitäts-Check, Kategorie-Tagging
- Social: 104 Übungen, Übungsübersicht-Modal
- Admin: Social-Media-Tracking-Sektion
- SW by-v356, APP_VER 343
This commit is contained in:
parent
75615140c4
commit
ba5547f993
7 changed files with 797 additions and 9 deletions
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '337'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '343'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
|
||||
const App = (() => {
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ window.Page_dog_profile = (() => {
|
|||
` : ''}
|
||||
|
||||
<div id="dp-skills" style="margin-bottom:var(--space-5);text-align:left"></div>
|
||||
<div id="dp-pflege" style="margin-bottom:var(--space-5);text-align:left"></div>
|
||||
|
||||
${dog.is_public ? `
|
||||
<div style="background:var(--c-primary-subtle);border:1px solid var(--c-primary-light);
|
||||
|
|
@ -220,6 +221,9 @@ window.Page_dog_profile = (() => {
|
|||
// Skills laden
|
||||
_loadSkills(dog);
|
||||
|
||||
// Pflegetipps laden
|
||||
_loadPflegeTipps(dog);
|
||||
|
||||
// Sitter-Zugang laden (nur für Besitzer)
|
||||
if (dog.user_id === _appState.user?.id) {
|
||||
_loadSittingAccess(dog.id);
|
||||
|
|
@ -324,6 +328,123 @@ window.Page_dog_profile = (() => {
|
|||
</div>`;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// PFLEGETIPPS
|
||||
// ----------------------------------------------------------
|
||||
async function _loadPflegeTipps(dog) {
|
||||
const el = document.getElementById('dp-pflege');
|
||||
if (!el) return;
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = await API.get(`/dogs/${dog.id}/pflege`);
|
||||
} catch { return; }
|
||||
|
||||
if (!data?.tipps?.length) return;
|
||||
|
||||
const t = data.tipp_des_tages;
|
||||
const kat_icons = {
|
||||
'Fell':'✂️','Krallen':'💅','Zähne':'🦷','Ohren':'👂',
|
||||
'Augen':'👁','Pfoten':'🐾','Parasiten':'🦟',
|
||||
'Saisonal':'🌸','Gesundheitsvorsorge':'❤️','Welpen-Pflege':'🐶',
|
||||
};
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="card" style="padding:var(--space-4)">
|
||||
<div style="display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-3)">
|
||||
<span style="font-size:1.1em">🛁</span>
|
||||
<span style="font-size:var(--text-sm);font-weight:600">
|
||||
Pflegetipps${data.rasse_name ? ` für ${_esc(data.rasse_name)}` : ''}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
${t ? `
|
||||
<!-- Tipp des Tages -->
|
||||
<div style="background:var(--c-surface-2);border-radius:10px;padding:12px;
|
||||
margin-bottom:var(--space-3);border-left:3px solid #a78bfa">
|
||||
<div style="font-size:10px;font-weight:700;color:#a78bfa;text-transform:uppercase;
|
||||
letter-spacing:.5px;margin-bottom:4px">
|
||||
${t.saisonal_aktuell ? '🌸 Aktuell & Saisonal' : '💡 Tipp des Tages'}
|
||||
</div>
|
||||
<div style="font-weight:600;font-size:var(--text-sm);margin-bottom:4px">
|
||||
${kat_icons[t.kategorie]||'🐾'} ${_esc(t.titel)}
|
||||
</div>
|
||||
<div style="font-size:12px;color:var(--c-text-secondary);margin-bottom:8px;
|
||||
line-height:1.5">${_esc(t.beschreibung||'')}</div>
|
||||
${t.haeufigkeit ? `<div style="font-size:11px;color:var(--c-text-muted)">
|
||||
🔄 ${_esc(t.haeufigkeit)}</div>` : ''}
|
||||
${t.materialien ? `<div style="font-size:11px;color:var(--c-text-muted)">
|
||||
🛒 ${_esc(t.materialien)}</div>` : ''}
|
||||
${t.schritte?.length ? `
|
||||
<details style="margin-top:8px">
|
||||
<summary style="font-size:12px;cursor:pointer;color:var(--c-primary);
|
||||
font-weight:600">Anleitung anzeigen</summary>
|
||||
<ol style="margin:8px 0 0 16px;padding:0;font-size:12px;
|
||||
color:var(--c-text);line-height:1.6">
|
||||
${t.schritte.map(s=>`<li style="margin-bottom:3px">${_esc(s)}</li>`).join('')}
|
||||
</ol>
|
||||
${t.tipp ? `<div style="margin-top:8px;font-size:11px;color:#a78bfa;
|
||||
font-style:italic">💜 ${_esc(t.tipp)}</div>` : ''}
|
||||
</details>` : ''}
|
||||
</div>` : ''}
|
||||
|
||||
<!-- Alle Tipps Button -->
|
||||
<button id="dp-pflege-alle" class="btn btn-secondary btn-sm"
|
||||
style="width:100%;font-size:12px">
|
||||
Alle ${data.tipps.length} Pflegetipps anzeigen
|
||||
</button>
|
||||
<div id="dp-pflege-liste" style="display:none;margin-top:var(--space-3)">
|
||||
${data.kategorien.map(kat => {
|
||||
const katTipps = data.tipps.filter(t=>t.kategorie===kat);
|
||||
return `
|
||||
<div style="margin-bottom:var(--space-3)">
|
||||
<div style="font-size:11px;font-weight:700;color:var(--c-text-muted);
|
||||
text-transform:uppercase;margin-bottom:8px">
|
||||
${kat_icons[kat]||'🐾'} ${_esc(kat)}</div>
|
||||
${katTipps.map(tip => `
|
||||
<details style="background:var(--c-surface-2);border-radius:8px;
|
||||
padding:10px;margin-bottom:6px">
|
||||
<summary style="font-size:var(--text-sm);font-weight:600;cursor:pointer;
|
||||
list-style:none;display:flex;justify-content:space-between;
|
||||
align-items:center">
|
||||
${_esc(tip.titel)}
|
||||
${tip.saisonal_aktuell ? '<span style="font-size:10px;color:#10b981">● Aktuell</span>' : ''}
|
||||
</summary>
|
||||
<div style="margin-top:8px;font-size:12px;color:var(--c-text-secondary);
|
||||
line-height:1.5">${_esc(tip.beschreibung||'')}</div>
|
||||
${tip.haeufigkeit ? `<div style="font-size:11px;color:var(--c-text-muted);
|
||||
margin-top:4px">🔄 ${_esc(tip.haeufigkeit)}</div>` : ''}
|
||||
${tip.schritte?.length ? `
|
||||
<ol style="margin:8px 0 0 16px;padding:0;font-size:12px;line-height:1.6">
|
||||
${tip.schritte.map(s=>`<li style="margin-bottom:3px">${_esc(s)}</li>`).join('')}
|
||||
</ol>` : ''}
|
||||
${tip.tipp ? `<div style="margin-top:6px;font-size:11px;color:#a78bfa;
|
||||
font-style:italic">💜 ${_esc(tip.tipp)}</div>` : ''}
|
||||
</details>`).join('')}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
el.querySelector('#dp-pflege-alle')?.addEventListener('click', e => {
|
||||
const liste = el.querySelector('#dp-pflege-liste');
|
||||
const btn = e.currentTarget;
|
||||
if (liste.style.display === 'none') {
|
||||
liste.style.display = '';
|
||||
btn.textContent = 'Pflegetipps einklappen ▲';
|
||||
} else {
|
||||
liste.style.display = 'none';
|
||||
btn.textContent = `Alle ${data.tipps.length} Pflegetipps anzeigen`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _esc(s) {
|
||||
if (!s) return '';
|
||||
return String(s).replace(/&/g,'&').replace(/</g,'<')
|
||||
.replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// SITTER-ZUGANG
|
||||
// ----------------------------------------------------------
|
||||
|
|
@ -788,11 +909,21 @@ window.Page_dog_profile = (() => {
|
|||
<label class="form-label">
|
||||
Rasse
|
||||
<span style="color:var(--c-text-secondary)">(optional)</span>
|
||||
${UI.help('Die Rasse wird für Rasseninformationen und Statistiken verwendet.')}
|
||||
${UI.help('Verknüpfe deine Rasse mit unserem Wiki für personalisierte Pflegetipps.')}
|
||||
</label>
|
||||
<input class="form-control" type="text" name="rasse"
|
||||
id="dp-rasse-input"
|
||||
value="${_esc(dog?.rasse || '')}"
|
||||
list="dp-rasse-list"
|
||||
autocomplete="off"
|
||||
placeholder="z. B. Mischling, Golden Retriever…">
|
||||
<datalist id="dp-rasse-list"></datalist>
|
||||
<input type="hidden" name="rasse_id" id="dp-rasse-id"
|
||||
value="${dog?.rasse_id || ''}">
|
||||
<div id="dp-rasse-match" style="display:none;margin-top:4px;font-size:11px;
|
||||
color:var(--c-success);font-weight:600">
|
||||
✓ Mit Wiki verknüpft — Pflegetipps werden auf diese Rasse angepasst
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
|
||||
|
|
@ -882,6 +1013,45 @@ window.Page_dog_profile = (() => {
|
|||
const form = document.getElementById('dp-form');
|
||||
if (!form) return;
|
||||
|
||||
// Rassen-Autocomplete aus Wiki laden
|
||||
let _wikiBreeds = [];
|
||||
API.get('/wiki/rassen?limit=1000&offset=0').then(data => {
|
||||
_wikiBreeds = data.rassen || [];
|
||||
const list = document.getElementById('dp-rasse-list');
|
||||
if (list) {
|
||||
list.innerHTML = _wikiBreeds.map(r =>
|
||||
`<option value="${r.name.replace(/"/g,'"')}" data-id="${r.id}">`
|
||||
).join('');
|
||||
}
|
||||
// Vorhandene Rasse: Match prüfen und Badge zeigen
|
||||
const rasseInput = document.getElementById('dp-rasse-input');
|
||||
const rasseIdInput = document.getElementById('dp-rasse-id');
|
||||
const matchBadge = document.getElementById('dp-rasse-match');
|
||||
if (rasseInput?.value) {
|
||||
const match = _wikiBreeds.find(r =>
|
||||
r.name.toLowerCase() === rasseInput.value.toLowerCase());
|
||||
if (match && matchBadge) {
|
||||
if (!rasseIdInput.value) rasseIdInput.value = match.id;
|
||||
matchBadge.style.display = '';
|
||||
}
|
||||
}
|
||||
}).catch(() => {});
|
||||
|
||||
// Rassen-Input: bei Änderung ID nachschlagen
|
||||
document.getElementById('dp-rasse-input')?.addEventListener('input', e => {
|
||||
const val = e.target.value.trim().toLowerCase();
|
||||
const rasseIdInput = document.getElementById('dp-rasse-id');
|
||||
const matchBadge = document.getElementById('dp-rasse-match');
|
||||
const match = _wikiBreeds.find(r => r.name.toLowerCase() === val);
|
||||
if (match) {
|
||||
rasseIdInput.value = match.id;
|
||||
if (matchBadge) matchBadge.style.display = '';
|
||||
} else {
|
||||
rasseIdInput.value = '';
|
||||
if (matchBadge) matchBadge.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Foto-Vorschau
|
||||
const fotoInput = document.getElementById('dp-form-foto');
|
||||
const fotoPreview = document.getElementById('dp-form-preview');
|
||||
|
|
@ -935,6 +1105,7 @@ window.Page_dog_profile = (() => {
|
|||
const payload = {
|
||||
name: fd.name.trim(),
|
||||
rasse: fd.rasse || null,
|
||||
rasse_id: fd.rasse_id ? parseInt(fd.rasse_id) : null,
|
||||
geburtstag: fd.geburtstag || null,
|
||||
geschlecht: fd.geschlecht || null,
|
||||
gewicht_kg: fd.gewicht_kg ? parseFloat(fd.gewicht_kg) : null,
|
||||
|
|
|
|||
|
|
@ -251,6 +251,12 @@ window.Page_social = (() => {
|
|||
🎾 Trainingstipp generieren
|
||||
<span style="font-size:10px;opacity:.7;margin-left:6px">104 Übungen</span>
|
||||
</button>
|
||||
<button id="sm-pflege-tip" class="btn btn-secondary"
|
||||
style="width:100%;min-height:44px;font-size:var(--text-sm);
|
||||
margin-bottom:4px;border:1.5px solid #a78bfa;color:#a78bfa">
|
||||
🛁 Pflegetipp generieren
|
||||
<span style="font-size:10px;opacity:.7;margin-left:6px">allg. oder für gewählte Rasse</span>
|
||||
</button>
|
||||
<button id="sm-show-exercises" class="btn btn-secondary"
|
||||
style="width:100%;min-height:36px;font-size:11px;
|
||||
margin-bottom:8px;color:var(--c-text-muted)">
|
||||
|
|
@ -514,6 +520,43 @@ window.Page_social = (() => {
|
|||
}
|
||||
});
|
||||
|
||||
// Pflegetipp
|
||||
el.querySelector('#sm-pflege-tip').addEventListener('click', async () => {
|
||||
const btn = el.querySelector('#sm-pflege-tip');
|
||||
const res = el.querySelector('#sm-gen-result');
|
||||
btn.disabled = true;
|
||||
res.innerHTML = _lunaProgressHtml();
|
||||
const interval = _startProgress(res);
|
||||
const breedId = parseInt(el.querySelector('#sm-breed-id')?.value) || null;
|
||||
try {
|
||||
const url = breedId ? `/social/pflege-tipp?breed_id=${breedId}` : '/social/pflege-tipp';
|
||||
const data = await API.post(url, {});
|
||||
clearInterval(interval.bar); clearInterval(interval.msg);
|
||||
_progressDone(res);
|
||||
await new Promise(r => setTimeout(r, 400));
|
||||
res.innerHTML = `
|
||||
<div style="background:var(--c-surface-2);border-radius:10px;padding:10px;
|
||||
margin-bottom:10px;display:flex;gap:10px;align-items:center">
|
||||
<span style="font-size:2em;flex-shrink:0">🛁</span>
|
||||
<div>
|
||||
<div style="font-size:11px;color:var(--c-text-muted)">
|
||||
Pflegetipp · ${_esc(data.pflege_kat||'')}
|
||||
${data.rasse_name ? ` · speziell für ${_esc(data.rasse_name)}` : ''}</div>
|
||||
<div style="font-weight:700;font-size:var(--text-base)">
|
||||
${_esc(data.pflege_titel||'')}</div>
|
||||
</div>
|
||||
</div>
|
||||
${_renderResult(data, null)}`;
|
||||
_bindResultEvents(res);
|
||||
Promise.all([API.get('/social/stats'), API.get('/social/diversity')])
|
||||
.then(([s,d]) => { _stats = s; _diversity = d; });
|
||||
} catch(e) {
|
||||
clearInterval(interval.bar); clearInterval(interval.msg);
|
||||
res.innerHTML = `<div style="color:var(--c-danger);padding:var(--space-3)">
|
||||
😬 ${_esc(e.message||String(e))}</div>`;
|
||||
} finally { btn.disabled = false; }
|
||||
});
|
||||
|
||||
// Rasse des Tages
|
||||
el.querySelector('#sm-breed-day').addEventListener('click', async () => {
|
||||
const btn = el.querySelector('#sm-breed-day');
|
||||
|
|
@ -628,7 +671,7 @@ window.Page_social = (() => {
|
|||
|
||||
function _startProgress(container) {
|
||||
let elapsed = 0, msgIdx = 0;
|
||||
const totalMs = 14000;
|
||||
const totalMs = 2800;
|
||||
|
||||
// Fortschrittsbalken: alle 250ms
|
||||
const barInterval = setInterval(() => {
|
||||
|
|
@ -694,14 +737,48 @@ window.Page_social = (() => {
|
|||
</div>
|
||||
</div>` : ''}
|
||||
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;flex-wrap:wrap">
|
||||
<span style="background:#f0fdf4;color:var(--c-success);border-radius:8px;
|
||||
padding:4px 10px;font-size:11px;font-weight:600">✓ Gespeichert</span>
|
||||
${score ? `<span>${score}</span>` : ''}
|
||||
<button class="btn btn-sm btn-secondary sm-preview-btn"
|
||||
data-id="${data.id}"
|
||||
style="margin-left:auto;font-size:11px;padding:4px 10px;min-height:30px">
|
||||
style="font-size:11px;padding:4px 10px;min-height:30px">
|
||||
👁 Vorschau</button>
|
||||
<button class="btn btn-sm btn-primary sm-posted-btn"
|
||||
data-id="${data.id}"
|
||||
style="margin-left:auto;font-size:11px;padding:4px 12px;min-height:30px;
|
||||
background:#10b981;border-color:#10b981">
|
||||
📤 Habe ich gepostet!
|
||||
</button>
|
||||
</div>
|
||||
<div id="sm-posted-form-${data.id}" style="display:none;background:var(--c-surface-2);
|
||||
border-radius:10px;padding:12px;margin-bottom:12px">
|
||||
<div style="font-size:12px;font-weight:600;margin-bottom:8px">
|
||||
🎉 Super! Kurze Angaben zum Post:</div>
|
||||
<div style="display:grid;gap:8px">
|
||||
<div>
|
||||
<div class="sm-label">Datum (leer = heute)</div>
|
||||
<input type="date" class="sm-post-date" data-id="${data.id}"
|
||||
style="width:100%;background:var(--c-surface);color:var(--c-text);
|
||||
border:1.5px solid var(--c-border);border-radius:8px;
|
||||
padding:8px 12px;font-size:var(--text-sm);font-family:inherit;
|
||||
box-sizing:border-box">
|
||||
</div>
|
||||
<div>
|
||||
<div class="sm-label">Post-URL (optional)</div>
|
||||
<input type="url" class="sm-post-url" data-id="${data.id}"
|
||||
placeholder="https://www.instagram.com/p/..."
|
||||
style="width:100%;background:var(--c-surface);color:var(--c-text);
|
||||
border:1.5px solid var(--c-border);border-radius:8px;
|
||||
padding:8px 12px;font-size:var(--text-sm);font-family:inherit;
|
||||
box-sizing:border-box">
|
||||
</div>
|
||||
<button class="btn btn-primary sm-confirm-posted" data-id="${data.id}"
|
||||
style="min-height:40px">
|
||||
✓ Bestätigen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${mediaUrl ? `
|
||||
|
|
@ -791,6 +868,47 @@ window.Page_social = (() => {
|
|||
if (item) _showPreview(item);
|
||||
});
|
||||
});
|
||||
// "Habe ich gepostet!" — Formular einblenden
|
||||
el.querySelectorAll('.sm-posted-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const form = el.querySelector(`#sm-posted-form-${btn.dataset.id}`);
|
||||
if (form) {
|
||||
form.style.display = form.style.display === 'none' ? '' : 'none';
|
||||
// Heute als Default-Datum
|
||||
const dateInput = form.querySelector('.sm-post-date');
|
||||
if (dateInput && !dateInput.value) {
|
||||
dateInput.value = new Date().toISOString().slice(0,10);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
// Bestätigen
|
||||
el.querySelectorAll('.sm-confirm-posted').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const id = parseInt(btn.dataset.id);
|
||||
const form = el.querySelector(`#sm-posted-form-${id}`);
|
||||
const date = form?.querySelector('.sm-post-date')?.value
|
||||
|| new Date().toISOString().slice(0,16);
|
||||
const url = form?.querySelector('.sm-post-url')?.value || null;
|
||||
btn.disabled = true;
|
||||
btn.textContent = '…';
|
||||
await API.patch(`/social/content/${id}`, {
|
||||
status: 'published',
|
||||
published_at: date,
|
||||
post_url: url || undefined,
|
||||
});
|
||||
// Form durch Bestätigung ersetzen
|
||||
if (form) form.innerHTML = `
|
||||
<div style="text-align:center;padding:8px;color:var(--c-success);
|
||||
font-weight:600;font-size:var(--text-sm)">
|
||||
🎉 Super! Post als veröffentlicht markiert.
|
||||
${url ? `<br><a href="${_esc(url)}" target="_blank" rel="noopener"
|
||||
style="font-size:11px;color:var(--c-primary)">Post ansehen →</a>` : ''}
|
||||
</div>`;
|
||||
// Stats aktualisieren
|
||||
API.get('/social/stats').then(s => { _stats = s; });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
|
@ -883,12 +1001,28 @@ window.Page_social = (() => {
|
|||
async function load(f) {
|
||||
filter = f;
|
||||
const url = f==='alle' ? '/social/content' : `/social/content?status=${f}`;
|
||||
const items = await API.get(url).catch(() => []);
|
||||
render(items);
|
||||
const [items, allItems] = await Promise.all([
|
||||
API.get(url).catch(() => []),
|
||||
f !== 'alle' ? API.get('/social/content').catch(() => []) : Promise.resolve(null),
|
||||
]);
|
||||
const pending = (allItems || items).filter(c =>
|
||||
c.status === 'idea' || c.status === 'draft').length;
|
||||
render(items, pending);
|
||||
}
|
||||
|
||||
function render(items) {
|
||||
function render(items, pending) {
|
||||
el.innerHTML = `
|
||||
${pending > 0 ? `
|
||||
<div style="background:var(--c-surface-2);border:1.5px solid var(--c-warning);
|
||||
border-radius:10px;padding:10px 12px;margin-bottom:12px;
|
||||
display:flex;align-items:center;gap:10px;font-size:var(--text-sm)">
|
||||
<span style="font-size:1.3em;flex-shrink:0">⏳</span>
|
||||
<div>
|
||||
<div style="font-weight:600;color:var(--c-warning)">${pending} Post${pending>1?'s':''} warten auf Bestätigung</div>
|
||||
<div style="font-size:11px;color:var(--c-text-secondary)">
|
||||
Tippe auf 📤 wenn du einen Post abgesetzt hast — so lernt Luna was wirklich live ging.</div>
|
||||
</div>
|
||||
</div>` : ''}
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:var(--space-3)">
|
||||
${['alle','idea','draft','scheduled','published','archived'].map(s => `
|
||||
<button class="btn btn-sm ${filter===s?'btn-primary':'btn-secondary'}"
|
||||
|
|
@ -916,6 +1050,13 @@ window.Page_social = (() => {
|
|||
font-style:italic">🎣 ${_esc(c.hook)}</div>` : ''}
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;gap:4px;flex-shrink:0">
|
||||
${c.status !== 'published' ? `
|
||||
<button class="btn btn-sm sm-quick-post" data-id="${c.id}"
|
||||
style="padding:3px 8px;font-size:11px;min-height:28px;
|
||||
background:#10b981;border:1px solid #10b981;
|
||||
color:#fff;border-radius:6px;cursor:pointer">
|
||||
📤</button>` : `
|
||||
<div style="text-align:center;font-size:18px;padding:3px 8px">✅</div>`}
|
||||
<button class="btn btn-sm btn-secondary sm-exp"
|
||||
data-id="${c.id}" style="padding:3px 8px;font-size:11px;min-height:28px">
|
||||
Details</button>
|
||||
|
|
@ -953,6 +1094,15 @@ window.Page_social = (() => {
|
|||
</div>`).join('')}`;
|
||||
|
||||
el.querySelectorAll('[data-f]').forEach(b => b.addEventListener('click', () => load(b.dataset.f)));
|
||||
el.querySelectorAll('.sm-quick-post').forEach(b => b.addEventListener('click', async () => {
|
||||
const url = prompt('Post-URL (optional, leer lassen wenn keine):', '') ?? null;
|
||||
await API.patch(`/social/content/${b.dataset.id}`, {
|
||||
status: 'published',
|
||||
published_at: new Date().toISOString().slice(0,16),
|
||||
post_url: url || undefined,
|
||||
});
|
||||
load(filter);
|
||||
}));
|
||||
el.querySelectorAll('.sm-exp').forEach(b => b.addEventListener('click', () => {
|
||||
const d = el.querySelector(`#sm-d-${b.dataset.id}`);
|
||||
if (d) { d.style.display = d.style.display==='none'?'':'none'; }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Offline-Cache + Push Notifications + Tile-Cache
|
||||
============================================================ */
|
||||
|
||||
const CACHE_VERSION = 'by-v349';
|
||||
const CACHE_VERSION = 'by-v356';
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue