feat(04-02): Unsplash API key in types e pagina Settings

- Aggiunto unsplash_api_key a interface Settings
- Aggiunto unsplash_api_key_configured a interface SettingsStatus
- Aggiunta sezione 'Immagini' in Settings con campo API Key Unsplash
- Toggle visibilita' showUnsplashKey separato da showApiKey
- Helper text condizionale: messaggio diverso se key configurata o no
- Logica submit: unsplash_api_key vuota non sovrascrive quella esistente
- Reset campo dopo salvataggio come per api_key Claude
This commit is contained in:
Michele
2026-03-09 08:15:42 +01:00
parent d320bf04f5
commit d537c03706
2 changed files with 41 additions and 2 deletions

View File

@@ -30,6 +30,7 @@ export function Settings() {
const [form, setForm] = useState<Partial<SettingsType>>({})
const [showApiKey, setShowApiKey] = useState(false)
const [showUnsplashKey, setShowUnsplashKey] = useState(false)
const [saved, setSaved] = useState(false)
// Popola il form quando i settings arrivano dal backend
@@ -37,6 +38,7 @@ export function Settings() {
if (settings) {
setForm({
api_key: '', // Non pre-populare l'API key (mascherata)
unsplash_api_key: '', // Non pre-populare la Unsplash key (mascherata)
llm_model: settings.llm_model,
nicchie_attive: settings.nicchie_attive,
lingua: settings.lingua,
@@ -62,16 +64,20 @@ export function Settings() {
async function handleSubmit(e: React.FormEvent) {
e.preventDefault()
// Prepara il payload: non inviare api_key se vuota (evita sovrascrittura)
// Prepara il payload: non inviare api_key/unsplash_api_key se vuote (evita sovrascrittura)
const payload: Partial<SettingsType> = { ...form }
if (!payload.api_key || payload.api_key.trim() === '') {
delete payload.api_key
}
if (!payload.unsplash_api_key || payload.unsplash_api_key.trim() === '') {
delete payload.unsplash_api_key
}
try {
await updateMutation.mutateAsync(payload)
setSaved(true)
setForm((prev) => ({ ...prev, api_key: '' })) // Reset campo API key dopo salvataggio
// Reset campi API key dopo salvataggio
setForm((prev) => ({ ...prev, api_key: '', unsplash_api_key: '' }))
setTimeout(() => setSaved(false), 3000)
} catch {
// L'errore è gestito da updateMutation.error
@@ -141,6 +147,37 @@ export function Settings() {
</div>
</section>
{/* Immagini / Unsplash */}
<section className="space-y-4">
<h2 className="text-xs font-semibold text-stone-500 uppercase tracking-wider">Immagini</h2>
<div className="space-y-1.5">
<label className="block text-sm font-medium text-stone-300">
API Key Unsplash <span className="text-stone-600 font-normal">(opzionale)</span>
</label>
<div className="relative">
<input
type={showUnsplashKey ? 'text' : 'password'}
value={form.unsplash_api_key ?? ''}
onChange={(e) => setForm((p) => ({ ...p, unsplash_api_key: e.target.value }))}
placeholder={settings?.unsplash_api_key ? '••••••••••••••••' : 'Incolla la tua Access Key Unsplash'}
className="w-full px-3 py-2 pr-10 rounded-lg bg-stone-800 border border-stone-700 text-stone-100 text-sm placeholder-stone-600 focus:outline-none focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500/50"
/>
<button
type="button"
onClick={() => setShowUnsplashKey((v) => !v)}
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-stone-500 hover:text-stone-300 transition-colors"
>
{showUnsplashKey ? <EyeOff size={16} /> : <Eye size={16} />}
</button>
</div>
<p className="text-xs text-stone-600">
{settings?.unsplash_api_key
? 'API key Unsplash configurata. Le keyword verranno risolte in URL immagini reali nel CSV.'
: 'Opzionale. Registrati su unsplash.com/developers per ottenere una Access Key gratuita (50 req/h).'}
</p>
</div>
</section>
{/* Brand */}
<section className="space-y-4">
<h2 className="text-xs font-semibold text-stone-500 uppercase tracking-wider">Brand</h2>

View File

@@ -146,11 +146,13 @@ export interface Settings {
frequenza_post: number
brand_name?: string | null
tono?: string | null
unsplash_api_key?: string | null
}
export interface SettingsStatus {
api_key_configured: boolean
llm_model: string
unsplash_api_key_configured: boolean
}
// ---------------------------------------------------------------------------