feat(cmp): vendor-agnostic consent data model — 13 new fields
Build + Deploy / build-admin-compliance (push) Successful in 2m28s
Build + Deploy / build-backend-compliance (push) Successful in 3m48s
Build + Deploy / build-ai-sdk (push) Failing after 45s
Build + Deploy / build-developer-portal (push) Successful in 1m28s
Build + Deploy / build-tts (push) Successful in 1m48s
Build + Deploy / build-document-crawler (push) Successful in 48s
Build + Deploy / build-dsms-gateway (push) Successful in 34s
Build + Deploy / build-dsms-node (push) Successful in 20s
CI / branch-name (push) Has been skipped
Build + Deploy / trigger-orca (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 24s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m1s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Failing after 49s
CI / test-python-backend (push) Successful in 45s
CI / test-python-document-crawler (push) Successful in 31s
CI / test-python-dsms-gateway (push) Successful in 27s
CI / validate-canonical-controls (push) Successful in 18s
Build + Deploy / build-admin-compliance (push) Successful in 2m28s
Build + Deploy / build-backend-compliance (push) Successful in 3m48s
Build + Deploy / build-ai-sdk (push) Failing after 45s
Build + Deploy / build-developer-portal (push) Successful in 1m28s
Build + Deploy / build-tts (push) Successful in 1m48s
Build + Deploy / build-document-crawler (push) Successful in 48s
Build + Deploy / build-dsms-gateway (push) Successful in 34s
Build + Deploy / build-dsms-node (push) Successful in 20s
CI / branch-name (push) Has been skipped
Build + Deploy / trigger-orca (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 24s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m1s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Failing after 49s
CI / test-python-backend (push) Successful in 45s
CI / test-python-document-crawler (push) Successful in 31s
CI / test-python-dsms-gateway (push) Successful in 27s
CI / validate-canonical-controls (push) Successful in 18s
Extend banner consent records with consent_method, banner_version, banner_config_hash, geo, page_url, referrer, device info, session_id and consent_scope for full Art. 7 DSGVO proof with any tracking vendor. Migration 107, backward-compatible (all fields nullable). Admin detail modal shows tracking context, device info and technical data. Fix pre-existing str|None → Optional[str] for Python 3.9 compat. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,18 @@ const categoryColors: Record<string, string> = {
|
||||
marketing: 'bg-pink-100 text-pink-700',
|
||||
}
|
||||
|
||||
const methodLabels: Record<string, string> = {
|
||||
accept_all: 'Alle akzeptiert',
|
||||
reject_all: 'Nur notwendige',
|
||||
custom_selection: 'Individuelle Auswahl',
|
||||
}
|
||||
|
||||
const methodColors: Record<string, string> = {
|
||||
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,
|
||||
@@ -76,7 +88,7 @@ export default function BannerConsentsTab() {
|
||||
<tr>
|
||||
<th className="text-left px-4 py-3 font-medium text-gray-500">Device</th>
|
||||
<th className="text-left px-4 py-3 font-medium text-gray-500">Kategorien</th>
|
||||
<th className="text-left px-4 py-3 font-medium text-gray-500">Verknüpft mit</th>
|
||||
<th className="text-left px-4 py-3 font-medium text-gray-500">Methode</th>
|
||||
<th className="text-left px-4 py-3 font-medium text-gray-500">Erteilt am</th>
|
||||
<th className="text-left px-4 py-3 font-medium text-gray-500">Ablauf</th>
|
||||
<th className="text-left px-4 py-3 font-medium text-gray-500">Browser</th>
|
||||
@@ -102,10 +114,11 @@ export default function BannerConsentsTab() {
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-xs">
|
||||
{record.linked_email
|
||||
? <span className="text-purple-600">{record.linked_email}</span>
|
||||
: <span className="text-gray-400">— (anonym)</span>
|
||||
}
|
||||
{record.consent_method ? (
|
||||
<span className={`px-2 py-0.5 rounded-full ${methodColors[record.consent_method] || 'bg-gray-100 text-gray-600'}`}>
|
||||
{methodLabels[record.consent_method] || record.consent_method}
|
||||
</span>
|
||||
) : <span className="text-gray-400">—</span>}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-xs text-gray-600">{formatDate(record.created_at)}</td>
|
||||
<td className="px-4 py-3 text-xs text-gray-600">{formatDate(record.expires_at)}</td>
|
||||
@@ -171,6 +184,14 @@ export default function BannerConsentsTab() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">Methode</span>
|
||||
<span>{detail.consent_method ? (
|
||||
<span className={`text-xs px-2 py-0.5 rounded-full ${methodColors[detail.consent_method] || 'bg-gray-100'}`}>
|
||||
{methodLabels[detail.consent_method] || detail.consent_method}
|
||||
</span>
|
||||
) : '—'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-500">Verknüpft mit</span>
|
||||
<span>{detail.linked_email || '— (anonym)'}</span>
|
||||
@@ -178,16 +199,40 @@ export default function BannerConsentsTab() {
|
||||
<div className="flex justify-between"><span className="text-gray-500">Erteilt</span><span>{formatDate(detail.created_at)}</span></div>
|
||||
<div className="flex justify-between"><span className="text-gray-500">Ablauf</span><span>{formatDate(detail.expires_at)}</span></div>
|
||||
<div className="flex justify-between"><span className="text-gray-500">Aktualisiert</span><span>{formatDate(detail.updated_at)}</span></div>
|
||||
<div className="border-t border-gray-100 pt-3">
|
||||
<span className="text-gray-500 text-xs">User-Agent</span>
|
||||
<p className="text-xs text-gray-600 mt-1 font-mono break-all">{detail.user_agent || '—'}</p>
|
||||
</div>
|
||||
{detail.ip_hash && (
|
||||
<div>
|
||||
<span className="text-gray-500 text-xs">IP-Hash</span>
|
||||
<p className="text-xs text-gray-600 mt-1 font-mono">{detail.ip_hash}</p>
|
||||
</div>
|
||||
<div className="flex justify-between"><span className="text-gray-500">Geltungsbereich</span><span>{detail.consent_scope || '—'}</span></div>
|
||||
{detail.banner_version && (
|
||||
<div className="flex justify-between"><span className="text-gray-500">Banner-Version</span><span>{detail.banner_version}</span></div>
|
||||
)}
|
||||
|
||||
{/* Tracking-Kontext */}
|
||||
<div className="border-t border-gray-100 pt-3">
|
||||
<p className="text-xs font-semibold text-gray-700 mb-2">Tracking-Kontext</p>
|
||||
{detail.page_url && <div className="flex justify-between"><span className="text-gray-500 text-xs">Seite</span><span className="text-xs text-gray-600 truncate max-w-[250px]">{detail.page_url}</span></div>}
|
||||
{detail.referrer && <div className="flex justify-between mt-1"><span className="text-gray-500 text-xs">Referrer</span><span className="text-xs text-gray-600 truncate max-w-[250px]">{detail.referrer}</span></div>}
|
||||
{detail.geo_country && <div className="flex justify-between mt-1"><span className="text-gray-500 text-xs">Land</span><span className="text-xs text-gray-600">{detail.geo_country}{detail.geo_region ? ` / ${detail.geo_region}` : ''}</span></div>}
|
||||
</div>
|
||||
|
||||
{/* Device-Informationen */}
|
||||
<div className="border-t border-gray-100 pt-3">
|
||||
<p className="text-xs font-semibold text-gray-700 mb-2">Device</p>
|
||||
<div className="grid grid-cols-2 gap-1 text-xs">
|
||||
<span className="text-gray-500">Typ</span><span className="text-gray-600">{detail.device_type || '—'}</span>
|
||||
<span className="text-gray-500">Browser</span><span className="text-gray-600">{detail.browser || shortenUA(detail.user_agent)}</span>
|
||||
<span className="text-gray-500">OS</span><span className="text-gray-600">{detail.os || '—'}</span>
|
||||
<span className="text-gray-500">Auflösung</span><span className="text-gray-600">{detail.screen_resolution || '—'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Technische Details */}
|
||||
<div className="border-t border-gray-100 pt-3">
|
||||
<p className="text-xs font-semibold text-gray-700 mb-2">Technisch</p>
|
||||
<div className="space-y-1">
|
||||
<div><span className="text-gray-500 text-xs">User-Agent</span><p className="text-xs text-gray-600 font-mono break-all">{detail.user_agent || '—'}</p></div>
|
||||
{detail.ip_hash && <div><span className="text-gray-500 text-xs">IP-Hash</span><p className="text-xs text-gray-600 font-mono">{detail.ip_hash}</p></div>}
|
||||
{detail.session_id && <div><span className="text-gray-500 text-xs">Session</span><p className="text-xs text-gray-600 font-mono">{detail.session_id}</p></div>}
|
||||
{detail.banner_config_hash && <div><span className="text-gray-500 text-xs">Config-Hash</span><p className="text-xs text-gray-600 font-mono">{detail.banner_config_hash}</p></div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -112,6 +112,20 @@ export interface BannerConsentRecord {
|
||||
user_agent: string | null
|
||||
linked_email: string | null
|
||||
consent_string: string | null
|
||||
// Vendor-agnostische Felder (Migration 107)
|
||||
consent_method: string | null
|
||||
banner_version: number | null
|
||||
banner_config_hash: string | null
|
||||
geo_country: string | null
|
||||
geo_region: string | null
|
||||
consent_scope: string | null
|
||||
page_url: string | null
|
||||
referrer: string | null
|
||||
device_type: string | null
|
||||
browser: string | null
|
||||
os: string | null
|
||||
screen_resolution: string | null
|
||||
session_id: string | null
|
||||
expires_at: string | null
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
|
||||
Reference in New Issue
Block a user