diff --git a/backend/app/services/content.py b/backend/app/services/content.py index c8e1805..447467f 100644 --- a/backend/app/services/content.py +++ b/backend/app/services/content.py @@ -41,7 +41,12 @@ def generate_post_text( f"Your expertise covers: {topics_str}. " f"Your communication style is {tone}. " f"You create authentic, engaging content that resonates with your audience. " - f"Never reveal you are an AI. Write as {name} would naturally write." + f"Never reveal you are an AI. Write as {name} would naturally write.\n\n" + f"REGOLA CRITICA: Se ti viene indicata una tecnica narrativa (PAS, AIDA, Storytelling, ecc.), " + f"usala SOLO come struttura invisibile del testo. " + f"NON scrivere MAI le etichette del framework nel post (es. non scrivere 'PROBLEMA:', " + f"'AGITAZIONE:', 'SOLUZIONE:', 'ATTENZIONE:', 'INTERESSE:', ecc.). " + f"Il lettore non deve percepire alcun framework — deve sembrare un post naturale e spontaneo." ) # Platform-specific instructions diff --git a/frontend/src/components/ContentPage.jsx b/frontend/src/components/ContentPage.jsx index c12ce43..2456837 100644 --- a/frontend/src/components/ContentPage.jsx +++ b/frontend/src/components/ContentPage.jsx @@ -346,16 +346,11 @@ export default function ContentPage() { )} {generated.hashtags?.length > 0 && ( -
- Hashtag -
- {generated.hashtags.map((tag, i) => ( - - {tag} - - ))} -
-
+ setGenerated(prev => ({ ...prev, hashtags: newTags }))} + postId={generated.id} + /> )}
@@ -382,6 +377,84 @@ export default function ContentPage() { ) } +function HashtagEditor({ hashtags, onChange, postId }) { + const [newTag, setNewTag] = useState('') + const [editIdx, setEditIdx] = useState(null) + const [editValue, setEditValue] = useState('') + const [saving, setSaving] = useState(false) + + const removeTag = (idx) => { + const updated = hashtags.filter((_, i) => i !== idx) + onChange(updated) + } + + const addTag = () => { + let tag = newTag.trim() + if (!tag) return + if (!tag.startsWith('#')) tag = `#${tag}` + if (!hashtags.includes(tag)) { + onChange([...hashtags, tag]) + } + setNewTag('') + } + + const startEdit = (idx) => { + setEditIdx(idx) + setEditValue(hashtags[idx]) + } + + const confirmEdit = () => { + if (editIdx === null) return + let tag = editValue.trim() + if (!tag) { removeTag(editIdx); setEditIdx(null); return } + if (!tag.startsWith('#')) tag = `#${tag}` + const updated = [...hashtags] + updated[editIdx] = tag + onChange(updated) + setEditIdx(null) + } + + const saveHashtags = async () => { + setSaving(true) + try { + await api.put(`/content/posts/${postId}`, { hashtags }) + } catch (e) { /* silent */ } + setSaving(false) + } + + return ( +
+ Hashtag +
+ {hashtags.map((tag, i) => ( + editIdx === i ? ( + setEditValue(e.target.value)} + onBlur={confirmEdit} onKeyDown={e => { if (e.key === 'Enter') confirmEdit(); if (e.key === 'Escape') setEditIdx(null) }} + autoFocus style={{ fontSize: '0.78rem', padding: '0.15rem 0.5rem', border: '1px solid var(--accent)', outline: 'none', fontFamily: "'DM Sans', sans-serif", width: Math.max(60, editValue.length * 8) }} /> + ) : ( + startEdit(i)}> + {tag} + { e.stopPropagation(); removeTag(i) }} style={{ fontSize: '0.65rem', cursor: 'pointer', opacity: 0.7, lineHeight: 1 }} title="Rimuovi">✕ + + ) + ))} +
+
+ setNewTag(e.target.value)} + onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); addTag() } }} + placeholder="Aggiungi hashtag…" style={{ fontSize: '0.8rem', padding: '0.3rem 0.6rem', border: '1px solid var(--border)', outline: 'none', fontFamily: "'DM Sans', sans-serif", flex: 1 }} + onFocus={e => e.target.style.borderColor = 'var(--ink)'} onBlur={e => e.target.style.borderColor = 'var(--border)'} /> + +
+ +
+ ) +} + const labelStyle = { fontSize: '0.72rem', fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'var(--ink)',