'use client' import { useState } from 'react' import { Button, toast, Spinner } from '@ciphera-net/ui' import { Plugs, Trash, ShieldCheck, CaretDown } from '@phosphor-icons/react' import { useGSCStatus, useBunnyStatus } from '@/lib/swr/dashboard' import { disconnectGSC, getGSCAuthURL } from '@/lib/api/gsc' import { disconnectBunny, getBunnyPullZones, connectBunny, type BunnyPullZone } from '@/lib/api/bunny' import { getAuthErrorMessage } from '@ciphera-net/ui' import { formatDateTime } from '@/lib/utils/formatDate' function GoogleIcon() { return ( ) } function BunnyIcon() { return ( ) } function IntegrationCard({ icon, name, description, connected, detail, onConnect, onDisconnect, connectLabel = 'Connect', children, }: { icon: React.ReactNode name: string description: string connected: boolean detail?: string onConnect: () => void onDisconnect: () => void connectLabel?: string children?: React.ReactNode }) { return (
{icon}

{name}

{connected && ( Connected )}

{detail || description}

{connected ? ( ) : ( )}
{children}
) } function SecurityNote({ text }: { text: string }) { return (

{text}

) } function StatusDot({ status }: { status?: string }) { const color = status === 'active' ? 'bg-green-400' : status === 'syncing' ? 'bg-yellow-400 animate-pulse' : status === 'error' ? 'bg-red-400' : 'bg-neutral-500' const label = status === 'active' ? 'Connected' : status === 'syncing' ? 'Syncing' : status === 'error' ? 'Error' : 'Unknown' return ( {label} ) } function GSCDetails({ gscStatus }: { gscStatus: { connected: boolean; google_email?: string; gsc_property?: string; status?: string; last_synced_at?: string | null; error_message?: string | null } }) { if (!gscStatus.connected) return null const rows = [ { label: 'Status', value: }, { label: 'Google Account', value: gscStatus.google_email || 'Unknown' }, { label: 'GSC Property', value: gscStatus.gsc_property || 'Unknown' }, { label: 'Last Synced', value: gscStatus.last_synced_at ? formatDateTime(new Date(gscStatus.last_synced_at)) : 'Never' }, ] return (
{rows.map(row => (
{row.label} {row.value}
))}
{gscStatus.error_message && (

{gscStatus.error_message}

)}
) } function BunnySetupForm({ siteId, onConnected }: { siteId: string; onConnected: () => void }) { const [apiKey, setApiKey] = useState('') const [pullZones, setPullZones] = useState([]) const [selectedZone, setSelectedZone] = useState(null) const [loadingZones, setLoadingZones] = useState(false) const [connecting, setConnecting] = useState(false) const [zonesLoaded, setZonesLoaded] = useState(false) const handleLoadZones = async () => { if (!apiKey.trim()) { toast.error('Please enter your BunnyCDN API key') return } setLoadingZones(true) try { const data = await getBunnyPullZones(siteId, apiKey.trim()) setPullZones(data.pull_zones || []) setSelectedZone(null) setZonesLoaded(true) if (!data.pull_zones?.length) { toast.error('No pull zones found for this API key') } } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to load pull zones') } finally { setLoadingZones(false) } } const handleConnect = async () => { if (!selectedZone) { toast.error('Please select a pull zone') return } setConnecting(true) try { await connectBunny(siteId, apiKey.trim(), selectedZone.id, selectedZone.name) toast.success('BunnyCDN connected successfully') onConnected() } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to connect BunnyCDN') } finally { setConnecting(false) } } return (
setApiKey(e.target.value)} placeholder="Enter your BunnyCDN API key" className="flex-1 px-3 py-2 text-sm bg-neutral-900 border border-neutral-700 rounded-lg text-white placeholder:text-neutral-500 focus:outline-none focus:border-neutral-500" />
{zonesLoaded && pullZones.length > 0 && (
)} {zonesLoaded && pullZones.length > 0 && ( )}
) } export default function SiteIntegrationsTab({ siteId }: { siteId: string }) { const { data: gscStatus, mutate: mutateGSC } = useGSCStatus(siteId) const { data: bunnyStatus, mutate: mutateBunny } = useBunnyStatus(siteId) const [showBunnySetup, setShowBunnySetup] = useState(false) const handleConnectGSC = async () => { try { const data = await getGSCAuthURL(siteId) window.open(data.auth_url, '_blank') } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to start Google authorization') } } const handleDisconnectGSC = async () => { if (!confirm('Disconnect Google Search Console? This will remove all synced data.')) return try { await disconnectGSC(siteId) await mutateGSC() toast.success('Google Search Console disconnected') } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to disconnect') } } const handleConnectBunny = () => { setShowBunnySetup(true) } const handleDisconnectBunny = async () => { if (!confirm('Disconnect BunnyCDN? This will remove all synced CDN data.')) return try { await disconnectBunny(siteId) await mutateBunny() setShowBunnySetup(false) toast.success('BunnyCDN disconnected') } catch (err) { toast.error(getAuthErrorMessage(err as Error) || 'Failed to disconnect') } } const bunnyConnected = bunnyStatus?.connected ?? false return (

Integrations

Connect third-party services to enrich your analytics.

} name="Google Search Console" description="View search queries, clicks, impressions, and ranking data." connected={gscStatus?.connected ?? false} detail={gscStatus?.connected ? `Connected as ${gscStatus.google_email || 'unknown'}` : undefined} onConnect={handleConnectGSC} onDisconnect={handleDisconnectGSC} connectLabel="Connect with Google" > {gscStatus?.connected && } } name="BunnyCDN" description="Monitor bandwidth, cache hit rates, and CDN performance." connected={bunnyConnected} detail={bunnyConnected ? `Pull zone: ${bunnyStatus?.pull_zone_name || 'connected'}` : undefined} onConnect={handleConnectBunny} onDisconnect={handleDisconnectBunny} > {bunnyConnected && (
Pull Zone {bunnyStatus?.pull_zone_name || 'Unknown'}
Last Synced {bunnyStatus?.last_synced_at ? formatDateTime(new Date(bunnyStatus.last_synced_at)) : 'Never'}
Connected Since {bunnyStatus?.created_at ? formatDateTime(new Date(bunnyStatus.created_at)) : 'Unknown'}
Status
{bunnyStatus?.error_message && (

{bunnyStatus.error_message}

)}
)} {!bunnyConnected && showBunnySetup && ( { mutateBunny() setShowBunnySetup(false) }} /> )}
) }