feat(01-04): layout, routing, API hooks, tipi TypeScript, Dashboard, Settings

- 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
This commit is contained in:
Michele
2026-03-08 02:23:55 +01:00
parent 60b46cb5c1
commit 738a877d39
11 changed files with 941 additions and 14 deletions

View File

@@ -0,0 +1,139 @@
/**
* 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>
)
}