Social: Vorschläge merken (📌), Post-Link nachträglich eintragen, Quick-Post ohne prompt(), SW by-v369
This commit is contained in:
parent
092230c4e1
commit
e2bb1a4b2d
3 changed files with 130 additions and 16 deletions
|
|
@ -1052,6 +1052,27 @@ async def get_suggestions(user=Depends(require_social_media)):
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
# POST /api/social/ideas/save — Vorschlag als Idee speichern (zurückstellen)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@router.post("/ideas/save", status_code=201)
|
||||||
|
async def save_idea(data: dict, user=Depends(require_social_media)):
|
||||||
|
"""Speichert einen KI-Vorschlag als Idee in der DB (status='idea')."""
|
||||||
|
topic = (data.get("topic") or data.get("thema") or "").strip()
|
||||||
|
platform = data.get("platform", "both")
|
||||||
|
fmt = data.get("format", "post")
|
||||||
|
category = data.get("category")
|
||||||
|
if not topic:
|
||||||
|
raise HTTPException(400, "topic fehlt.")
|
||||||
|
with db() as conn:
|
||||||
|
cur = conn.execute(
|
||||||
|
"""INSERT INTO social_content
|
||||||
|
(created_by, platform, format, topic, status, category, source)
|
||||||
|
VALUES (?,?,?,?,'idea',?,'saved_suggestion')""",
|
||||||
|
(user["id"], platform, fmt, topic, category),
|
||||||
|
)
|
||||||
|
return {"id": cur.lastrowid, "topic": topic, "status": "idea"}
|
||||||
|
|
||||||
|
|
||||||
# GET /api/social/content — alle Einträge
|
# GET /api/social/content — alle Einträge
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@router.get("/content")
|
@router.get("/content")
|
||||||
|
|
|
||||||
|
|
@ -422,13 +422,21 @@ window.Page_social = (() => {
|
||||||
padding:2px 6px;border-radius:4px">${_PL[idea.platform]||idea.platform}</span>
|
padding:2px 6px;border-radius:4px">${_PL[idea.platform]||idea.platform}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display:flex;flex-direction:column;gap:5px;flex-shrink:0">
|
||||||
<button class="btn btn-primary btn-sm sm-use"
|
<button class="btn btn-primary btn-sm sm-use"
|
||||||
style="flex-shrink:0;font-size:11px;padding:6px 10px;min-height:34px;
|
style="font-size:11px;padding:6px 10px;min-height:34px;white-space:nowrap"
|
||||||
white-space:nowrap"
|
|
||||||
data-thema="${_esc(idea.thema)}"
|
data-thema="${_esc(idea.thema)}"
|
||||||
data-format="${idea.format||'post'}"
|
data-format="${idea.format||'post'}"
|
||||||
data-platform="${idea.platform||'both'}">
|
data-platform="${idea.platform||'both'}">
|
||||||
Nutzen →</button>
|
Nutzen →</button>
|
||||||
|
<button class="btn btn-secondary btn-sm sm-save-idea"
|
||||||
|
style="font-size:10px;padding:4px 8px;min-height:26px;white-space:nowrap"
|
||||||
|
data-thema="${_esc(idea.thema)}"
|
||||||
|
data-format="${idea.format||'post'}"
|
||||||
|
data-platform="${idea.platform||'both'}"
|
||||||
|
data-category="${_esc(idea.category||'')}">
|
||||||
|
📌 Merken</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`).join('');
|
</div>`).join('');
|
||||||
|
|
||||||
|
|
@ -453,6 +461,28 @@ window.Page_social = (() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
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 => {
|
box.querySelectorAll('.sm-idea').forEach(card => {
|
||||||
card.addEventListener('mouseenter', () => card.style.borderColor = 'var(--c-primary)');
|
card.addEventListener('mouseenter', () => card.style.borderColor = 'var(--c-primary)');
|
||||||
card.addEventListener('mouseleave', () => card.style.borderColor = 'transparent');
|
card.addEventListener('mouseleave', () => card.style.borderColor = 'transparent');
|
||||||
|
|
@ -1126,7 +1156,19 @@ window.Page_social = (() => {
|
||||||
<div style="font-weight:600;font-size:var(--text-sm);margin-bottom:3px;
|
<div style="font-weight:600;font-size:var(--text-sm);margin-bottom:3px;
|
||||||
line-height:1.3">${_esc(c.topic)}</div>
|
line-height:1.3">${_esc(c.topic)}</div>
|
||||||
${c.hook ? `<div style="font-size:11px;color:var(--c-text-secondary);
|
${c.hook ? `<div style="font-size:11px;color:var(--c-text-secondary);
|
||||||
font-style:italic">🎣 ${_esc(c.hook)}</div>` : ''}
|
font-style:italic;margin-bottom:2px">🎣 ${_esc(c.hook)}</div>` : ''}
|
||||||
|
${c.post_url
|
||||||
|
? `<a href="${_esc(c.post_url)}" target="_blank" rel="noopener"
|
||||||
|
style="font-size:10px;color:var(--c-primary);display:inline-flex;
|
||||||
|
align-items:center;gap:3px;margin-top:2px">
|
||||||
|
🔗 Post ansehen</a>`
|
||||||
|
: c.status === 'published'
|
||||||
|
? `<button class="sm-add-url" data-id="${c.id}"
|
||||||
|
style="font-size:10px;color:var(--c-text-muted);background:none;
|
||||||
|
border:none;cursor:pointer;padding:0;margin-top:2px;
|
||||||
|
text-align:left;text-decoration:underline">
|
||||||
|
+ Link eintragen</button>`
|
||||||
|
: ''}
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;flex-direction:column;gap:4px;flex-shrink:0">
|
<div style="display:flex;flex-direction:column;gap:4px;flex-shrink:0">
|
||||||
${c.status !== 'published' ? `
|
${c.status !== 'published' ? `
|
||||||
|
|
@ -1173,14 +1215,65 @@ window.Page_social = (() => {
|
||||||
</div>`).join('')}`;
|
</div>`).join('')}`;
|
||||||
|
|
||||||
el.querySelectorAll('[data-f]').forEach(b => b.addEventListener('click', () => load(b.dataset.f)));
|
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;
|
// Quick-Post: Inline-Form statt prompt()
|
||||||
await API.patch(`/social/content/${b.dataset.id}`, {
|
el.querySelectorAll('.sm-quick-post').forEach(b => b.addEventListener('click', () => {
|
||||||
status: 'published',
|
const id = b.dataset.id;
|
||||||
published_at: new Date().toISOString().slice(0,16),
|
UI.modal.open({
|
||||||
post_url: url || undefined,
|
title: '📤 Als gepostet markieren',
|
||||||
|
body: `
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Datum</label>
|
||||||
|
<input class="form-control" type="date" id="qp-date-${id}"
|
||||||
|
value="${new Date().toISOString().slice(0,10)}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Post-Link <span style="color:var(--c-text-muted)">(optional)</span></label>
|
||||||
|
<input class="form-control" type="url" id="qp-url-${id}"
|
||||||
|
placeholder="https://www.instagram.com/p/…">
|
||||||
|
</div>`,
|
||||||
|
footer: `
|
||||||
|
<button class="btn btn-secondary flex-1" id="qp-cancel">Abbrechen</button>
|
||||||
|
<button class="btn btn-primary flex-1" id="qp-ok-${id}">✓ Bestätigen</button>`,
|
||||||
});
|
});
|
||||||
|
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);
|
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: `
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Link zum veröffentlichten Post</label>
|
||||||
|
<input class="form-control" type="url" id="au-url-${id}"
|
||||||
|
placeholder="https://www.instagram.com/p/…" autofocus>
|
||||||
|
</div>`,
|
||||||
|
footer: `
|
||||||
|
<button class="btn btn-secondary flex-1" id="au-cancel">Abbrechen</button>
|
||||||
|
<button class="btn btn-primary flex-1" id="au-ok-${id}">💾 Speichern</button>`,
|
||||||
|
});
|
||||||
|
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', () => {
|
el.querySelectorAll('.sm-exp').forEach(b => b.addEventListener('click', () => {
|
||||||
const d = el.querySelector(`#sm-d-${b.dataset.id}`);
|
const d = el.querySelector(`#sm-d-${b.dataset.id}`);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Offline-Cache + Push Notifications + Tile-Cache
|
Offline-Cache + Push Notifications + Tile-Cache
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const CACHE_VERSION = 'by-v368';
|
const CACHE_VERSION = 'by-v369';
|
||||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue