'use client' import { useState, useCallback } from 'react' import { useBannerConsents } from '../_hooks/useBannerConsents' import { BannerConsentRecord, PAGE_SIZE } from '../_types' const BANNER_API = '/api/sdk/v1/banner' const TENANT_ID = '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e' function formatDate(iso: string | null): string { if (!iso) return '—' return new Date(iso).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }) } function shortenFingerprint(fp: string): string { return fp.length > 12 ? fp.slice(0, 12) + '...' : fp } function shortenUA(ua: string | null): string { if (!ua) return '—' const match = ua.match(/(Chrome|Safari|Firefox|Edge|Opera)\/[\d.]+/) if (match) return match[0] return ua.length > 30 ? ua.slice(0, 30) + '...' : ua } const categoryColors: Record = { essential: 'bg-gray-100 text-gray-700', functional: 'bg-blue-100 text-blue-700', analytics: 'bg-purple-100 text-purple-700', marketing: 'bg-pink-100 text-pink-700', } const methodLabels: Record = { accept_all: 'Alle akzeptiert', reject_all: 'Nur notwendige', custom_selection: 'Individuelle Auswahl', } const methodColors: Record = { accept_all: 'bg-green-100 text-green-700', reject_all: 'bg-red-100 text-red-700', custom_selection: 'bg-yellow-100 text-yellow-700', } export default function BannerConsentsTab() { const { records, sites, selectedSite, changeSite, stats, currentPage, setCurrentPage, totalRecords, loading, reload, } = useBannerConsents() const [detail, setDetail] = useState(null) const [linkEmailInput, setLinkEmailInput] = useState('') const [linkingEmail, setLinkingEmail] = useState(false) const totalPages = Math.ceil(totalRecords / PAGE_SIZE) const withdrawConsent = useCallback(async (id: string) => { if (!confirm('Consent wirklich widerrufen? Diese Aktion kann nicht rueckgaengig gemacht werden.')) return await fetch(`${BANNER_API}/consent/${id}`, { method: 'DELETE', headers: { 'x-tenant-id': TENANT_ID } }) setDetail(null) reload() }, [reload]) const linkEmail = useCallback(async (record: BannerConsentRecord) => { if (!linkEmailInput.includes('@')) return setLinkingEmail(true) await fetch(`${BANNER_API}/consent/link-email`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-tenant-id': TENANT_ID }, body: JSON.stringify({ site_id: record.site_id, device_fingerprint: record.device_fingerprint, email: linkEmailInput }), }) setLinkingEmail(false) setLinkEmailInput('') setDetail({ ...record, linked_email: linkEmailInput }) reload() }, [linkEmailInput, reload]) return (
{/* Stats + Site Selector */}
{totalRecords} Consents
{stats && Object.keys(stats.category_acceptance).length > 0 && (
{Object.entries(stats.category_acceptance).map(([cat, data]) => ( {cat}: {data.rate}% ))}
)}
{sites.length > 0 && ( )}
{/* Table */}
{loading && records.length === 0 ? ( ) : records.length === 0 ? ( ) : ( records.map(record => ( )) )}
Device Kategorien Methode Erteilt am Ablauf Browser Aktion
Laden...
Keine Consents vorhanden
{shortenFingerprint(record.device_fingerprint)}
{record.categories.length > 0 ? record.categories.map(cat => ( {cat} )) : }
{record.consent_method ? ( {methodLabels[record.consent_method] || record.consent_method} ) : } {formatDate(record.created_at)} {formatDate(record.expires_at)} {shortenUA(record.user_agent)}
{/* Pagination */} {totalPages > 1 && (
Seite {currentPage} von {totalPages} ({totalRecords} Einträge)
)} {/* Detail Modal */} {detail && (
setDetail(null)}>
e.stopPropagation()}>

Consent Details

ID{detail.id}
Site{detail.site_id}
Device{detail.device_fingerprint}
Kategorien
{detail.categories.map(cat => ( {cat} ))}
{detail.vendor_consents && Object.keys(detail.vendor_consents).length > 0 && (
Vendors
{Object.entries(detail.vendor_consents).map(([name, accepted]) => ( {name} ))}
)}
Methode {detail.consent_method ? ( {methodLabels[detail.consent_method] || detail.consent_method} ) : '—'}
Verknüpft mit {detail.linked_email ? ( {detail.linked_email} ) : (
setLinkEmailInput(e.target.value)} className="text-xs border border-gray-200 rounded px-2 py-1 w-40" />
)}
Erteilt{formatDate(detail.created_at)}
Ablauf{formatDate(detail.expires_at)}
Aktualisiert{formatDate(detail.updated_at)}
Geltungsbereich{detail.consent_scope || '—'}
{detail.banner_version && (
Banner-Version{detail.banner_version}
)} {/* Tracking-Kontext */}

Tracking-Kontext

{detail.page_url &&
Seite{detail.page_url}
} {detail.referrer &&
Referrer{detail.referrer}
} {detail.geo_country &&
Land{detail.geo_country}{detail.geo_region ? ` / ${detail.geo_region}` : ''}
}
{/* Device-Informationen */}

Device

Typ{detail.device_type || '—'} Browser{detail.browser || shortenUA(detail.user_agent)} OS{detail.os || '—'} Auflösung{detail.screen_resolution || '—'}
{/* Scripts & Cookies */} {(detail.scripts_released?.length > 0 || detail.cookies_set?.length > 0) && (

Scripts & Cookies

{detail.scripts_released?.length > 0 && (
Freigegebene Scripts {detail.scripts_released.map((s, i) => (

{s.src} {s.category}

))}
)} {detail.scripts_blocked?.length > 0 && (
Blockierte Scripts {detail.scripts_blocked.map((s, i) => (

{s.src} {s.category}

))}
)} {detail.cookies_set?.length > 0 && (
Gesetzte Cookies {detail.cookies_set.map((c, i) => (

{c.name} ({c.domain}) {c.category}

))}
)}
)} {/* Technische Details */}

Technisch

User-Agent

{detail.user_agent || '—'}

{detail.ip_hash &&
IP-Hash

{detail.ip_hash}

} {detail.session_id &&
Session

{detail.session_id}

} {detail.banner_config_hash &&
Config-Hash

{detail.banner_config_hash}

}
{/* Widerruf-Button */}
)}
) }