'use client' import { useState, useEffect } from 'react' import { BookOpen, Loader2, Check, X, RefreshCw, Trash2, CalendarDays, Mail } from 'lucide-react' import { toast } from 'sonner' import { SettingsHelpBox } from '@/components/settings/settings-help-box' import { useLanguage } from '@/lib/i18n' export default function IntegrationsPage() { const { t } = useLanguage() // ── Readwise ─────────────────────────────────────────────────────────── const [rwToken, setRwToken] = useState('') const [rwConnected, setRwConnected] = useState(false) const [rwSyncing, setRwSyncing] = useState(false) const [rwConnecting, setRwConnecting] = useState(false) const [rwLastSync, setRwLastSync] = useState<{ created: number; updated: number } | null>(null) // ── Google Calendar ──────────────────────────────────────────────────── const [calConnected, setCalConnected] = useState(false) const [calLoading, setCalLoading] = useState(true) const [calEvents, setCalEvents] = useState([]) const [calFetching, setCalFetching] = useState(false) // ── Gmail ──────────────────────────────────────────────────────────── const [gmailConnected, setGmailConnected] = useState(false) const [gmailLoading, setGmailLoading] = useState(true) const [gmailRecent, setGmailRecent] = useState(0) const [gmailScanning, setGmailScanning] = useState(false) const [loading, setLoading] = useState(true) useEffect(() => { Promise.all([ fetch('/api/integrations/readwise').then((r) => r.json()), fetch('/api/integrations/calendar').then((r) => r.json()), fetch('/api/integrations/gmail').then((r) => r.json()), ]).then(([rw, cal, gmail]) => { setRwConnected(rw.connected) setCalConnected(cal.connected) setGmailConnected(gmail.connected) setGmailRecent(gmail.recentCaptures ?? 0) }).finally(() => { setLoading(false) setCalLoading(false) setGmailLoading(false) }) // Handle redirect params const params = new URLSearchParams(window.location.search) if (params.get('connected') === 'calendar') { setCalConnected(true) toast.success(t('integrations.calendarConnected')) window.history.replaceState({}, '', '/settings/integrations') } if (params.get('connected') === 'gmail') { setGmailConnected(true) toast.success(t('integrations.gmail.connected')) window.history.replaceState({}, '', '/settings/integrations') } if (params.get('error')) { toast.error(t('integrations.errorWith', { error: params.get('error') || '' })) window.history.replaceState({}, '', '/settings/integrations') } }, [t]) // ── Readwise handlers ────────────────────────────────────────────────── const handleRwConnect = async () => { if (!rwToken.trim()) return setRwConnecting(true) try { const res = await fetch('/api/integrations/readwise', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: rwToken.trim() }), }) const data = await res.json() if (!res.ok) { toast.error(data.error || t('integrations.readwiseError')); return } setRwConnected(true) setRwToken('') setRwLastSync({ created: data.created, updated: data.updated }) toast.success(t('integrations.readwiseConnected', { created: data.created, updated: data.updated })) } catch { toast.error(t('integrations.readwiseConnectError')) } finally { setRwConnecting(false) } } const handleRwSync = async () => { setRwSyncing(true) try { const res = await fetch('/api/integrations/readwise', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}), }) const data = await res.json() if (!res.ok) { toast.error(data.error || t('integrations.syncError')); return } setRwLastSync({ created: data.created, updated: data.updated }) toast.success(t('integrations.readwiseSynced', { created: data.created, updated: data.updated })) } catch { toast.error(t('integrations.syncFailed')) } finally { setRwSyncing(false) } } const handleRwDisconnect = async () => { await fetch('/api/integrations/readwise', { method: 'DELETE' }) setRwConnected(false) toast.success(t('integrations.readwiseDisconnected')) } // ── Calendar handlers ────────────────────────────────────────────────── const handleCalConnect = () => { window.location.href = '/api/integrations/calendar?connect=1' } const handleCalFetchEvents = async () => { setCalFetching(true) try { const res = await fetch('/api/integrations/calendar?events=1') const data = await res.json() if (!res.ok) { toast.error(data.error || t('common.error')); return } setCalEvents(data.events ?? []) if (data.events.length === 0) toast.info(t('integrations.noEvents')) } catch { toast.error(t('integrations.eventsLoadError')) } finally { setCalFetching(false) } } const handleCreateMeetingNote = async (event: any) => { const res = await fetch('/api/integrations/calendar', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ eventId: event.id, summary: event.summary, start: event.start }), }) const data = await res.json() if (data.success) { toast.success(t('integrations.meetingNoteCreated', { summary: event.summary }), { action: { label: t('integrations.open'), onClick: () => window.location.href = `/home?openNote=${data.note.id}` }, }) } else { toast.error(t('integrations.createNoteError')) } } const handleCalDisconnect = async () => { await fetch('/api/integrations/calendar', { method: 'DELETE' }) setCalConnected(false) setCalEvents([]) toast.success(t('integrations.calendarDisconnected')) } // ── Gmail handlers ───────────────────────────────────────────────────── const handleGmailConnect = () => { window.location.href = '/api/integrations/gmail?connect=1' } const handleGmailScan = async () => { setGmailScanning(true) try { const res = await fetch('/api/integrations/gmail?scan=1') const data = await res.json() if (!res.ok) { toast.error(data.error || t('common.error')); return } setGmailRecent(prev => prev + (data.created ?? 0)) toast.success(t('integrations.gmail.scanDone', { created: data.created ?? 0 })) } catch { toast.error(t('integrations.gmailScanError')) } finally { setGmailScanning(false) } } const handleGmailDisconnect = async () => { await fetch('/api/integrations/gmail', { method: 'DELETE' }) setGmailConnected(false) setGmailRecent(0) toast.success(t('integrations.gmail.disconnected')) } const StatusBadge = ({ connected }: { connected: boolean }) => connected ? ( {t('integrations.connected')} ) : ( {t('integrations.notConnected')} ) return (

{t('settings.integrationsTitle')}

{t('settings.integrationsDesc')}

{/* ── Gmail ──────────────────────────────────────────────────────── */}

{t('integrations.gmail.title')}

{t('integrations.gmail.description')}

{gmailLoading ? : }
{!gmailConnected ? (
) : (
{gmailRecent > 0 && (

{t('integrations.gmail.recentCaptures', { count: gmailRecent })}

)}
)}
{/* ── Google Calendar ────────────────────────────────────────────── */}

Google Calendar

{t('integrations.calendarDesc')}

{calLoading ? : }
{!calConnected ? (
) : (
{calEvents.length > 0 && (
{calEvents.map((ev) => (

{ev.summary}

{ev.start ? new Date(ev.start).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }) : ''}

))}
)}
)}
{/* ── Readwise ──────────────────────────────────────────────────── */}

Readwise

{t('integrations.readwiseDesc')}

{loading ? : }
{!rwConnected && (
setRwToken(e.target.value)} placeholder={t('integrations.readwiseTokenPlaceholder')} className="flex-1 text-sm border border-border/40 rounded-xl px-3 py-2 bg-paper text-ink placeholder:text-concrete/50 focus:outline-none focus:ring-1 focus:ring-brand-accent/40" onKeyDown={(e) => e.key === 'Enter' && handleRwConnect()} />
)} {rwConnected && (
)} {rwLastSync && (

{t('integrations.lastSync')} {rwLastSync.created} {t('integrations.createdLabel')}, {rwLastSync.updated} {t('integrations.updatedLabel')}

)}
{/* Placeholder */}

{t('integrations.moreComing')}

) }