fix for settings page

This commit is contained in:
Marek Maslowski
2026-04-05 11:08:58 +02:00
parent 0d3a10120a
commit b0b85fff3a
4 changed files with 264 additions and 1451 deletions
@@ -1,11 +1,12 @@
import React, { useState, useEffect } from 'react'
import { Camera, Terminal, Save, Check, Copy, Plus, Trash2 } from 'lucide-react'
import Section from './Section'
import React, { useEffect, useState } from 'react'
import { useTranslation } from '../../i18n'
import { useToast } from '../shared/Toast'
import { Trash2, Copy, Terminal, Plus, Check } from 'lucide-react'
import { authApi } from '../../api/client'
import apiClient from '../../api/client'
import { useAddonStore } from '../../store/addonStore'
import Section from './Section'
import PhotoProvidersSection from './PhotoProvidersSection'
interface McpToken {
id: number
@@ -18,65 +19,12 @@ interface McpToken {
export default function IntegrationsTab(): React.ReactElement {
const { t, locale } = useTranslation()
const toast = useToast()
const { isEnabled: addonEnabled } = useAddonStore()
const memoriesEnabled = addonEnabled('memories')
const { isEnabled: addonEnabled, loadAddons } = useAddonStore()
const mcpEnabled = addonEnabled('mcp')
// Immich state
const [immichUrl, setImmichUrl] = useState('')
const [immichApiKey, setImmichApiKey] = useState('')
const [immichConnected, setImmichConnected] = useState(false)
const [immichTesting, setImmichTesting] = useState(false)
const [immichSaving, setImmichSaving] = useState(false)
const [immichTestPassed, setImmichTestPassed] = useState(false)
useEffect(() => {
if (memoriesEnabled) {
apiClient.get('/integrations/immich/settings').then(r => {
setImmichUrl(r.data.immich_url || '')
setImmichConnected(r.data.connected)
}).catch(() => {})
}
}, [memoriesEnabled])
const handleSaveImmich = async () => {
setImmichSaving(true)
try {
const saveRes = await apiClient.put('/integrations/immich/settings', { immich_url: immichUrl, immich_api_key: immichApiKey || undefined })
if (saveRes.data.warning) toast.warning(saveRes.data.warning)
toast.success(t('memories.saved'))
const res = await apiClient.get('/integrations/immich/status')
setImmichConnected(res.data.connected)
setImmichTestPassed(false)
} catch {
toast.error(t('memories.connectionError'))
} finally {
setImmichSaving(false)
}
}
const handleTestImmich = async () => {
setImmichTesting(true)
try {
const res = await apiClient.post('/integrations/immich/test', { immich_url: immichUrl, immich_api_key: immichApiKey })
if (res.data.connected) {
if (res.data.canonicalUrl) {
setImmichUrl(res.data.canonicalUrl)
toast.success(`${t('memories.connectionSuccess')}${res.data.user?.name || ''} (URL updated to ${res.data.canonicalUrl})`)
} else {
toast.success(`${t('memories.connectionSuccess')}${res.data.user?.name || ''}`)
}
setImmichTestPassed(true)
} else {
toast.error(`${t('memories.connectionError')}: ${res.data.error}`)
setImmichTestPassed(false)
}
} catch {
toast.error(t('memories.connectionError'))
} finally {
setImmichTesting(false)
}
}
loadAddons()
}, [loadAddons])
// MCP state
const [mcpTokens, setMcpTokens] = useState<McpToken[]>([])
@@ -143,45 +91,7 @@ export default function IntegrationsTab(): React.ReactElement {
return (
<>
{memoriesEnabled && (
<Section title="Immich" icon={Camera}>
<div className="space-y-3">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1.5">{t('memories.immichUrl')}</label>
<input type="url" value={immichUrl} onChange={e => { setImmichUrl(e.target.value); setImmichTestPassed(false) }}
placeholder="https://immich.example.com"
className="w-full px-3 py-2.5 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-slate-300" />
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1.5">{t('memories.immichApiKey')}</label>
<input type="password" value={immichApiKey} onChange={e => { setImmichApiKey(e.target.value); setImmichTestPassed(false) }}
placeholder={immichConnected ? '••••••••' : 'API Key'}
className="w-full px-3 py-2.5 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-slate-300" />
</div>
<div className="flex items-center gap-3">
<button onClick={handleSaveImmich} disabled={immichSaving || !immichTestPassed}
className="flex items-center gap-2 px-4 py-2 bg-slate-900 text-white rounded-lg text-sm hover:bg-slate-700 disabled:bg-slate-400"
title={!immichTestPassed ? t('memories.testFirst') : ''}>
<Save className="w-4 h-4" /> {t('common.save')}
</button>
<button onClick={handleTestImmich} disabled={immichTesting}
className="flex items-center gap-2 px-4 py-2 border border-slate-200 rounded-lg text-sm hover:bg-slate-50">
{immichTesting
? <div className="w-4 h-4 border-2 border-slate-300 border-t-slate-700 rounded-full animate-spin" />
: <Camera className="w-4 h-4" />}
{t('memories.testConnection')}
</button>
{immichConnected && (
<span className="text-xs font-medium text-green-600 flex items-center gap-1">
<span className="w-2 h-2 bg-green-500 rounded-full" />
{t('memories.connected')}
</span>
)}
</div>
</div>
</Section>
)}
<PhotoProvidersSection />
{mcpEnabled && (
<Section title={t('settings.mcp.title')} icon={Terminal}>
{/* Endpoint URL */}