/* BAN YARO — Social Media Manager (Luna-Coach) */
window.Page_social = (() => {
let _el, _state, _breeds = [], _activeTab = 'idee', _stats = null;
const _SL = { idea:'Idee', draft:'Entwurf', scheduled:'Geplant',
published:'Veröffentlicht', archived:'Archiviert' };
const _SC = { idea:'var(--c-text-muted)', draft:'var(--c-warning)',
scheduled:'var(--c-primary)', published:'var(--c-success)',
archived:'var(--c-text-muted)' };
const _FL = { reel:' Reel', story:'⭕ Story', post:'🖼 Post', carousel:'🎠 Carousel' };
const _PL = { tiktok:' TikTok', instagram:' Instagram', both:' Beide' };
// Luna-Wartebotschaften (alle 10s rotierend)
const _LUNA_MSGS = [
['🌙','Ich überlege…'],
['🤔','Einen Moment noch…'],
['💡','Mir ist da was eingefallen…'],
['✍️','Ich arbeite das aus…'],
['⏳','Kleinen Moment noch…'],
[' ','Gleich hab ich es…'],
['🎯','Ich feile noch etwas dran…'],
['🐾','Fast fertig für dich…'],
];
let _diversity = null, _unusedBreeds = [];
async function init(el, state) {
_el = el; _state = state;
[_breeds, _stats, _diversity, _unusedBreeds] = await Promise.all([
API.get('/social/breeds').catch(() => []),
API.get('/social/stats').catch(() => null),
API.get('/social/diversity').catch(() => null),
API.get('/social/unused-breeds?limit=6').catch(() => []),
]);
_render();
}
function refresh() { if (_el) init(_el, _state); }
// ---------------------------------------------------------------
// HAUPT-RENDER
// ---------------------------------------------------------------
function _render() {
const lvlBar = _stats ? _levelBar(_stats) : '';
_el.innerHTML = `
📱
Social Media
Luna ist dein KI-Coach
${_stats ? `
${_stats.level}
${_stats.xp} XP
` : ''}
${lvlBar}
${[['idee',' Ideen'],['archiv','📂 Archiv'],['bewerten','🔍 Prüfen']].map(([t,l]) => `
${l} `).join('')}
`;
_el.querySelectorAll('.sm-tab').forEach(b =>
b.addEventListener('click', () => { _activeTab = b.dataset.tab; _render(); }));
const c = _el.querySelector('#sm-content');
if (_activeTab === 'idee') _renderIdee(c);
if (_activeTab === 'archiv') _renderArchiv(c);
if (_activeTab === 'bewerten') _renderBewerten(c);
}
function _levelBar(s) {
if (!s.xp_next) return '';
const pct = Math.min(100, Math.round(
((s.xp - s.xp_current_min) / (s.xp_next - s.xp_current_min)) * 100));
return `
${s.level}
${s.next_level ? `${s.xp_next - s.xp} XP bis ${s.next_level} ` : ''}
`;
}
// ---------------------------------------------------------------
// IDEEN-TAB
// ---------------------------------------------------------------
function _renderIdee(el) {
let selPlatform = 'both', selFormat = 'post', uploadedMediaUrl = null;
el.innerHTML = `
🌙
Hey, ich bin Luna 👋
Ich schlage dir Ideen vor und erkläre warum sie funktionieren.
Lern Social Media richtig — nicht nur kopieren!
${_diversity?.warning ? `
⚠️
Luna bemerkt eine Wiederholung!
${_diversity.warning.pct}% deiner letzten Posts waren
${_diversity.warning.dominant_de} -Content.
Versuch mal was anderes, damit dein Feed abwechslungsreicher wird!
${_diversity.warning.suggestions_de.length ? `
💡 Probier mal: ${_diversity.warning.suggestions_de.join(', ')}
` : ''}
` : ''}
💡 Was könntest du heute posten?
↻ Neue
${_lunaThinking('Klein...')}
Plattform
${['both','instagram','tiktok'].map((p,i) => `
${_PL[p]} `).join('')}
Format
${['post','reel','story','carousel'].map((f,i) => `
${_FL[f]} `).join('')}
Idee übernommen — prüf die Einstellungen und tippe auf Los geht's! 👇
Rasse (optional)
${_unusedBreeds.length ? `
🌙 Noch nicht gezeigt:
${_unusedBreeds.map(b =>
`
${UI.escape(b.name)} `).join('')}
` : ''}
${_breeds.map(b => ``).join('')}
Schnell generieren
🐾
Rasse des Tages
${_unusedBreeds.length ? `${_unusedBreeds.length} übrig ` : ''}
🎾
Trainingstipp
104 Übungen
🛁
Pflegetipp
je Rasse
📋 Alle 104 Übungen ansehen →
Los geht's!
`;
// Platform toggle
el.querySelectorAll('.sm-plat').forEach(b => b.addEventListener('click', () => {
selPlatform = b.dataset.p;
el.querySelectorAll('.sm-plat').forEach(x =>
x.className = `btn btn-sm sm-plat ${x===b?'btn-primary':'btn-secondary'}`);
}));
el.querySelectorAll('.sm-fmt').forEach(b => b.addEventListener('click', () => {
selFormat = b.dataset.f;
el.querySelectorAll('.sm-fmt').forEach(x =>
x.className = `btn btn-sm sm-fmt ${x===b?'btn-primary':'btn-secondary'}`);
}));
// Breed-Chips
el.querySelectorAll('.sm-breed-chip').forEach(chip => {
chip.addEventListener('click', () => {
const search = el.querySelector('#sm-breed-search');
const hiddenId = el.querySelector('#sm-breed-id');
if (search) search.value = chip.dataset.name;
if (hiddenId) hiddenId.value = chip.dataset.id;
el.querySelectorAll('.sm-breed-chip').forEach(c =>
c.style.background = c === chip ? 'var(--c-primary)' : 'var(--c-surface-2)');
el.querySelectorAll('.sm-breed-chip').forEach(c =>
c.style.color = c === chip ? '#fff' : 'var(--c-text)');
});
});
// Datalist-Autocomplete → breed-id setzen
el.querySelector('#sm-breed-search')?.addEventListener('input', e => {
const val = e.target.value.trim().toLowerCase();
const match = _breeds.find(b => b.name.toLowerCase() === val);
const hiddenId = el.querySelector('#sm-breed-id');
if (hiddenId) hiddenId.value = match ? match.id : '';
});
// Media upload
function handleMedia(file) {
if (!file) return;
const preview = el.querySelector('#sm-media-preview');
const reader = new FileReader();
reader.onload = e => {
preview.style.display = '';
if (file.type.startsWith('video/')) {
preview.innerHTML = ` `;
} else {
preview.innerHTML = ` `;
}
// Upload
const fd = new FormData();
fd.append('file', file);
fetch('/api/social/media', {
method: 'POST',
headers: {Authorization: `Bearer ${localStorage.getItem('by_token')}`},
body: fd,
}).then(r => {
// r.ok prüfen: SW antwortet offline mit 503+JSON → json() wirft nicht, d.url wäre undefined
if (!r.ok) throw new Error(`upload ${r.status}`);
return r.json();
}).then(d => { uploadedMediaUrl = d.url; })
.catch(() => UI.toast.error('Medien-Upload fehlgeschlagen.'));
};
reader.readAsDataURL(file);
}
el.querySelector('#sm-media-file').addEventListener('change', e => handleMedia(e.target.files[0]));
el.querySelector('#sm-media-file2').addEventListener('change', e => handleMedia(e.target.files[0]));
// Vorschläge laden
async function loadSuggestions() {
const box = el.querySelector('#sm-suggestions');
box.innerHTML = ``;
let mi = 0;
const sgInt = setInterval(() => {
mi = (mi + 1) % _LUNA_MSGS.length;
const [e, t] = _LUNA_MSGS[mi];
const te = box.querySelector('#sg-text');
const ee = box.querySelector('#sg-emoji');
if (te) te.textContent = 'Luna: ' + t;
if (ee) ee.textContent = e;
}, 10000);
try {
const ideas = await API.get('/social/suggestions');
clearInterval(sgInt);
if (!ideas?.length) { box.innerHTML = 'Keine Ideen erhalten.
'; return; }
box.innerHTML = ideas.map((idea, i) => `
${idea.emoji||'💡'}
${UI.escape(idea.thema)}
🎓 ${UI.escape(idea.warum)}
${_FL[idea.format]||idea.format}
${_PL[idea.platform]||idea.platform}
Nutzen →
Merken
`).join('');
box.querySelectorAll('.sm-use').forEach(btn => {
btn.addEventListener('click', e => {
e.stopPropagation();
el.querySelector('#sm-topic').value = btn.dataset.thema;
const fb = el.querySelector(`.sm-fmt[data-f="${btn.dataset.format}"]`);
if (fb) fb.click();
const pb = el.querySelector(`.sm-plat[data-p="${btn.dataset.platform}"]`);
if (pb) pb.click();
// Banner + Scroll zu "Los geht's!"
const hint = el.querySelector('#sm-next-hint');
if (hint) hint.style.display = '';
const genBtn = el.querySelector('#sm-gen');
if (genBtn) {
setTimeout(() => {
genBtn.scrollIntoView({behavior:'smooth', block:'center'});
genBtn.style.transform = 'scale(1.04)';
setTimeout(() => genBtn.style.transform = '', 600);
}, 200);
}
});
});
box.querySelectorAll('.sm-save-idea').forEach(btn => {
btn.addEventListener('click', async e => {
e.stopPropagation();
btn.disabled = true;
btn.textContent = '…';
try {
await API.post('/social/ideas/save', {
topic: btn.dataset.thema,
format: btn.dataset.format,
platform: btn.dataset.platform,
category: btn.dataset.category || undefined,
});
btn.textContent = ' Gemerkt';
btn.style.background = 'var(--c-success)';
btn.style.color = '#fff';
btn.style.borderColor = 'var(--c-success)';
} catch {
btn.textContent = ' Merken';
btn.disabled = false;
}
});
});
box.querySelectorAll('.sm-idea').forEach(card => {
card.addEventListener('mouseenter', () => card.style.borderColor = 'var(--c-primary)');
card.addEventListener('mouseleave', () => card.style.borderColor = 'transparent');
});
} catch {
clearInterval(sgInt);
box.innerHTML = 'Ideen konnten nicht geladen werden.
';
}
}
el.querySelector('#sm-refresh').addEventListener('click', loadSuggestions);
loadSuggestions();
// Übungs-Übersicht
el.querySelector('#sm-show-exercises').addEventListener('click', async () => {
const exercises = await API.get('/social/exercises').catch(() => []);
const cats = [...new Set(exercises.map(e => e.kategorie))];
const modal = document.createElement('div');
modal.style.cssText = `position:fixed;inset:0;background:rgba(0,0,0,.75);
z-index:9999;overflow-y:auto;padding:16px`;
modal.innerHTML = `
🎾 ${exercises.length} Übungen
✕
${cats.map(cat => `
${cat}
${exercises.filter(e=>e.kategorie===cat).map(e => `
${UI.escape(e.name)}
${UI.escape(e.schwierigkeit||'')} · ${UI.escape(e.alter_ab||'')} · ${UI.escape(e.dauer||'')}
${e.posts_count > 0 ? ` ${e.posts_count}x ` : ''}
Nutzen
`).join('')}
`).join('')}
Schließen
`;
document.body.appendChild(modal);
['sm-ex-close','sm-ex-close2'].forEach(id =>
modal.querySelector(`#${id}`)?.addEventListener('click', () => modal.remove()));
modal.addEventListener('click', e => { if(e.target===modal) modal.remove(); });
modal.querySelectorAll('.sm-ex-use').forEach(btn => {
btn.addEventListener('click', () => {
// Trainingstipp für diese spezifische Übung generieren
modal.remove();
const res = el.querySelector('#sm-gen-result');
const trainBtn = el.querySelector('#sm-training-tip');
// Direkt generieren mit exercise filter via topic-Feld
el.querySelector('#sm-topic').value = btn.dataset.name;
trainBtn.scrollIntoView({behavior:'smooth', block:'center'});
const hint = el.querySelector('#sm-next-hint');
if (hint) { hint.style.display=''; hint.querySelector('strong').textContent = 'Trainingstipp generieren! 🎾'; }
});
});
});
// Trainingstipp
el.querySelector('#sm-training-tip').addEventListener('click', async () => {
const btn = el.querySelector('#sm-training-tip');
const res = el.querySelector('#sm-gen-result');
btn.disabled = true;
res.innerHTML = _lunaProgressHtml();
const interval = _startProgress(res);
try {
const data = await API.post('/social/training-tip', {});
clearInterval(interval.bar); clearInterval(interval.msg);
_progressDone(res);
await new Promise(r => setTimeout(r, 400));
const stilLabel = {tutorial:'📹 Tutorial', community:'🙋 Community', aspirational:'💪 Aspirational'}[data.stil] || '';
res.innerHTML = `
🎾
Trainingstipp · ${UI.escape(data.exercise_kat||'')} · ${stilLabel}
${UI.escape(data.exercise_name||'')}
${_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 = `
😬 ${UI.escape(e.message||String(e))}
`;
} finally {
btn.disabled = false;
}
});
// 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 = `
🛁
Pflegetipp · ${UI.escape(data.pflege_kat||'')}
${data.rasse_name ? ` · speziell für ${UI.escape(data.rasse_name)}` : ''}
${UI.escape(data.pflege_titel||'')}
${_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 = `
😬 ${UI.escape(e.message||String(e))}
`;
} finally { btn.disabled = false; }
});
// Rasse des Tages
el.querySelector('#sm-breed-day').addEventListener('click', async () => {
const btn = el.querySelector('#sm-breed-day');
const res = el.querySelector('#sm-gen-result');
btn.disabled = true;
res.innerHTML = _lunaProgressHtml();
const interval = _startProgress(res);
try {
const data = await API.post('/social/breed-of-day', {});
clearInterval(interval.bar); clearInterval(interval.msg);
_progressDone(res);
await new Promise(r => setTimeout(r, 400));
// Foto anzeigen wenn vorhanden
const mediaUrl = data.breed_foto || data.media_url || null;
res.innerHTML = `
${mediaUrl ? `
` : '
🐶 '}
Rasse des Tages
${UI.escape(data.topic?.replace('Rasse des Tages: ',''))}
${_renderResult(data, mediaUrl)}`;
_bindResultEvents(res);
// unused breeds neu laden
API.get('/social/unused-breeds?limit=6').then(r => { _unusedBreeds = r; });
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 = `
😬 ${UI.escape(e.message||String(e))}
`;
} finally {
btn.disabled = false;
}
});
// Generieren
el.querySelector('#sm-gen').addEventListener('click', async () => {
const topic = el.querySelector('#sm-topic').value.trim();
if (!topic) { UI.toast('Gib ein Thema ein 🐾', 'warning'); return; }
const btn = el.querySelector('#sm-gen');
const res = el.querySelector('#sm-gen-result');
btn.disabled = true;
// Luna-Progress starten
res.innerHTML = _lunaProgressHtml();
const interval = _startProgress(res);
try {
const data = await API.post('/social/generate', {
platform: selPlatform, format: selFormat,
topic,
breed_id: parseInt(el.querySelector('#sm-breed-id')?.value) || null,
});
clearInterval(interval.bar); clearInterval(interval.msg);
_progressDone(res);
await new Promise(r => setTimeout(r, 400));
res.innerHTML = _renderResult(data, uploadedMediaUrl);
_bindResultEvents(res);
el.querySelector('#sm-topic').value = '';
const hint = el.querySelector('#sm-next-hint');
if (hint) hint.style.display = 'none';
uploadedMediaUrl = null;
el.querySelector('#sm-media-preview').style.display = 'none';
// Stats + Diversity aktualisieren
Promise.all([
API.get('/social/stats'),
API.get('/social/diversity'),
]).then(([s, d]) => { _stats = s; _diversity = d; _updateLevelDisplay(); });
} catch(e) {
clearInterval(interval);
res.innerHTML = `
😬 Ups: ${UI.escape(e.message||String(e))}
`;
} finally {
btn.disabled = false;
btn.innerHTML = ' Los geht\'s!';
}
});
}
// ---------------------------------------------------------------
// LUNA PROGRESS ANIMATION
// ---------------------------------------------------------------
function _lunaThinking(msg = 'Denkt nach…') {
return ``;
}
function _lunaProgressHtml() {
return `
🌙
Luna liest dein Thema…
0%
`;
}
function _startProgress(container) {
let elapsed = 0, msgIdx = 0;
const totalMs = 2800;
// Fortschrittsbalken: alle 250ms
const barInterval = setInterval(() => {
elapsed += 250;
const pct = Math.min(93, Math.round((elapsed / totalMs) * 100));
const bar = container.querySelector('#lp-bar');
const pctEl = container.querySelector('#lp-pct');
if (bar) bar.style.width = pct + '%';
if (pctEl) pctEl.textContent = pct + '%';
}, 250);
// Text: alle 10s rotieren
const [e0, t0] = _LUNA_MSGS[0];
const txt = container.querySelector('#lp-text');
const emoji = container.querySelector('#lp-emoji');
if (txt) txt.textContent = 'Luna: ' + t0;
if (emoji) emoji.textContent = e0;
const msgInterval = setInterval(() => {
msgIdx = (msgIdx + 1) % _LUNA_MSGS.length;
const [e, t] = _LUNA_MSGS[msgIdx];
const txt2 = container.querySelector('#lp-text');
const emoji2 = container.querySelector('#lp-emoji');
if (txt2) txt2.textContent = 'Luna: ' + t;
if (emoji2) emoji2.textContent = e;
}, 10000);
// Beide Intervalle zurückgeben
return { bar: barInterval, msg: msgInterval };
}
function _progressDone(container) {
const bar = container.querySelector('#lp-bar');
const txt = container.querySelector('#lp-text');
const emoji = container.querySelector('#lp-emoji');
const pctEl = container.querySelector('#lp-pct');
if (bar) bar.style.width = '100%';
if (pctEl) pctEl.textContent = '100%';
if (txt) txt.textContent = '🎉 Fertig!';
if (emoji) emoji.textContent = ' ';
}
// ---------------------------------------------------------------
// RESULT RENDER
// ---------------------------------------------------------------
function _renderResult(data, mediaUrl) {
const score = data.ai_score ? '⭐'.repeat(Math.min(data.ai_score,5)) : '';
const unsplash = data.unsplash_query
? `https://unsplash.com/s/photos/${encodeURIComponent(data.unsplash_query)}` : null;
return `
${data.coaching ? `
🌙
Luna sagt:
${UI.escape(data.coaching)}
` : ''}
Gespeichert
${score ? `${score} ` : ''}
📤 Habe ich gepostet!
Vorschau
🎉 Super! Kurze Angaben zum Post:
${mediaUrl ? `
📎 Dein Medien-Upload
` : ''}
${_resultBlock('📝 Caption', data.caption, true)}
${data.hashtags ? `
Hashtags
${data.hashtags.split(',').map(h=>`#${h.trim()}`).join(' ')}
${_copyBtn(data.hashtags.split(',').map(h=>`#${h.trim()}`).join(' '))}
` : ''}
${(data.hook||data.cta) ? `
${data.hook ? `
🎣 Hook
"${UI.escape(data.hook)}"
` : ''}
${data.cta ? `
📣 Call-to-Action
${UI.escape(data.cta)}
` : ''}
` : ''}
${_resultBlock('📸 Was du filmen/fotografieren solltest', data.visual_brief, false)}
${data.script ? `
🎬 Video-Aufbau
${UI.escape(data.script)}
` : ''}
${(data.image_prompt||data.canva_notes||unsplash) ? `
🛠 Wenn du kein eigenes Bild hast
${data.image_prompt ? `
DALL-E / Midjourney:
${UI.escape(data.image_prompt)}
${_copyBtn(data.image_prompt)}` : ''}
${data.canva_notes ? `
Canva:
${UI.escape(data.canva_notes)}
` : ''}
${unsplash ? `
🔍 Kostenlose Fotos auf Unsplash → ` : ''}
` : ''}`;
}
function _resultBlock(label, text, copyable) {
if (!text) return '';
return `
${label}
${UI.escape(text)}
${copyable ? _copyBtn(text) : ''}
`;
}
function _copyBtn(text) {
return `
📋 Kopieren `;
}
function _bindResultEvents(el) {
el.querySelectorAll('.sm-copy').forEach(btn => {
btn.addEventListener('click', () => {
const text = btn.getAttribute('data-copy')
.replace(/&/g,'&').replace(/</g,'<')
.replace(/>/g,'>').replace(/"/g,'"');
navigator.clipboard?.writeText(text).then(() => {
btn.textContent = ' Kopiert!';
setTimeout(() => btn.textContent = '📋 Kopieren', 2000);
});
});
});
el.querySelectorAll('.sm-preview-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const id = parseInt(btn.dataset.id);
const contents = await API.get('/social/content').catch(() => []);
const item = contents.find(c => c.id === id);
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 = `
🎉 Super! Post als veröffentlicht markiert.
${url ? `
Post ansehen → ` : ''}
`;
// Stats aktualisieren
API.get('/social/stats').then(s => { _stats = s; });
});
});
}
// ---------------------------------------------------------------
// POST-VORSCHAU
// ---------------------------------------------------------------
function _showPreview(item) {
const modal = document.createElement('div');
modal.style.cssText = `position:fixed;inset:0;background:rgba(0,0,0,.7);
z-index:9999;display:flex;align-items:center;justify-content:center;padding:16px;
overflow-y:auto`;
const isReel = item.format === 'reel';
const w = isReel ? '220px' : '280px';
const h = isReel ? '390px' : '300px';
modal.innerHTML = `
${item.platform==='tiktok'?'🎵 TikTok':'📸 Instagram'} Vorschau
✕
🐶
🔖
banyaro.app
${UI.escape((item.caption||'').substring(0,150))}${(item.caption||'').length>150?'…':''}
${item.hashtags ? `
${item.hashtags.split(',').slice(0,5).map(h=>`#${h.trim()}`).join(' ')}
` : ''}
Schließen
`;
document.body.appendChild(modal);
['sm-close-preview','sm-close-preview2'].forEach(id => {
modal.querySelector(`#${id}`)?.addEventListener('click', () => modal.remove());
});
modal.addEventListener('click', e => { if (e.target===modal) modal.remove(); });
}
function _updateLevelDisplay() {
if (!_stats || !_el) return;
const lvlEl = _el.querySelector('[data-level]');
if (lvlEl) lvlEl.textContent = _stats.level;
}
// ---------------------------------------------------------------
// ARCHIV
// ---------------------------------------------------------------
async function _renderArchiv(el) {
el.innerHTML = _lunaThinking('Lädt…');
let filter = 'alle';
const fLabel = {alle:'Alle',idea:'Ideen',draft:'Entwürfe',
scheduled:'Geplant',published:'Veröffentlicht',archived:'Archiviert'};
async function load(f) {
filter = f;
const url = f==='alle' ? '/social/content' : `/social/content?status=${f}`;
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, pending) {
el.innerHTML = `
${pending > 0 ? `
⏳
${pending} Post${pending>1?'s':''} warten auf Bestätigung
Tippe auf 📤 wenn du einen Post abgesetzt hast — so lernt Luna was wirklich live ging.
` : ''}
${['alle','idea','draft','scheduled','published','archived'].map(s => `
${fLabel[s]} `).join('')}
${!items.length
? UI.emptyState({icon:'camera',title:'Noch nichts hier',
text:'Geh zu " Ideen" und erstelle deinen ersten Post!'})
: items.map(c => `
${_SL[c.status]||c.status}
${c.created_at?.slice(0,10)||''}
${c.ai_score ? `${'⭐'.repeat(c.ai_score)} ` : ''}
${UI.escape(c.topic)}
${c.hook ? `
🎣 ${UI.escape(c.hook)}
` : ''}
${c.post_url
? `
Post ansehen `
: c.status === 'published'
? `
+ Link eintragen `
: ''}
${c.status !== 'published' ? `
📤 ` : `
`}
Details
✕
${c.coaching ? `
🌙 ${UI.escape(c.coaching)}
` : ''}
${c.caption ? `
Caption:
${UI.escape(c.caption)}
${_copyBtn(c.caption)}` : ''}
${c.hashtags ? `
${c.hashtags.split(',').map(h=>`#${h.trim()}`).join(' ')}
${_copyBtn(c.hashtags.split(',').map(h=>`#${h.trim()}`).join(' '))}` : ''}
${({idea:['draft','archived'],draft:['scheduled','archived'],
scheduled:['published','draft'],published:['archived'],
archived:['idea']}[c.status]||[]).map(s => `
→ ${fLabel[s]||s} `).join('')}
`).join('')}`;
el.querySelectorAll('[data-f]').forEach(b => b.addEventListener('click', () => load(b.dataset.f)));
// Quick-Post: Inline-Form statt prompt()
el.querySelectorAll('.sm-quick-post').forEach(b => b.addEventListener('click', () => {
const id = b.dataset.id;
UI.modal.open({
title: '📤 Als gepostet markieren',
body: `
Datum
Post-Link (optional)
`,
footer: `
Abbrechen
Bestätigen `,
});
document.getElementById('qp-cancel')?.addEventListener('click', UI.modal.close);
document.getElementById(`qp-ok-${id}`)?.addEventListener('click', async () => {
const date = document.getElementById(`qp-date-${id}`)?.value
|| new Date().toISOString().slice(0,10);
const url = document.getElementById(`qp-url-${id}`)?.value || undefined;
await API.patch(`/social/content/${id}`, {
status: 'published',
published_at: date,
post_url: url,
});
UI.modal.close();
load(filter);
});
}));
// "Link eintragen" für bereits veröffentlichte Posts ohne URL
el.querySelectorAll('.sm-add-url').forEach(b => b.addEventListener('click', () => {
const id = b.dataset.id;
UI.modal.open({
title: ' Post-Link eintragen',
body: `
Link zum veröffentlichten Post
`,
footer: `
Abbrechen
💾 Speichern `,
});
document.getElementById('au-cancel')?.addEventListener('click', UI.modal.close);
document.getElementById(`au-ok-${id}`)?.addEventListener('click', async () => {
const url = document.getElementById(`au-url-${id}`)?.value;
if (!url) return;
await API.patch(`/social/content/${id}`, { post_url: url });
UI.modal.close();
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'; }
}));
el.querySelectorAll('.sm-copy').forEach(b => b.addEventListener('click', () => {
const text = b.getAttribute('data-copy')
.replace(/&/g,'&').replace(/</g,'<')
.replace(/>/g,'>').replace(/"/g,'"');
navigator.clipboard?.writeText(text).then(() => {
b.textContent = ' Kopiert!';
setTimeout(() => b.textContent = '📋 Kopieren', 2000);
});
}));
el.querySelectorAll('.sm-sts').forEach(b => b.addEventListener('click', async () => {
await API.patch(`/social/content/${b.dataset.id}`, {status: b.dataset.s});
load(filter);
}));
el.querySelectorAll('.sm-del').forEach(b => b.addEventListener('click', async () => {
if (!window.confirm('Löschen?')) return;
await API.delete(`/social/content/${b.dataset.id}`);
load(filter);
}));
el.querySelectorAll('.sm-prev-arch').forEach(b => b.addEventListener('click', async () => {
const it = items.find(c => c.id === parseInt(b.dataset.id));
if (it) _showPreview(it);
}));
}
await load('alle');
}
// ---------------------------------------------------------------
// BEWERTEN
// ---------------------------------------------------------------
function _renderBewerten(el) {
let selPlatform = 'instagram';
el.innerHTML = `
🌙
Zeig mir deinen Entwurf — ich sage dir was gut ist und wie du ihn
noch besser machen kannst!
${['instagram','tiktok','both'].map((p,i) => `
${_PL[p]} `).join('')}
🔍 Luna, schau mal drüber!
`;
el.querySelectorAll('.sm-ep').forEach(b => b.addEventListener('click', () => {
selPlatform = b.dataset.p;
el.querySelectorAll('.sm-ep').forEach(x =>
x.className = `btn btn-sm sm-ep ${x===b?'btn-primary':'btn-secondary'}`);
}));
el.querySelector('#sm-eval').addEventListener('click', async () => {
const draft = el.querySelector('#sm-draft').value.trim();
if (!draft) { UI.toast('Gib einen Text ein 😊', 'warning'); return; }
const btn = el.querySelector('#sm-eval');
const res = el.querySelector('#sm-eval-res');
btn.disabled = true;
res.innerHTML = _lunaProgressHtml();
const interval = _startProgress(res);
try {
const data = await API.post('/social/evaluate', {
platform: selPlatform, format: 'post', draft,
});
clearInterval(interval.bar); clearInterval(interval.msg);
_progressDone(res);
await new Promise(r => setTimeout(r, 400));
res.innerHTML = `
${data.notes ? `
🌙
Lunas Feedback:
${UI.escape(data.notes)}
` : ''}
${_renderResult(data, null)}`;
_bindResultEvents(res);
API.get('/social/stats').then(s => { _stats = s; });
} catch(e) {
clearInterval(interval);
res.innerHTML = `😬 Fehler: ${UI.escape(e.message||String(e))}
`;
} finally {
btn.disabled = false;
btn.innerHTML = '🔍 Luna, schau mal drüber!';
}
});
}
function _lunaBreedSuggestions(breeds, n = 3) {
// 3 zufällige Rassen aus den ersten 100 (bekannte Rassen)
const pool = breeds.slice(0, 100);
const picked = [];
const used = new Set();
while (picked.length < n && picked.length < pool.length) {
const i = Math.floor(Math.random() * pool.length);
if (!used.has(i)) { used.add(i); picked.push(pool[i]); }
}
return picked;
}
// CSS
const style = document.createElement('style');
style.textContent = `
.sm-label{font-size:11px;font-weight:700;color:var(--c-text-muted);
text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px;display:block}
@keyframes luna-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.15)}}
#sm-breed-day:hover,#sm-training-tip:hover,#sm-pflege-tip:hover{
transform:translateY(-2px);box-shadow:var(--shadow-md)!important}
#sm-breed-day:active,#sm-training-tip:active,#sm-pflege-tip:active{
transform:translateY(0)}
#sm-show-exercises:hover{background:var(--c-surface-2)!important;
border-color:var(--c-border)!important;color:var(--c-text-secondary)!important}
.sm-breed-chip:hover{background:var(--c-primary-subtle)!important;
border-color:var(--c-primary)!important;color:var(--c-primary-dark)!important}
`;
document.head.appendChild(style);
return { init, refresh };
})();