import { useState, useCallback } from 'react';
import { Plug, AlertCircle, CheckCircle, Loader2, X, ExternalLink } from 'lucide-react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { securityApi } from '@/api/securityApi';
import type { ConnectedAccount } from '@/api/securityApi';
const SERVICE_NAMES: Record<string, string> = {
servicetitan: 'ServiceTitan',
hubspot: 'HubSpot',
google_ads: 'Google Ads',
facebook_ads: 'Facebook Ads',
quickbooks: 'QuickBooks',
stripe: 'Stripe',
slack: 'Slack',
xero: 'Xero',
};
const STATUS_CONFIG: Record<string, { color: string; bg: string; label: string; icon: any }> = {
connected: { color: 'text-green-700', bg: 'bg-green-100', label: 'Connected', icon: CheckCircle },
syncing: { color: 'text-blue-700', bg: 'bg-blue-100', label: 'Syncing', icon: Loader2 },
error: { color: 'text-red-700', bg: 'bg-red-100', label: 'Error', icon: AlertCircle },
inactive: { color: 'text-gray-700', bg: 'bg-gray-100', label: 'Inactive', icon: X },
};
function formatDate(dateStr: string | null): string {
if (!dateStr) return 'N/A';
const date = new Date(dateStr);
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
});
}
export default function ConnectedAccounts() {
const [disconnecting, setDisconnecting] = useState<string | null>(null);
const { data: accounts = [], isLoading } = useQuery({
queryKey: ['connected-accounts'],
queryFn: securityApi.getConnectedAccounts,
});
const queryClient = useQueryClient();
const disconnectMutation = useMutation({
mutationFn: (service: string) => securityApi.disconnectAccount(service),
onSuccess: () => {
setDisconnecting(null);
queryClient.invalidateQueries({ queryKey: ['connected-accounts'] });
},
onError: () => {
setDisconnecting(null);
},
});
const handleDisconnect = useCallback(
(service: string) => {
setDisconnecting(service);
disconnectMutation.mutate(service);
},
[disconnectMutation],
);
return (
<div className="bg-white rounded-xl border border-gray-200 p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<Plug className="h-5 w-5 text-gray-600" />
<h3 className="text-lg font-semibold text-gray-900">Connected Accounts</h3>
</div>
</div>
<p className="text-sm text-gray-500 mb-4">
Manage your connected third-party services and integrations.
</p>
{/* Loading */}
{isLoading && (
<div className="flex items-center gap-2 text-gray-400 py-8 justify-center">
<Loader2 className="h-4 w-4 animate-spin" />
<span className="text-sm">Loading connected accounts...</span>
</div>
)}
{/* Empty state */}
{!isLoading && accounts.length === 0 && (
<div className="text-center py-8 text-gray-400">
<Plug className="h-8 w-8 mx-auto mb-2 opacity-50" />
<p className="text-sm">No connected accounts</p>
</div>
)}
{/* Accounts list */}
{!isLoading && accounts.length > 0 && (
<div className="space-y-3">
{accounts.map((account) => {
const statusConfig = STATUS_CONFIG[account.status] || STATUS_CONFIG.inactive;
const StatusIcon = statusConfig.icon;
const displayName = SERVICE_NAMES[account.service] || account.display_name || account.service;
return (
<div
key={account.service}
className="flex items-center justify-between p-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors"
>
<div className="flex items-center gap-3">
<div className="text-2xl w-10 h-10 flex items-center justify-center bg-gray-50 rounded-lg">
{account.icon || '🔌'}
</div>
<div>
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-900">{displayName}</span>
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium ${statusConfig.bg} ${statusConfig.color}`}>
<StatusIcon className={`h-3 w-3 ${account.status === 'syncing' ? 'animate-spin' : ''}`} />
{statusConfig.label}
</span>
</div>
<div className="flex items-center gap-3 mt-0.5 text-xs text-gray-400">
<span>Connected {formatDate(account.connected_at)}</span>
{account.last_sync && <span>Last sync {formatDate(account.last_sync)}</span>}
</div>
</div>
</div>
<div className="flex items-center gap-2 ml-3 flex-shrink-0">
{account.providerUrl && (
<a
href={account.providerUrl}
target="_blank"
rel="noopener noreferrer"
className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
title="Open provider"
>
<ExternalLink className="h-4 w-4" />
</a>
)}
{account.status !== 'inactive' && (
<button
onClick={() => handleDisconnect(account.service)}
disabled={disconnectMutation.isPending}
className="inline-flex items-center gap-1 px-2.5 py-1.5 text-sm text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg transition-colors"
title="Disconnect"
>
{disconnectMutation.isPending && disconnecting === account.service ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<X className="h-4 w-4" />
)}
Disconnect
</button>
)}
</div>
</div>
);
})}
</div>
)}
</div>
);
}