import { ConnectAccountDialog } from '@/components/open-banking/connect-account-dialog'; import { ConnectionStatusBadge } from '@/components/open-banking/connection-status-badge'; import { DisconnectDialog } from '@/components/open-banking/disconnect-dialog'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { CreateButton } from '@/components/ui/create-button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Spinner } from '@/components/ui/spinner'; import AppLayout from '@/layouts/app-layout'; import SettingsLayout from '@/layouts/settings/layout'; import type { SharedData } from '@/types'; import type { BankingConnection } from '@/types/banking'; import { __ } from '@/utils/i18n'; import { Head, router, usePage, usePoll } from '@inertiajs/react'; import { ArrowRight, MoreHorizontal, RefreshCw, Unplug } from 'lucide-react'; import { useEffect, useState } from 'react'; import { toast } from 'sonner'; interface Props { connections: BankingConnection[]; } export default function ConnectionsPage({ connections }: Props) { const { auth, flash } = usePage().props; const isDemoAccount = auth?.isDemoAccount ?? false; const [connectDialogOpen, setConnectDialogOpen] = useState(false); const [disconnectConnection, setDisconnectConnection] = useState(null); const hasSyncing = connections.some( (c) => c.status === 'active' && !c.last_synced_at, ); const { start, stop } = usePoll(5000, {}, { autoStart: false }); useEffect(() => { if (flash?.error) { toast.error(flash.error); } if (flash?.success) { toast.success(flash.success); } }, [flash?.error, flash?.success]); useEffect(() => { if (hasSyncing) { start(); } else { stop(); } }, [hasSyncing, start, stop]); function handleSync(connection: BankingConnection) { router.post(`/settings/connections/${connection.id}/sync`); } function formatDate(dateString: string | null): string { if (!dateString) return __('Never'); return new Date(dateString).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); } return (

{__('Bank Connections')}

{__( 'Manage your connected bank accounts for automatic transaction syncing.', )}

setConnectDialogOpen(true)} disabled={isDemoAccount} > {__('Connect Bank')}
{connections.length === 0 ? (

{__( 'No bank connections yet. Connect a bank to automatically sync your transactions.', )}

) : (
{connections.map((connection) => (
{connection.aspsp_name} {connection.aspsp_country}{' '} ·{' '} {connection.accounts_count}{' '} {connection.accounts_count === 1 ? __('account') : __('accounts')}
{connection.status === 'awaiting_mapping' && ( router.visit( `/open-banking/connections/${connection.id}/map-accounts`, ) } > {__('Map Accounts')} )} {connection.status === 'active' && ( handleSync( connection, ) } > {__('Sync Now')} )} setDisconnectConnection( connection, ) } className="text-destructive" > {__('Disconnect')}
{connection.status === 'awaiting_mapping' ? ( {__( 'Accounts need to be mapped before syncing can begin.', )} ) : connection.status === 'active' && !connection.last_synced_at ? ( {__( 'Syncing transactions and balances…', )} ) : ( {__('Last synced')}:{' '} {formatDate( connection.last_synced_at, )} )} {connection.valid_until && ( {__('Expires')}:{' '} {formatDate( connection.valid_until, )} )}
{connection.error_message && (

{connection.error_message}

)}
))}
)}
{disconnectConnection && ( { if (!open) setDisconnectConnection(null); }} /> )}
); }