feat: multi-platform generation + custom confirm modal + per-platform tabs

Backend:
- /generate now returns array of posts (one per platform selected)
- Each post generated with platform-specific LLM prompt and char limits
- Monthly counter incremented by number of platforms

Frontend:
- ConfirmModal: reusable Editorial Fresh modal replaces ugly browser confirm()
- ContentPage: platform tabs when multiple posts, switch between variants
- ContentPage: generatedPosts array state replaces single generated
- ContentArchive: uses ConfirmModal for delete confirmation
- Platform chips filtered by plan (Freemium: IG/FB only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michele
2026-04-03 19:05:25 +02:00
parent 5620a71f1b
commit 3b17ed0a9b
4 changed files with 180 additions and 60 deletions

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { api } from '../api'
import ConfirmModal from './ConfirmModal'
const statusLabels = { draft: 'Bozza', approved: 'Approvato', scheduled: 'Schedulato', published: 'Pubblicato' }
const statusColors = {
@@ -19,6 +20,7 @@ export default function ContentArchive() {
const [editText, setEditText] = useState('')
const [filterCharacter, setFilterCharacter] = useState('')
const [filterStatus, setFilterStatus] = useState('')
const [deleteTarget, setDeleteTarget] = useState(null)
useEffect(() => { loadData() }, [])
@@ -40,8 +42,7 @@ export default function ContentArchive() {
try { await api.post(`/content/posts/${postId}/approve`); loadData() } catch {}
}
const handleDelete = async (postId) => {
if (!confirm('Eliminare questo contenuto?')) return
try { await api.delete(`/content/posts/${postId}`); loadData() } catch {}
try { await api.delete(`/content/posts/${postId}`); setDeleteTarget(null); loadData() } catch {}
}
const handleSaveEdit = async (postId) => {
try {
@@ -171,7 +172,7 @@ export default function ContentArchive() {
)}
<button onClick={() => { setEditingId(post.id); setEditText(post.text_content || ''); setExpandedId(post.id) }}
style={{ ...btnSmall, backgroundColor: 'var(--cream-dark)', color: 'var(--ink-light)' }}>Modifica</button>
<button onClick={() => handleDelete(post.id)}
<button onClick={() => setDeleteTarget(post.id)}
style={{ ...btnSmall, color: 'var(--error)', backgroundColor: 'transparent', marginLeft: 'auto' }}>Elimina</button>
</div>
</div>
@@ -180,6 +181,16 @@ export default function ContentArchive() {
})}
</div>
)}
<ConfirmModal
open={deleteTarget !== null}
title="Elimina contenuto"
message="Sei sicuro di voler eliminare questo contenuto? L'operazione non è reversibile."
confirmLabel="Elimina"
cancelLabel="Annulla"
confirmStyle="danger"
onConfirm={() => handleDelete(deleteTarget)}
onCancel={() => setDeleteTarget(null)}
/>
<style>{`@keyframes spin { to { transform: rotate(360deg) } }`}</style>
</div>
)