- types.ts: CalendarSlot, GeneratedPost, PostResult, JobStatus, Settings, SettingsStatus - api/client.ts: aggiunto apiGet, apiPost, apiPut, apiDownload, triggerDownload - api/hooks.ts: 10+ hooks TanStack Query (settings, generate, job polling, CSV export) - components/Layout.tsx + Sidebar.tsx: sidebar stone/amber palette con 4 nav link - pages/Dashboard.tsx: banner API key, quick actions link, step guide - pages/Settings.tsx: form completo (API key password, LLM select, brand, nicchie checkbox) - App.tsx: 5 route con BrowserRouter basename='/postgenerator', QueryClientProvider, Layout
140 lines
6.2 KiB
TypeScript
140 lines
6.2 KiB
TypeScript
/**
|
|
* Dashboard principale.
|
|
* Mostra stato API key e quick actions per iniziare.
|
|
*/
|
|
|
|
import { AlertTriangle, ArrowRight, Calendar, FileText, Settings } from 'lucide-react'
|
|
import { Link } from 'react-router-dom'
|
|
import { useSettingsStatus } from '../api/hooks'
|
|
|
|
export function Dashboard() {
|
|
const { data: status, isLoading } = useSettingsStatus()
|
|
|
|
const apiKeyOk = status?.api_key_configured ?? false
|
|
|
|
return (
|
|
<div className="max-w-3xl mx-auto px-6 py-10 space-y-8">
|
|
{/* Header */}
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-stone-100">Dashboard</h1>
|
|
<p className="mt-1 text-stone-400 text-sm">
|
|
Genera un calendario editoriale di 13 caroselli Instagram strategici, pronti per Canva.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Banner API key non configurata */}
|
|
{!isLoading && !apiKeyOk && (
|
|
<div className="flex items-start gap-3 px-4 py-3 rounded-lg bg-amber-500/10 border border-amber-500/30">
|
|
<AlertTriangle size={18} className="text-amber-400 flex-shrink-0 mt-0.5" />
|
|
<div className="text-sm">
|
|
<p className="text-amber-300 font-medium">API key Claude non configurata</p>
|
|
<p className="text-stone-400 mt-0.5">
|
|
Devi configurare la tua API key Anthropic prima di poter generare contenuti.{' '}
|
|
<Link to="/impostazioni" className="text-amber-400 underline underline-offset-2 hover:text-amber-300">
|
|
Vai alle Impostazioni
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Banner API key OK */}
|
|
{!isLoading && apiKeyOk && (
|
|
<div className="flex items-center gap-3 px-4 py-3 rounded-lg bg-emerald-500/10 border border-emerald-500/30">
|
|
<div className="w-2 h-2 rounded-full bg-emerald-400 flex-shrink-0" />
|
|
<p className="text-sm text-emerald-300">
|
|
API key configurata — modello: <span className="font-mono text-xs">{status?.llm_model}</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Quick actions */}
|
|
<div className="space-y-3">
|
|
<h2 className="text-xs font-semibold text-stone-500 uppercase tracking-wider">Azioni rapide</h2>
|
|
|
|
<div className="grid gap-3 sm:grid-cols-2">
|
|
<Link
|
|
to="/genera"
|
|
className={[
|
|
'group flex items-center justify-between px-5 py-4 rounded-xl border transition-all',
|
|
apiKeyOk
|
|
? 'bg-stone-800 border-stone-700 hover:border-amber-500/50 hover:bg-stone-700/80'
|
|
: 'bg-stone-800/40 border-stone-800 opacity-60 cursor-not-allowed pointer-events-none',
|
|
].join(' ')}
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-9 h-9 rounded-lg bg-amber-500/15 flex items-center justify-center">
|
|
<Calendar size={18} className="text-amber-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-stone-100">Genera Calendario</p>
|
|
<p className="text-xs text-stone-500">13 post in un click</p>
|
|
</div>
|
|
</div>
|
|
<ArrowRight size={16} className="text-stone-600 group-hover:text-stone-300 transition-colors" />
|
|
</Link>
|
|
|
|
<Link
|
|
to="/genera-singolo"
|
|
className={[
|
|
'group flex items-center justify-between px-5 py-4 rounded-xl border transition-all',
|
|
apiKeyOk
|
|
? 'bg-stone-800 border-stone-700 hover:border-amber-500/50 hover:bg-stone-700/80'
|
|
: 'bg-stone-800/40 border-stone-800 opacity-60 cursor-not-allowed pointer-events-none',
|
|
].join(' ')}
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-9 h-9 rounded-lg bg-stone-700 flex items-center justify-center">
|
|
<FileText size={18} className="text-stone-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-stone-100">Genera Singolo Post</p>
|
|
<p className="text-xs text-stone-500">Test e rigenerazione</p>
|
|
</div>
|
|
</div>
|
|
<ArrowRight size={16} className="text-stone-600 group-hover:text-stone-300 transition-colors" />
|
|
</Link>
|
|
|
|
<Link
|
|
to="/impostazioni"
|
|
className="group flex items-center justify-between px-5 py-4 rounded-xl border bg-stone-800 border-stone-700 hover:border-stone-600 hover:bg-stone-700/80 transition-all"
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-9 h-9 rounded-lg bg-stone-700 flex items-center justify-center">
|
|
<Settings size={18} className="text-stone-400" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-medium text-stone-100">Impostazioni</p>
|
|
<p className="text-xs text-stone-500">API key, modello, nicchie</p>
|
|
</div>
|
|
</div>
|
|
<ArrowRight size={16} className="text-stone-600 group-hover:text-stone-300 transition-colors" />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Descrizione sistema */}
|
|
<div className="space-y-3">
|
|
<h2 className="text-xs font-semibold text-stone-500 uppercase tracking-wider">Come funziona</h2>
|
|
<div className="grid gap-2 sm:grid-cols-3">
|
|
{[
|
|
{ n: '1', title: 'Configura', desc: 'Aggiungi API key e scegli le nicchie target' },
|
|
{ n: '2', title: 'Genera', desc: 'Inserisci obiettivo campagna, Claude crea 13 post' },
|
|
{ n: '3', title: 'Esporta', desc: 'Scarica il CSV pronto per Canva Bulk Create' },
|
|
].map((step) => (
|
|
<div key={step.n} className="px-4 py-3 rounded-lg bg-stone-800/50 border border-stone-800">
|
|
<div className="flex items-center gap-2 mb-1.5">
|
|
<span className="w-5 h-5 rounded-full bg-amber-500/20 text-amber-400 text-xs font-bold flex items-center justify-center">
|
|
{step.n}
|
|
</span>
|
|
<span className="text-sm font-medium text-stone-200">{step.title}</span>
|
|
</div>
|
|
<p className="text-xs text-stone-500">{step.desc}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|