fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
256
admin-v2/app/(admin)/developers/sdk/configuration/page.tsx
Normal file
256
admin-v2/app/(admin)/developers/sdk/configuration/page.tsx
Normal file
@@ -0,0 +1,256 @@
|
||||
import { DevPortalLayout, CodeBlock, InfoBox, ParameterTable } from '@/components/developers/DevPortalLayout'
|
||||
|
||||
export default function SDKConfigurationPage() {
|
||||
return (
|
||||
<DevPortalLayout
|
||||
title="SDK Konfiguration"
|
||||
description="Alle Konfigurationsoptionen des AI Compliance SDK"
|
||||
>
|
||||
<h2>SDKProvider Props</h2>
|
||||
<p>
|
||||
Der SDKProvider akzeptiert folgende Konfigurationsoptionen:
|
||||
</p>
|
||||
<ParameterTable
|
||||
parameters={[
|
||||
{
|
||||
name: 'tenantId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Ihre eindeutige Tenant-ID (erhalten nach Abo-Abschluss)',
|
||||
},
|
||||
{
|
||||
name: 'apiKey',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'API Key fuer authentifizierte Anfragen (nur serverseitig verwenden)',
|
||||
},
|
||||
{
|
||||
name: 'userId',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Benutzer-ID fuer Audit-Trail und Checkpoints',
|
||||
},
|
||||
{
|
||||
name: 'enableBackendSync',
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
description: 'Aktiviert automatische Synchronisation mit dem Backend (default: false)',
|
||||
},
|
||||
{
|
||||
name: 'apiBaseUrl',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Custom API URL fuer Self-Hosted Installationen',
|
||||
},
|
||||
{
|
||||
name: 'syncInterval',
|
||||
type: 'number',
|
||||
required: false,
|
||||
description: 'Intervall fuer Auto-Sync in Millisekunden (default: 30000)',
|
||||
},
|
||||
{
|
||||
name: 'enableOfflineSupport',
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
description: 'Aktiviert localStorage Fallback bei Offline (default: true)',
|
||||
},
|
||||
{
|
||||
name: 'initialStep',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Initialer Schritt beim ersten Laden (default: "advisory-board")',
|
||||
},
|
||||
{
|
||||
name: 'onError',
|
||||
type: '(error: Error) => void',
|
||||
required: false,
|
||||
description: 'Callback fuer Fehlerbehandlung',
|
||||
},
|
||||
{
|
||||
name: 'onStateChange',
|
||||
type: '(state: SDKState) => void',
|
||||
required: false,
|
||||
description: 'Callback bei State-Aenderungen',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<h2>Vollstaendiges Beispiel</h2>
|
||||
<CodeBlock language="typescript" filename="app/layout.tsx">
|
||||
{`'use client'
|
||||
|
||||
import { SDKProvider } from '@breakpilot/compliance-sdk'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
export default function SDKLayout({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<SDKProvider
|
||||
tenantId={process.env.NEXT_PUBLIC_TENANT_ID!}
|
||||
userId="user-123"
|
||||
enableBackendSync={true}
|
||||
syncInterval={60000} // Sync alle 60 Sekunden
|
||||
enableOfflineSupport={true}
|
||||
initialStep="use-case-workshop"
|
||||
onError={(error) => {
|
||||
console.error('SDK Error:', error)
|
||||
// Optional: Sentry oder anderes Error-Tracking
|
||||
}}
|
||||
onStateChange={(state) => {
|
||||
console.log('State changed:', state.currentStep)
|
||||
// Optional: Analytics-Events
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>
|
||||
)
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Synchronisations-Strategien</h2>
|
||||
|
||||
<h3>1. Nur localStorage (Offline-Only)</h3>
|
||||
<CodeBlock language="typescript" filename="Offline-Only">
|
||||
{`<SDKProvider
|
||||
tenantId="my-tenant"
|
||||
enableBackendSync={false}
|
||||
enableOfflineSupport={true}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>`}
|
||||
</CodeBlock>
|
||||
<p>
|
||||
Ideal fuer: Lokale Entwicklung, Demos, Privacy-fokussierte Installationen.
|
||||
Daten werden nur im Browser gespeichert.
|
||||
</p>
|
||||
|
||||
<h3>2. Backend-Sync mit Fallback</h3>
|
||||
<CodeBlock language="typescript" filename="Backend + Fallback">
|
||||
{`<SDKProvider
|
||||
tenantId="my-tenant"
|
||||
enableBackendSync={true}
|
||||
enableOfflineSupport={true}
|
||||
syncInterval={30000}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>`}
|
||||
</CodeBlock>
|
||||
<p>
|
||||
Empfohlen fuer: Produktionsumgebungen. Daten werden mit dem Backend
|
||||
synchronisiert, localStorage dient als Fallback bei Netzwerkproblemen.
|
||||
</p>
|
||||
|
||||
<h3>3. Nur Backend (kein lokaler Cache)</h3>
|
||||
<CodeBlock language="typescript" filename="Backend-Only">
|
||||
{`<SDKProvider
|
||||
tenantId="my-tenant"
|
||||
enableBackendSync={true}
|
||||
enableOfflineSupport={false}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>`}
|
||||
</CodeBlock>
|
||||
<p>
|
||||
Ideal fuer: Strenge Compliance-Anforderungen, Multi-User-Szenarien.
|
||||
Daten werden nur im Backend gespeichert.
|
||||
</p>
|
||||
|
||||
<InfoBox type="warning" title="Backend-Only Modus">
|
||||
Im Backend-Only Modus ist eine aktive Internetverbindung erforderlich.
|
||||
Bei Netzwerkproblemen koennen Daten verloren gehen.
|
||||
</InfoBox>
|
||||
|
||||
<h2>API URL Konfiguration</h2>
|
||||
|
||||
<h3>Cloud-Version (Standard)</h3>
|
||||
<p>Keine zusaetzliche Konfiguration erforderlich:</p>
|
||||
<CodeBlock language="typescript" filename="Cloud">
|
||||
{`<SDKProvider tenantId="my-tenant">
|
||||
{/* Nutzt automatisch https://api.breakpilot.io/sdk/v1 */}
|
||||
</SDKProvider>`}
|
||||
</CodeBlock>
|
||||
|
||||
<h3>Self-Hosted</h3>
|
||||
<CodeBlock language="typescript" filename="Self-Hosted">
|
||||
{`<SDKProvider
|
||||
tenantId="my-tenant"
|
||||
apiBaseUrl="https://your-server.com/sdk/v1"
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>`}
|
||||
</CodeBlock>
|
||||
|
||||
<h3>Lokale Entwicklung</h3>
|
||||
<CodeBlock language="typescript" filename="Local Dev">
|
||||
{`<SDKProvider
|
||||
tenantId="dev-tenant"
|
||||
apiBaseUrl="http://localhost:8085/sdk/v1"
|
||||
enableBackendSync={true}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Feature Flags</h2>
|
||||
<p>
|
||||
Das SDK unterstuetzt Feature Flags ueber Subscription-Levels:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="Feature Checks">
|
||||
{`import { useSDK } from '@breakpilot/compliance-sdk'
|
||||
|
||||
function MyComponent() {
|
||||
const { state } = useSDK()
|
||||
|
||||
// Subscription-basierte Features
|
||||
const isEnterprise = state.subscription === 'ENTERPRISE'
|
||||
const isProfessional = ['PROFESSIONAL', 'ENTERPRISE'].includes(state.subscription)
|
||||
|
||||
// Feature-Gates
|
||||
const canExportPDF = isProfessional
|
||||
const canUseRAG = isProfessional
|
||||
const canCustomizeDSFA = isEnterprise
|
||||
const canUseAPI = isProfessional
|
||||
|
||||
return (
|
||||
<div>
|
||||
{canExportPDF && <button>PDF Export</button>}
|
||||
{canUseRAG && <RAGSearchPanel />}
|
||||
</div>
|
||||
)
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Logging & Debugging</h2>
|
||||
<p>
|
||||
Aktivieren Sie detailliertes Logging fuer die Entwicklung:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="Debug Mode">
|
||||
{`// In Ihrer .env.local
|
||||
NEXT_PUBLIC_SDK_DEBUG=true
|
||||
|
||||
// Oder programmatisch
|
||||
<SDKProvider
|
||||
tenantId="my-tenant"
|
||||
onStateChange={(state) => {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log('[SDK] State Update:', {
|
||||
phase: state.currentPhase,
|
||||
step: state.currentStep,
|
||||
useCases: state.useCases.length,
|
||||
risks: state.risks.length,
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>`}
|
||||
</CodeBlock>
|
||||
|
||||
<InfoBox type="info" title="React DevTools">
|
||||
Der SDK-State ist im React DevTools unter dem SDKProvider-Context sichtbar.
|
||||
Installieren Sie die React Developer Tools Browser-Extension fuer einfaches Debugging.
|
||||
</InfoBox>
|
||||
</DevPortalLayout>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check } from 'lucide-react'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="p-2 hover:bg-gray-700 rounded transition-colors"
|
||||
title="Kopieren"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-gray-400" />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code }: { code: string }) {
|
||||
return (
|
||||
<div className="relative bg-gray-900 rounded-lg overflow-hidden">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function MethodCard({
|
||||
name,
|
||||
signature,
|
||||
description,
|
||||
params,
|
||||
returns,
|
||||
example,
|
||||
}: {
|
||||
name: string
|
||||
signature: string
|
||||
description: string
|
||||
params?: { name: string; type: string; description: string }[]
|
||||
returns?: string
|
||||
example?: string
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-gray-200 bg-gray-50">
|
||||
<code className="text-violet-600 font-mono font-semibold">{name}</code>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="bg-gray-100 rounded-lg p-3 mb-4">
|
||||
<code className="text-sm font-mono text-gray-800">{signature}</code>
|
||||
</div>
|
||||
<p className="text-gray-600 mb-4">{description}</p>
|
||||
|
||||
{params && params.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<h4 className="font-medium text-gray-900 mb-2">Parameter</h4>
|
||||
<table className="min-w-full">
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
{params.map((param) => (
|
||||
<tr key={param.name}>
|
||||
<td className="py-2 pr-4">
|
||||
<code className="text-sm text-violet-600">{param.name}</code>
|
||||
</td>
|
||||
<td className="py-2 pr-4">
|
||||
<code className="text-sm text-gray-500">{param.type}</code>
|
||||
</td>
|
||||
<td className="py-2 text-sm text-gray-600">{param.description}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{returns && (
|
||||
<div className="mb-4">
|
||||
<h4 className="font-medium text-gray-900 mb-2">Rueckgabe</h4>
|
||||
<code className="text-sm text-gray-600">{returns}</code>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{example && (
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900 mb-2">Beispiel</h4>
|
||||
<CodeBlock code={example} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function APIReferencePage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">API Referenz</h1>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Vollstaendige Dokumentation aller Methoden und Konfigurationsoptionen des Consent SDK.
|
||||
</p>
|
||||
|
||||
{/* ConsentManager */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-6">ConsentManager</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
Die zentrale Klasse fuer das Consent Management. Verwaltet Einwilligungen, Script-Blocking und Events.
|
||||
</p>
|
||||
|
||||
{/* Constructor */}
|
||||
<div className="space-y-6">
|
||||
<MethodCard
|
||||
name="constructor"
|
||||
signature="new ConsentManager(config: ConsentConfig)"
|
||||
description="Erstellt eine neue Instanz des ConsentManagers mit der angegebenen Konfiguration."
|
||||
params={[
|
||||
{
|
||||
name: 'config',
|
||||
type: 'ConsentConfig',
|
||||
description: 'Konfigurationsobjekt fuer den Manager',
|
||||
},
|
||||
]}
|
||||
example={`const consent = new ConsentManager({
|
||||
apiEndpoint: 'https://api.example.com/consent',
|
||||
siteId: 'my-site',
|
||||
debug: true,
|
||||
});`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="init"
|
||||
signature="async init(): Promise<void>"
|
||||
description="Initialisiert das SDK, laedt bestehenden Consent und startet das Script-Blocking. Zeigt den Banner an falls noetig."
|
||||
example={`await consent.init();`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="hasConsent"
|
||||
signature="hasConsent(category: ConsentCategory): boolean"
|
||||
description="Prueft ob Einwilligung fuer eine Kategorie vorliegt."
|
||||
params={[
|
||||
{
|
||||
name: 'category',
|
||||
type: 'ConsentCategory',
|
||||
description: 'essential | functional | analytics | marketing | social',
|
||||
},
|
||||
]}
|
||||
returns="boolean - true wenn Einwilligung vorliegt"
|
||||
example={`if (consent.hasConsent('analytics')) {
|
||||
// Analytics laden
|
||||
loadGoogleAnalytics();
|
||||
}`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="setConsent"
|
||||
signature="async setConsent(input: ConsentInput): Promise<void>"
|
||||
description="Setzt die Einwilligungen und speichert sie lokal sowie auf dem Server."
|
||||
params={[
|
||||
{
|
||||
name: 'input',
|
||||
type: 'ConsentInput',
|
||||
description: 'Objekt mit Kategorien und optionalen Vendors',
|
||||
},
|
||||
]}
|
||||
example={`await consent.setConsent({
|
||||
essential: true,
|
||||
functional: true,
|
||||
analytics: true,
|
||||
marketing: false,
|
||||
social: false,
|
||||
});`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="acceptAll"
|
||||
signature="async acceptAll(): Promise<void>"
|
||||
description="Akzeptiert alle Consent-Kategorien und schliesst den Banner."
|
||||
example={`document.getElementById('accept-all').addEventListener('click', async () => {
|
||||
await consent.acceptAll();
|
||||
});`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="rejectAll"
|
||||
signature="async rejectAll(): Promise<void>"
|
||||
description="Lehnt alle nicht-essentiellen Kategorien ab und schliesst den Banner."
|
||||
example={`document.getElementById('reject-all').addEventListener('click', async () => {
|
||||
await consent.rejectAll();
|
||||
});`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="revokeAll"
|
||||
signature="async revokeAll(): Promise<void>"
|
||||
description="Widerruft alle Einwilligungen und loescht den gespeicherten Consent."
|
||||
example={`document.getElementById('revoke').addEventListener('click', async () => {
|
||||
await consent.revokeAll();
|
||||
location.reload();
|
||||
});`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="on"
|
||||
signature="on<T>(event: ConsentEventType, callback: (data: T) => void): () => void"
|
||||
description="Registriert einen Event-Listener. Gibt eine Unsubscribe-Funktion zurueck."
|
||||
params={[
|
||||
{
|
||||
name: 'event',
|
||||
type: 'ConsentEventType',
|
||||
description: 'init | change | accept_all | reject_all | banner_show | banner_hide | etc.',
|
||||
},
|
||||
{
|
||||
name: 'callback',
|
||||
type: 'function',
|
||||
description: 'Callback-Funktion die bei Event aufgerufen wird',
|
||||
},
|
||||
]}
|
||||
returns="() => void - Funktion zum Entfernen des Listeners"
|
||||
example={`const unsubscribe = consent.on('change', (state) => {
|
||||
console.log('Consent geaendert:', state);
|
||||
});
|
||||
|
||||
// Spaeter: Listener entfernen
|
||||
unsubscribe();`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="getConsent"
|
||||
signature="getConsent(): ConsentState | null"
|
||||
description="Gibt den aktuellen Consent-Status zurueck oder null falls kein Consent vorliegt."
|
||||
returns="ConsentState | null"
|
||||
example={`const state = consent.getConsent();
|
||||
if (state) {
|
||||
console.log('Consent ID:', state.consentId);
|
||||
console.log('Kategorien:', state.categories);
|
||||
}`}
|
||||
/>
|
||||
|
||||
<MethodCard
|
||||
name="exportConsent"
|
||||
signature="async exportConsent(): Promise<string>"
|
||||
description="Exportiert alle Consent-Daten als JSON-String (DSGVO Art. 20 Datenportabilitaet)."
|
||||
returns="Promise<string> - JSON-formatierter Export"
|
||||
example={`const exportData = await consent.exportConsent();
|
||||
downloadAsFile(exportData, 'consent-export.json');`}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Configuration */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-6">Konfiguration</h2>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Option
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Typ
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Default
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Beschreibung
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">apiEndpoint</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">string</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-500">erforderlich</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">URL des Consent-Backends</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">siteId</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">string</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-500">erforderlich</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Eindeutige Site-ID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">debug</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">boolean</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-500">false</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Aktiviert Debug-Logging</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">language</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">string</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-500">'de'</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Sprache fuer UI-Texte</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">consent.rememberDays</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">number</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-500">365</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Gueltigkeitsdauer in Tagen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">consent.recheckAfterDays</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">number</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-500">180</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Erneute Abfrage nach X Tagen</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Events */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-6">Events</h2>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Event
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Daten
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Beschreibung
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">init</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">ConsentState | null</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">SDK initialisiert</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">change</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">ConsentState</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Consent geaendert</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">accept_all</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">ConsentState</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle akzeptiert</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">reject_all</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">ConsentState</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle abgelehnt</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">banner_show</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">undefined</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Banner angezeigt</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">banner_hide</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">undefined</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Banner versteckt</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-violet-600">error</code>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<code className="text-sm text-gray-500">Error</code>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Fehler aufgetreten</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Types */}
|
||||
<section>
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-6">TypeScript Types</h2>
|
||||
<CodeBlock
|
||||
code={`// Consent-Kategorien
|
||||
type ConsentCategory = 'essential' | 'functional' | 'analytics' | 'marketing' | 'social';
|
||||
|
||||
// Consent-Status
|
||||
interface ConsentState {
|
||||
categories: Record<ConsentCategory, boolean>;
|
||||
vendors: Record<string, boolean>;
|
||||
timestamp: string;
|
||||
version: string;
|
||||
consentId?: string;
|
||||
expiresAt?: string;
|
||||
}
|
||||
|
||||
// Konfiguration
|
||||
interface ConsentConfig {
|
||||
apiEndpoint: string;
|
||||
siteId: string;
|
||||
debug?: boolean;
|
||||
language?: string;
|
||||
fallbackLanguage?: string;
|
||||
ui?: ConsentUIConfig;
|
||||
consent?: ConsentBehaviorConfig;
|
||||
onConsentChange?: (state: ConsentState) => void;
|
||||
onBannerShow?: () => void;
|
||||
onBannerHide?: () => void;
|
||||
onError?: (error: Error) => void;
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check } from 'lucide-react'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
return (
|
||||
<button onClick={handleCopy} className="p-2 hover:bg-gray-700 rounded transition-colors">
|
||||
{copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4 text-gray-400" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code, filename }: { code: string; filename?: string }) {
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-lg overflow-hidden">
|
||||
{filename && (
|
||||
<div className="px-4 py-2 bg-gray-800 text-gray-400 text-xs border-b border-gray-700">
|
||||
{filename}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function AngularIntegrationPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-red-500 flex items-center justify-center">
|
||||
<span className="text-white font-bold">A</span>
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Angular Integration</h1>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Service und Module fuer Angular 14+ Projekte.
|
||||
</p>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Installation</h2>
|
||||
<CodeBlock code="npm install @breakpilot/consent-sdk" />
|
||||
</section>
|
||||
|
||||
{/* Module Setup */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Module Setup</h2>
|
||||
<CodeBlock
|
||||
filename="app.module.ts"
|
||||
code={`import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ConsentModule } from '@breakpilot/consent-sdk/angular';
|
||||
import { environment } from '../environments/environment';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
ConsentModule.forRoot({
|
||||
apiEndpoint: environment.consentApi,
|
||||
siteId: 'my-site',
|
||||
debug: !environment.production,
|
||||
}),
|
||||
],
|
||||
})
|
||||
export class AppModule {}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Standalone Setup */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Standalone Setup (Angular 15+)</h2>
|
||||
<CodeBlock
|
||||
filename="app.config.ts"
|
||||
code={`import { ApplicationConfig } from '@angular/core';
|
||||
import { provideConsent } from '@breakpilot/consent-sdk/angular';
|
||||
import { environment } from '../environments/environment';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideConsent({
|
||||
apiEndpoint: environment.consentApi,
|
||||
siteId: 'my-site',
|
||||
debug: !environment.production,
|
||||
}),
|
||||
],
|
||||
};`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Service Usage */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Service Usage</h2>
|
||||
<CodeBlock
|
||||
filename="components/analytics.component.ts"
|
||||
code={`import { Component, OnInit } from '@angular/core';
|
||||
import { ConsentService } from '@breakpilot/consent-sdk/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-analytics',
|
||||
template: \`
|
||||
<div *ngIf="hasAnalyticsConsent$ | async">
|
||||
<!-- Analytics Code hier -->
|
||||
</div>
|
||||
\`,
|
||||
})
|
||||
export class AnalyticsComponent implements OnInit {
|
||||
hasAnalyticsConsent$ = this.consentService.hasConsent$('analytics');
|
||||
|
||||
constructor(private consentService: ConsentService) {}
|
||||
|
||||
async loadAnalytics() {
|
||||
if (await this.consentService.hasConsent('analytics')) {
|
||||
// Load analytics
|
||||
}
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Cookie Banner */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Cookie Banner Component</h2>
|
||||
<CodeBlock
|
||||
filename="components/cookie-banner.component.ts"
|
||||
code={`import { Component } from '@angular/core';
|
||||
import { ConsentService } from '@breakpilot/consent-sdk/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-cookie-banner',
|
||||
template: \`
|
||||
<div
|
||||
*ngIf="isBannerVisible$ | async"
|
||||
class="fixed bottom-0 inset-x-0 bg-white border-t shadow-lg p-4 z-50"
|
||||
>
|
||||
<div class="max-w-4xl mx-auto flex items-center justify-between">
|
||||
<p class="text-sm text-gray-600">
|
||||
Wir verwenden Cookies um Ihr Erlebnis zu verbessern.
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<button (click)="rejectAll()" class="px-4 py-2 border rounded">
|
||||
Ablehnen
|
||||
</button>
|
||||
<button (click)="showSettings()" class="px-4 py-2 border rounded">
|
||||
Einstellungen
|
||||
</button>
|
||||
<button (click)="acceptAll()" class="px-4 py-2 bg-blue-600 text-white rounded">
|
||||
Alle akzeptieren
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
\`,
|
||||
})
|
||||
export class CookieBannerComponent {
|
||||
isBannerVisible$ = this.consentService.isBannerVisible$;
|
||||
|
||||
constructor(private consentService: ConsentService) {}
|
||||
|
||||
async acceptAll() {
|
||||
await this.consentService.acceptAll();
|
||||
}
|
||||
|
||||
async rejectAll() {
|
||||
await this.consentService.rejectAll();
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
this.consentService.showSettings();
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Directive */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">ConsentGate Directive</h2>
|
||||
<CodeBlock
|
||||
filename="template.html"
|
||||
code={`<!-- Zeigt Element nur wenn Consent vorhanden -->
|
||||
<iframe
|
||||
*consentGate="'social'"
|
||||
src="https://www.youtube.com/embed/VIDEO_ID"
|
||||
width="560"
|
||||
height="315"
|
||||
></iframe>
|
||||
|
||||
<!-- Mit Custom Fallback -->
|
||||
<div *consentGate="'analytics'; else noConsent">
|
||||
<app-analytics-dashboard></app-analytics-dashboard>
|
||||
</div>
|
||||
<ng-template #noConsent>
|
||||
<div class="bg-gray-100 p-4 rounded-lg text-center">
|
||||
<p>Bitte stimmen Sie Statistik-Cookies zu.</p>
|
||||
<button (click)="showSettings()">Einstellungen</button>
|
||||
</div>
|
||||
</ng-template>`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Service API */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Service API</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Property/Method
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Typ
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Beschreibung
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">consent$</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Observable<ConsentState></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Observable des aktuellen Consent</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">hasConsent$()</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Observable<boolean></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Reaktive Consent-Pruefung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">hasConsent()</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Promise<boolean></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Async Consent-Pruefung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">isBannerVisible$</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Observable<boolean></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Banner-Sichtbarkeit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">acceptAll()</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Promise<void></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Akzeptiert alle</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">rejectAll()</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Promise<void></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Lehnt alle ab</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">setConsent()</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Promise<void></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Setzt spezifische Kategorien</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
name: 'React',
|
||||
href: '/developers/sdk/consent/frameworks/react',
|
||||
logo: '/logos/react.svg',
|
||||
description: 'Hooks und Provider fuer React 17+ und Next.js',
|
||||
features: ['ConsentProvider', 'useConsent Hook', 'ConsentGate Component'],
|
||||
color: 'bg-cyan-500',
|
||||
},
|
||||
{
|
||||
name: 'Vue 3',
|
||||
href: '/developers/sdk/consent/frameworks/vue',
|
||||
logo: '/logos/vue.svg',
|
||||
description: 'Composables und Plugin fuer Vue 3 und Nuxt',
|
||||
features: ['Vue Plugin', 'useConsent Composable', 'ConsentGate Component'],
|
||||
color: 'bg-emerald-500',
|
||||
},
|
||||
{
|
||||
name: 'Angular',
|
||||
href: '/developers/sdk/consent/frameworks/angular',
|
||||
logo: '/logos/angular.svg',
|
||||
description: 'Service und Module fuer Angular 14+',
|
||||
features: ['ConsentService', 'ConsentModule', 'Dependency Injection'],
|
||||
color: 'bg-red-500',
|
||||
},
|
||||
]
|
||||
|
||||
export default function FrameworksPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Framework Integration</h1>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Das Consent SDK bietet native Integrationen fuer alle gaengigen Frontend-Frameworks.
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
{frameworks.map((framework) => (
|
||||
<Link
|
||||
key={framework.name}
|
||||
href={framework.href}
|
||||
className="block bg-white rounded-xl border border-gray-200 p-6 hover:border-violet-300 hover:shadow-md transition-all group"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl ${framework.color} flex items-center justify-center shrink-0`}>
|
||||
<span className="text-white font-bold text-lg">{framework.name[0]}</span>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-xl font-semibold text-gray-900 group-hover:text-violet-600 transition-colors">
|
||||
{framework.name}
|
||||
</h2>
|
||||
<ChevronRight className="w-5 h-5 text-gray-400 group-hover:text-violet-600 transition-colors" />
|
||||
</div>
|
||||
<p className="text-gray-600 mt-1">{framework.description}</p>
|
||||
<div className="flex flex-wrap gap-2 mt-3">
|
||||
{framework.features.map((feature) => (
|
||||
<span
|
||||
key={feature}
|
||||
className="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-md"
|
||||
>
|
||||
{feature}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Vanilla JS Note */}
|
||||
<div className="mt-8 p-4 bg-blue-50 border border-blue-200 rounded-xl">
|
||||
<h3 className="font-medium text-blue-900">Vanilla JavaScript</h3>
|
||||
<p className="text-sm text-blue-700 mt-1">
|
||||
Sie koennen das SDK auch ohne Framework verwenden. Importieren Sie einfach den ConsentManager direkt
|
||||
aus dem Hauptpaket. Siehe{' '}
|
||||
<Link href="/developers/sdk/consent/installation" className="underline">
|
||||
Installation
|
||||
</Link>{' '}
|
||||
fuer Details.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check } from 'lucide-react'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
return (
|
||||
<button onClick={handleCopy} className="p-2 hover:bg-gray-700 rounded transition-colors">
|
||||
{copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4 text-gray-400" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code, filename }: { code: string; filename?: string }) {
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-lg overflow-hidden">
|
||||
{filename && (
|
||||
<div className="px-4 py-2 bg-gray-800 text-gray-400 text-xs border-b border-gray-700">
|
||||
{filename}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ReactIntegrationPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-cyan-500 flex items-center justify-center">
|
||||
<span className="text-white font-bold">R</span>
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">React Integration</h1>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Hooks und Provider fuer React 17+ und Next.js Projekte.
|
||||
</p>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Installation</h2>
|
||||
<CodeBlock code="npm install @breakpilot/consent-sdk" />
|
||||
</section>
|
||||
|
||||
{/* Provider Setup */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Provider Setup</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Umschliessen Sie Ihre App mit dem ConsentProvider:
|
||||
</p>
|
||||
<CodeBlock
|
||||
filename="app/layout.tsx"
|
||||
code={`import { ConsentProvider } from '@breakpilot/consent-sdk/react';
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="de">
|
||||
<body>
|
||||
<ConsentProvider
|
||||
config={{
|
||||
apiEndpoint: process.env.NEXT_PUBLIC_CONSENT_API!,
|
||||
siteId: 'my-site',
|
||||
debug: process.env.NODE_ENV === 'development',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ConsentProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* useConsent Hook */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">useConsent Hook</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Verwenden Sie den Hook in jeder Komponente:
|
||||
</p>
|
||||
<CodeBlock
|
||||
filename="components/Analytics.tsx"
|
||||
code={`import { useConsent } from '@breakpilot/consent-sdk/react';
|
||||
|
||||
export function Analytics() {
|
||||
const { hasConsent, acceptAll, rejectAll, showSettings } = useConsent();
|
||||
|
||||
if (!hasConsent('analytics')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Script
|
||||
src="https://www.googletagmanager.com/gtag/js?id=GA_ID"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
);
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* ConsentGate */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">ConsentGate Component</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Zeigt Inhalte nur wenn Consent vorhanden ist:
|
||||
</p>
|
||||
<CodeBlock
|
||||
filename="components/YouTubeEmbed.tsx"
|
||||
code={`import { ConsentGate } from '@breakpilot/consent-sdk/react';
|
||||
|
||||
export function YouTubeEmbed({ videoId }: { videoId: string }) {
|
||||
return (
|
||||
<ConsentGate
|
||||
category="social"
|
||||
fallback={
|
||||
<div className="bg-gray-100 p-4 rounded-lg text-center">
|
||||
<p>Video erfordert Ihre Zustimmung.</p>
|
||||
<button onClick={() => showSettings()}>
|
||||
Einstellungen oeffnen
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<iframe
|
||||
src={\`https://www.youtube.com/embed/\${videoId}\`}
|
||||
width="560"
|
||||
height="315"
|
||||
allowFullScreen
|
||||
/>
|
||||
</ConsentGate>
|
||||
);
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Custom Banner */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Custom Cookie Banner</h2>
|
||||
<CodeBlock
|
||||
filename="components/CookieBanner.tsx"
|
||||
code={`import { useConsent } from '@breakpilot/consent-sdk/react';
|
||||
|
||||
export function CookieBanner() {
|
||||
const {
|
||||
isBannerVisible,
|
||||
acceptAll,
|
||||
rejectAll,
|
||||
showSettings,
|
||||
} = useConsent();
|
||||
|
||||
if (!isBannerVisible) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-0 inset-x-0 bg-white border-t shadow-lg p-4">
|
||||
<div className="max-w-4xl mx-auto flex items-center justify-between">
|
||||
<p className="text-sm text-gray-600">
|
||||
Wir verwenden Cookies um Ihr Erlebnis zu verbessern.
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={rejectAll}
|
||||
className="px-4 py-2 text-sm border rounded"
|
||||
>
|
||||
Ablehnen
|
||||
</button>
|
||||
<button
|
||||
onClick={showSettings}
|
||||
className="px-4 py-2 text-sm border rounded"
|
||||
>
|
||||
Einstellungen
|
||||
</button>
|
||||
<button
|
||||
onClick={acceptAll}
|
||||
className="px-4 py-2 text-sm bg-blue-600 text-white rounded"
|
||||
>
|
||||
Alle akzeptieren
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Hook API */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Hook API</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Property
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Typ
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Beschreibung
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">hasConsent</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">(category) => boolean</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Prueft Consent fuer Kategorie</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">consent</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">ConsentState | null</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Aktueller Consent-Status</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">acceptAll</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">() => Promise</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Akzeptiert alle Kategorien</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">rejectAll</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">() => Promise</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Lehnt alle ab (ausser essential)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">setConsent</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">(input) => Promise</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Setzt spezifische Kategorien</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">isBannerVisible</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">boolean</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Banner sichtbar?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">showBanner</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">() => void</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Zeigt den Banner</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">showSettings</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">() => void</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Oeffnet Einstellungen</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check } from 'lucide-react'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
return (
|
||||
<button onClick={handleCopy} className="p-2 hover:bg-gray-700 rounded transition-colors">
|
||||
{copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4 text-gray-400" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code, filename }: { code: string; filename?: string }) {
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-lg overflow-hidden">
|
||||
{filename && (
|
||||
<div className="px-4 py-2 bg-gray-800 text-gray-400 text-xs border-b border-gray-700">
|
||||
{filename}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function VueIntegrationPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-emerald-500 flex items-center justify-center">
|
||||
<span className="text-white font-bold">V</span>
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Vue 3 Integration</h1>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Composables und Plugin fuer Vue 3 und Nuxt Projekte.
|
||||
</p>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Installation</h2>
|
||||
<CodeBlock code="npm install @breakpilot/consent-sdk" />
|
||||
</section>
|
||||
|
||||
{/* Plugin Setup */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Plugin Setup</h2>
|
||||
<CodeBlock
|
||||
filename="main.ts"
|
||||
code={`import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import { ConsentPlugin } from '@breakpilot/consent-sdk/vue';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(ConsentPlugin, {
|
||||
apiEndpoint: import.meta.env.VITE_CONSENT_API,
|
||||
siteId: 'my-site',
|
||||
debug: import.meta.env.DEV,
|
||||
});
|
||||
|
||||
app.mount('#app');`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Composable */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">useConsent Composable</h2>
|
||||
<CodeBlock
|
||||
filename="components/Analytics.vue"
|
||||
code={`<script setup lang="ts">
|
||||
import { useConsent } from '@breakpilot/consent-sdk/vue';
|
||||
|
||||
const { hasConsent, acceptAll, rejectAll } = useConsent();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="hasConsent('analytics')">
|
||||
<!-- Analytics Code hier -->
|
||||
</div>
|
||||
</template>`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Cookie Banner */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Cookie Banner Component</h2>
|
||||
<CodeBlock
|
||||
filename="components/CookieBanner.vue"
|
||||
code={`<script setup lang="ts">
|
||||
import { useConsent } from '@breakpilot/consent-sdk/vue';
|
||||
|
||||
const {
|
||||
isBannerVisible,
|
||||
acceptAll,
|
||||
rejectAll,
|
||||
showSettings,
|
||||
} = useConsent();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="slide">
|
||||
<div
|
||||
v-if="isBannerVisible"
|
||||
class="fixed bottom-0 inset-x-0 bg-white border-t shadow-lg p-4 z-50"
|
||||
>
|
||||
<div class="max-w-4xl mx-auto flex items-center justify-between">
|
||||
<p class="text-sm text-gray-600">
|
||||
Wir verwenden Cookies um Ihr Erlebnis zu verbessern.
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
@click="rejectAll"
|
||||
class="px-4 py-2 text-sm border rounded hover:bg-gray-50"
|
||||
>
|
||||
Ablehnen
|
||||
</button>
|
||||
<button
|
||||
@click="showSettings"
|
||||
class="px-4 py-2 text-sm border rounded hover:bg-gray-50"
|
||||
>
|
||||
Einstellungen
|
||||
</button>
|
||||
<button
|
||||
@click="acceptAll"
|
||||
class="px-4 py-2 text-sm bg-blue-600 text-white rounded hover:bg-blue-700"
|
||||
>
|
||||
Alle akzeptieren
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.slide-enter-from,
|
||||
.slide-leave-to {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
</style>`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* ConsentGate */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">ConsentGate Component</h2>
|
||||
<CodeBlock
|
||||
filename="components/YouTubeEmbed.vue"
|
||||
code={`<script setup lang="ts">
|
||||
import { ConsentGate } from '@breakpilot/consent-sdk/vue';
|
||||
|
||||
defineProps<{
|
||||
videoId: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ConsentGate category="social">
|
||||
<template #default>
|
||||
<iframe
|
||||
:src="\`https://www.youtube.com/embed/\${videoId}\`"
|
||||
width="560"
|
||||
height="315"
|
||||
allowfullscreen
|
||||
/>
|
||||
</template>
|
||||
<template #fallback>
|
||||
<div class="bg-gray-100 p-4 rounded-lg text-center">
|
||||
<p>Video erfordert Ihre Zustimmung.</p>
|
||||
<button class="mt-2 px-4 py-2 bg-blue-600 text-white rounded">
|
||||
Zustimmen
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</ConsentGate>
|
||||
</template>`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Nuxt */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Nuxt 3 Setup</h2>
|
||||
<CodeBlock
|
||||
filename="plugins/consent.client.ts"
|
||||
code={`import { ConsentPlugin } from '@breakpilot/consent-sdk/vue';
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.vueApp.use(ConsentPlugin, {
|
||||
apiEndpoint: useRuntimeConfig().public.consentApi,
|
||||
siteId: 'my-site',
|
||||
});
|
||||
});`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Composable API */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Composable API</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Property
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Typ
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Beschreibung
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">hasConsent</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">(category) => boolean</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Reaktive Consent-Pruefung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">consent</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Ref<ConsentState></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Reaktiver Consent-Status</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">isBannerVisible</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">Ref<boolean></code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Reaktive Banner-Sichtbarkeit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">acceptAll</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">() => Promise</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Akzeptiert alle</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">rejectAll</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">() => Promise</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Lehnt alle ab</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">setConsent</code></td>
|
||||
<td className="px-6 py-4"><code className="text-gray-500">(input) => Promise</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Setzt spezifische Kategorien</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check, Info, AlertTriangle } from 'lucide-react'
|
||||
|
||||
type PackageManager = 'npm' | 'yarn' | 'pnpm'
|
||||
|
||||
const installCommands: Record<PackageManager, string> = {
|
||||
npm: 'npm install @breakpilot/consent-sdk',
|
||||
yarn: 'yarn add @breakpilot/consent-sdk',
|
||||
pnpm: 'pnpm add @breakpilot/consent-sdk',
|
||||
}
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="p-2 hover:bg-gray-700 rounded transition-colors"
|
||||
title="Kopieren"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-gray-400" />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code, language = 'typescript' }: { code: string; language?: string }) {
|
||||
return (
|
||||
<div className="relative bg-gray-900 rounded-lg overflow-hidden">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function InfoBox({ type = 'info', children }: { type?: 'info' | 'warning'; children: React.ReactNode }) {
|
||||
const styles = {
|
||||
info: 'bg-blue-50 border-blue-200 text-blue-800',
|
||||
warning: 'bg-yellow-50 border-yellow-200 text-yellow-800',
|
||||
}
|
||||
const Icon = type === 'warning' ? AlertTriangle : Info
|
||||
|
||||
return (
|
||||
<div className={`p-4 border rounded-lg ${styles[type]} flex items-start gap-3`}>
|
||||
<Icon className="w-5 h-5 shrink-0 mt-0.5" />
|
||||
<div className="text-sm">{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function InstallationPage() {
|
||||
const [selectedPM, setSelectedPM] = useState<PackageManager>('npm')
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Installation</h1>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Installieren Sie das Consent SDK in Ihrem Projekt.
|
||||
</p>
|
||||
|
||||
{/* Package Installation */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">NPM Package</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-gray-200 flex gap-1 bg-gray-50">
|
||||
{(['npm', 'yarn', 'pnpm'] as const).map((pm) => (
|
||||
<button
|
||||
key={pm}
|
||||
onClick={() => setSelectedPM(pm)}
|
||||
className={`px-3 py-1.5 text-sm rounded-md transition-colors ${
|
||||
selectedPM === pm
|
||||
? 'bg-white text-gray-900 shadow-sm border border-gray-200'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{pm}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="bg-gray-900 px-4 py-4 flex items-center justify-between">
|
||||
<code className="text-green-400 font-mono text-sm">
|
||||
$ {installCommands[selectedPM]}
|
||||
</code>
|
||||
<CopyButton text={installCommands[selectedPM]} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Framework-specific */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Framework-spezifische Imports</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900 mb-2">Vanilla JavaScript</h3>
|
||||
<CodeBlock
|
||||
code={`import { ConsentManager } from '@breakpilot/consent-sdk';
|
||||
|
||||
const consent = new ConsentManager({
|
||||
apiEndpoint: 'https://api.example.com/consent',
|
||||
siteId: 'your-site-id',
|
||||
});
|
||||
|
||||
await consent.init();`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900 mb-2">React</h3>
|
||||
<CodeBlock
|
||||
code={`import { ConsentProvider, useConsent } from '@breakpilot/consent-sdk/react';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ConsentProvider
|
||||
config={{
|
||||
apiEndpoint: 'https://api.example.com/consent',
|
||||
siteId: 'your-site-id',
|
||||
}}
|
||||
>
|
||||
<YourApp />
|
||||
</ConsentProvider>
|
||||
);
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900 mb-2">Vue 3</h3>
|
||||
<CodeBlock
|
||||
code={`import { createApp } from 'vue';
|
||||
import { ConsentPlugin } from '@breakpilot/consent-sdk/vue';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(ConsentPlugin, {
|
||||
apiEndpoint: 'https://api.example.com/consent',
|
||||
siteId: 'your-site-id',
|
||||
});`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Script Blocking Setup */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Script Blocking einrichten</h2>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Um Third-Party Scripts automatisch zu blockieren, verwenden Sie das{' '}
|
||||
<code className="px-1.5 py-0.5 bg-gray-100 rounded text-sm">data-consent</code> Attribut:
|
||||
</p>
|
||||
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`<!-- Analytics Script (blockiert bis Consent) -->
|
||||
<script
|
||||
data-consent="analytics"
|
||||
data-src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
|
||||
type="text/plain"
|
||||
></script>
|
||||
|
||||
<!-- Marketing Script (blockiert bis Consent) -->
|
||||
<script data-consent="marketing" type="text/plain">
|
||||
fbq('init', 'YOUR_PIXEL_ID');
|
||||
</script>
|
||||
|
||||
<!-- Embedded iFrame (blockiert bis Consent) -->
|
||||
<iframe
|
||||
data-consent="social"
|
||||
data-src="https://www.youtube.com/embed/VIDEO_ID"
|
||||
width="560"
|
||||
height="315"
|
||||
></iframe>`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Requirements */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Systemvoraussetzungen</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Anforderung
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Minimum
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">Node.js</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 18.0.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">React (optional)</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 17.0.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">Vue (optional)</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 3.0.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">TypeScript (optional)</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 4.7.0</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Browser Support */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Browser-Unterstuetzung</h2>
|
||||
<InfoBox type="info">
|
||||
Das SDK unterstuetzt alle modernen Browser mit ES2017+ Unterstuetzung.
|
||||
Fuer aeltere Browser wird ein automatischer Fallback fuer Crypto-Funktionen bereitgestellt.
|
||||
</InfoBox>
|
||||
<div className="mt-4 bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Browser
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Minimum Version
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">Chrome</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 60</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">Firefox</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 55</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">Safari</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 11</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm text-gray-900">Edge</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">>= 79 (Chromium)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Next Steps */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Naechste Schritte</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<a
|
||||
href="/developers/sdk/consent/api-reference"
|
||||
className="p-4 bg-white rounded-xl border border-gray-200 hover:border-violet-300 hover:shadow-md transition-all"
|
||||
>
|
||||
<h3 className="font-medium text-gray-900">API Referenz</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Vollstaendige Dokumentation aller Methoden und Konfigurationsoptionen.
|
||||
</p>
|
||||
</a>
|
||||
<a
|
||||
href="/developers/sdk/consent/frameworks"
|
||||
className="p-4 bg-white rounded-xl border border-gray-200 hover:border-violet-300 hover:shadow-md transition-all"
|
||||
>
|
||||
<h3 className="font-medium text-gray-900">Framework Integration</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">
|
||||
Detaillierte Anleitungen fuer React, Vue und Angular.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check, Smartphone } from 'lucide-react'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
return (
|
||||
<button onClick={handleCopy} className="p-2 hover:bg-gray-700 rounded transition-colors">
|
||||
{copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4 text-gray-400" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code, filename }: { code: string; filename?: string }) {
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-lg overflow-hidden">
|
||||
{filename && (
|
||||
<div className="px-4 py-2 bg-gray-800 text-gray-400 text-xs border-b border-gray-700">
|
||||
{filename}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function AndroidSDKPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-green-600 flex items-center justify-center">
|
||||
<Smartphone className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Android SDK (Kotlin)</h1>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Native Kotlin SDK fuer Android API 26+ mit Jetpack Compose Unterstuetzung.
|
||||
</p>
|
||||
|
||||
{/* Requirements */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Systemvoraussetzungen</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Kotlin Version</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">1.9+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Min SDK</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">API 26 (Android 8.0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Compile SDK</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">34+</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Installation</h2>
|
||||
<CodeBlock
|
||||
filename="build.gradle.kts (Module)"
|
||||
code={`dependencies {
|
||||
implementation("com.breakpilot:consent-sdk:1.0.0")
|
||||
|
||||
// Fuer Jetpack Compose
|
||||
implementation("com.breakpilot:consent-sdk-compose:1.0.0")
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Basic Setup */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Grundlegende Einrichtung</h2>
|
||||
<CodeBlock
|
||||
filename="MyApplication.kt"
|
||||
code={`import android.app.Application
|
||||
import com.breakpilot.consent.ConsentManager
|
||||
|
||||
class MyApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// Consent Manager konfigurieren
|
||||
ConsentManager.configure(
|
||||
context = this,
|
||||
config = ConsentConfig(
|
||||
apiEndpoint = "https://api.example.com/consent",
|
||||
siteId = "my-android-app"
|
||||
)
|
||||
)
|
||||
|
||||
// Initialisieren
|
||||
lifecycleScope.launch {
|
||||
ConsentManager.initialize()
|
||||
}
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Jetpack Compose */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Jetpack Compose Integration</h2>
|
||||
<CodeBlock
|
||||
filename="MainActivity.kt"
|
||||
code={`import androidx.compose.runtime.*
|
||||
import com.breakpilot.consent.compose.*
|
||||
|
||||
@Composable
|
||||
fun MainScreen() {
|
||||
val consent = rememberConsentState()
|
||||
|
||||
Column {
|
||||
// Consent-abhaengige UI
|
||||
if (consent.hasConsent(ConsentCategory.ANALYTICS)) {
|
||||
AnalyticsView()
|
||||
}
|
||||
|
||||
// Buttons
|
||||
Button(onClick = { consent.acceptAll() }) {
|
||||
Text("Alle akzeptieren")
|
||||
}
|
||||
|
||||
Button(onClick = { consent.rejectAll() }) {
|
||||
Text("Alle ablehnen")
|
||||
}
|
||||
}
|
||||
|
||||
// Consent Banner (automatisch angezeigt wenn noetig)
|
||||
ConsentBanner()
|
||||
}
|
||||
|
||||
// ConsentGate Composable
|
||||
@Composable
|
||||
fun ProtectedContent() {
|
||||
ConsentGate(
|
||||
category = ConsentCategory.MARKETING,
|
||||
fallback = {
|
||||
Text("Marketing-Zustimmung erforderlich")
|
||||
}
|
||||
) {
|
||||
MarketingContent()
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Traditional Android */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">View-basierte Integration</h2>
|
||||
<CodeBlock
|
||||
filename="MainActivity.kt"
|
||||
code={`import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.breakpilot.consent.ConsentManager
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
// Auf Consent-Aenderungen reagieren
|
||||
lifecycleScope.launch {
|
||||
ConsentManager.consentFlow.collect { state ->
|
||||
updateUI(state)
|
||||
}
|
||||
}
|
||||
|
||||
// Banner anzeigen wenn noetig
|
||||
if (ConsentManager.needsConsent()) {
|
||||
ConsentManager.showBanner(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUI(state: ConsentState?) {
|
||||
if (state?.hasConsent(ConsentCategory.ANALYTICS) == true) {
|
||||
loadAnalytics()
|
||||
}
|
||||
}
|
||||
|
||||
fun onAcceptAllClick(view: View) {
|
||||
lifecycleScope.launch {
|
||||
ConsentManager.acceptAll()
|
||||
}
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* API Reference */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">API Referenz</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Methode</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">configure()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">SDK konfigurieren</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">initialize()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">SDK initialisieren (suspend)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">hasConsent()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Consent fuer Kategorie pruefen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">consentFlow</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Flow fuer reaktive Updates</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">acceptAll()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle akzeptieren (suspend)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">rejectAll()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle ablehnen (suspend)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">setConsent()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Kategorien setzen (suspend)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">showBanner()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Banner als DialogFragment</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check, Smartphone } from 'lucide-react'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
return (
|
||||
<button onClick={handleCopy} className="p-2 hover:bg-gray-700 rounded transition-colors">
|
||||
{copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4 text-gray-400" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code, filename }: { code: string; filename?: string }) {
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-lg overflow-hidden">
|
||||
{filename && (
|
||||
<div className="px-4 py-2 bg-gray-800 text-gray-400 text-xs border-b border-gray-700">
|
||||
{filename}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function FlutterSDKPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-500 flex items-center justify-center">
|
||||
<Smartphone className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Flutter SDK</h1>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Cross-Platform SDK fuer Flutter 3.16+ mit iOS, Android und Web Support.
|
||||
</p>
|
||||
|
||||
{/* Requirements */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Systemvoraussetzungen</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Dart Version</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">3.0+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Flutter Version</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">3.16+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Plattformen</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">iOS, Android, Web</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Installation</h2>
|
||||
<CodeBlock
|
||||
filename="pubspec.yaml"
|
||||
code={`dependencies:
|
||||
consent_sdk: ^1.0.0`}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<CodeBlock code="flutter pub get" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Basic Setup */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Grundlegende Einrichtung</h2>
|
||||
<CodeBlock
|
||||
filename="main.dart"
|
||||
code={`import 'package:flutter/material.dart';
|
||||
import 'package:consent_sdk/consent_sdk.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Consent SDK initialisieren
|
||||
await ConsentManager.instance.initialize(
|
||||
config: ConsentConfig(
|
||||
apiEndpoint: 'https://api.example.com/consent',
|
||||
siteId: 'my-flutter-app',
|
||||
),
|
||||
);
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: const ConsentWrapper(
|
||||
child: HomeScreen(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Widget Usage */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Widget Integration</h2>
|
||||
<CodeBlock
|
||||
filename="home_screen.dart"
|
||||
code={`import 'package:flutter/material.dart';
|
||||
import 'package:consent_sdk/consent_sdk.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
// StreamBuilder fuer reaktive Updates
|
||||
StreamBuilder<ConsentState?>(
|
||||
stream: ConsentManager.instance.consentStream,
|
||||
builder: (context, snapshot) {
|
||||
final consent = snapshot.data;
|
||||
|
||||
if (consent?.hasConsent(ConsentCategory.analytics) ?? false) {
|
||||
return const AnalyticsWidget();
|
||||
}
|
||||
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
|
||||
// ConsentGate Widget
|
||||
ConsentGate(
|
||||
category: ConsentCategory.marketing,
|
||||
fallback: const Center(
|
||||
child: Text('Marketing-Zustimmung erforderlich'),
|
||||
),
|
||||
child: const MarketingWidget(),
|
||||
),
|
||||
|
||||
// Buttons
|
||||
ElevatedButton(
|
||||
onPressed: () => ConsentManager.instance.acceptAll(),
|
||||
child: const Text('Alle akzeptieren'),
|
||||
),
|
||||
|
||||
ElevatedButton(
|
||||
onPressed: () => ConsentManager.instance.rejectAll(),
|
||||
child: const Text('Alle ablehnen'),
|
||||
),
|
||||
|
||||
TextButton(
|
||||
onPressed: () => ConsentManager.instance.showSettings(context),
|
||||
child: const Text('Einstellungen'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Custom Banner */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Custom Cookie Banner</h2>
|
||||
<CodeBlock
|
||||
filename="cookie_banner.dart"
|
||||
code={`import 'package:flutter/material.dart';
|
||||
import 'package:consent_sdk/consent_sdk.dart';
|
||||
|
||||
class CustomCookieBanner extends StatelessWidget {
|
||||
const CustomCookieBanner({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder<bool>(
|
||||
stream: ConsentManager.instance.isBannerVisibleStream,
|
||||
builder: (context, snapshot) {
|
||||
if (!(snapshot.data ?? false)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'Wir verwenden Cookies um Ihr Erlebnis zu verbessern.',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => ConsentManager.instance.rejectAll(),
|
||||
child: const Text('Ablehnen'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => ConsentManager.instance.showSettings(context),
|
||||
child: const Text('Einstellungen'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => ConsentManager.instance.acceptAll(),
|
||||
child: const Text('Alle akzeptieren'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* API Reference */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">API Referenz</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Methode/Property</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">initialize()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">SDK initialisieren (Future)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">hasConsent()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Consent pruefen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">consentStream</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Stream fuer Consent-Updates</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">isBannerVisibleStream</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Stream fuer Banner-Sichtbarkeit</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">acceptAll()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle akzeptieren (Future)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">rejectAll()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle ablehnen (Future)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">setConsent()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Kategorien setzen (Future)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">showSettings()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Einstellungs-Dialog oeffnen</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
283
admin-v2/app/(admin)/developers/sdk/consent/mobile/ios/page.tsx
Normal file
283
admin-v2/app/(admin)/developers/sdk/consent/mobile/ios/page.tsx
Normal file
@@ -0,0 +1,283 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Copy, Check, Apple } from 'lucide-react'
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
return (
|
||||
<button onClick={handleCopy} className="p-2 hover:bg-gray-700 rounded transition-colors">
|
||||
{copied ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4 text-gray-400" />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function CodeBlock({ code, filename }: { code: string; filename?: string }) {
|
||||
return (
|
||||
<div className="bg-gray-900 rounded-lg overflow-hidden">
|
||||
{filename && (
|
||||
<div className="px-4 py-2 bg-gray-800 text-gray-400 text-xs border-b border-gray-700">
|
||||
{filename}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="absolute top-2 right-2">
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
<pre className="p-4 text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function iOSSDKPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-gray-900 flex items-center justify-center">
|
||||
<Apple className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">iOS SDK (Swift)</h1>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Native Swift SDK fuer iOS 15+ und iPadOS mit SwiftUI-Unterstuetzung.
|
||||
</p>
|
||||
|
||||
{/* Requirements */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Systemvoraussetzungen</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Swift Version</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">5.9+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">iOS Deployment Target</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">iOS 15.0+</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Xcode Version</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">15.0+</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Installation</h2>
|
||||
<h3 className="font-medium text-gray-900 mb-2">Swift Package Manager</h3>
|
||||
<CodeBlock
|
||||
filename="Package.swift"
|
||||
code={`dependencies: [
|
||||
.package(url: "https://github.com/breakpilot/consent-sdk-ios.git", from: "1.0.0")
|
||||
]`}
|
||||
/>
|
||||
<p className="text-sm text-gray-600 mt-4">
|
||||
Oder in Xcode: File → Add Package Dependencies → URL eingeben
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Basic Usage */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Grundlegende Verwendung</h2>
|
||||
<CodeBlock
|
||||
filename="AppDelegate.swift"
|
||||
code={`import ConsentSDK
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
|
||||
// Consent Manager konfigurieren
|
||||
ConsentManager.shared.configure(
|
||||
apiEndpoint: "https://api.example.com/consent",
|
||||
siteId: "my-ios-app"
|
||||
)
|
||||
|
||||
// Initialisieren
|
||||
Task {
|
||||
await ConsentManager.shared.initialize()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* SwiftUI Integration */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">SwiftUI Integration</h2>
|
||||
<CodeBlock
|
||||
filename="ContentView.swift"
|
||||
code={`import SwiftUI
|
||||
import ConsentSDK
|
||||
|
||||
struct ContentView: View {
|
||||
@StateObject private var consent = ConsentManager.shared
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if consent.hasConsent(.analytics) {
|
||||
AnalyticsView()
|
||||
}
|
||||
|
||||
Button("Alle akzeptieren") {
|
||||
Task {
|
||||
await consent.acceptAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
.consentBanner() // Zeigt Banner automatisch
|
||||
}
|
||||
}
|
||||
|
||||
// Consent Gate Modifier
|
||||
struct ProtectedView: View {
|
||||
var body: some View {
|
||||
Text("Geschuetzter Inhalt")
|
||||
.requiresConsent(.marketing) {
|
||||
// Fallback wenn kein Consent
|
||||
Text("Marketing-Zustimmung erforderlich")
|
||||
}
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* UIKit Integration */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">UIKit Integration</h2>
|
||||
<CodeBlock
|
||||
filename="ViewController.swift"
|
||||
code={`import UIKit
|
||||
import ConsentSDK
|
||||
import Combine
|
||||
|
||||
class ViewController: UIViewController {
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Reaktiv auf Consent-Aenderungen reagieren
|
||||
ConsentManager.shared.$consent
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] state in
|
||||
self?.updateUI(consent: state)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func updateUI(consent: ConsentState?) {
|
||||
if consent?.hasConsent(.analytics) == true {
|
||||
loadAnalytics()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func acceptAllTapped(_ sender: UIButton) {
|
||||
Task {
|
||||
await ConsentManager.shared.acceptAll()
|
||||
}
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Consent Categories */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Consent-Kategorien</h2>
|
||||
<CodeBlock
|
||||
code={`// Verfuegbare Kategorien
|
||||
enum ConsentCategory {
|
||||
case essential // Immer aktiv
|
||||
case functional // Funktionale Features
|
||||
case analytics // Statistik & Analyse
|
||||
case marketing // Werbung & Tracking
|
||||
case social // Social Media Integration
|
||||
}
|
||||
|
||||
// Consent pruefen
|
||||
if ConsentManager.shared.hasConsent(.analytics) {
|
||||
// Analytics laden
|
||||
}
|
||||
|
||||
// Mehrere Kategorien pruefen
|
||||
if ConsentManager.shared.hasConsent([.analytics, .marketing]) {
|
||||
// Beide Kategorien haben Consent
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* API Reference */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">API Referenz</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Methode</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">configure()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">SDK konfigurieren</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">initialize()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">SDK initialisieren (async)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">hasConsent(_:)</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Consent fuer Kategorie pruefen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">acceptAll()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle Kategorien akzeptieren (async)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">rejectAll()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Alle ablehnen (async)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">setConsent(_:)</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Spezifische Kategorien setzen (async)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">showBanner()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Banner anzeigen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4"><code className="text-violet-600">exportConsent()</code></td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Consent-Daten exportieren (DSGVO)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
95
admin-v2/app/(admin)/developers/sdk/consent/mobile/page.tsx
Normal file
95
admin-v2/app/(admin)/developers/sdk/consent/mobile/page.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { ChevronRight, Apple, Smartphone } from 'lucide-react'
|
||||
|
||||
const platforms = [
|
||||
{
|
||||
name: 'iOS (Swift)',
|
||||
href: '/developers/sdk/consent/mobile/ios',
|
||||
description: 'Native Swift SDK fuer iOS 15+ und iPadOS',
|
||||
features: ['Swift 5.9+', 'iOS 15.0+', 'SwiftUI Support', 'Combine Integration'],
|
||||
color: 'bg-gray-900',
|
||||
icon: Apple,
|
||||
},
|
||||
{
|
||||
name: 'Android (Kotlin)',
|
||||
href: '/developers/sdk/consent/mobile/android',
|
||||
description: 'Native Kotlin SDK fuer Android API 26+',
|
||||
features: ['Kotlin 1.9+', 'API 26+', 'Jetpack Compose', 'Coroutines'],
|
||||
color: 'bg-green-600',
|
||||
icon: Smartphone,
|
||||
},
|
||||
{
|
||||
name: 'Flutter',
|
||||
href: '/developers/sdk/consent/mobile/flutter',
|
||||
description: 'Cross-Platform SDK fuer Flutter 3.16+',
|
||||
features: ['Dart 3.0+', 'Flutter 3.16+', 'iOS & Android', 'Web Support'],
|
||||
color: 'bg-blue-500',
|
||||
icon: Smartphone,
|
||||
},
|
||||
]
|
||||
|
||||
export default function MobileSDKsPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Mobile SDKs</h1>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Native SDKs fuer iOS, Android und Flutter mit vollstaendiger DSGVO-Konformitaet.
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
{platforms.map((platform) => (
|
||||
<Link
|
||||
key={platform.name}
|
||||
href={platform.href}
|
||||
className="block bg-white rounded-xl border border-gray-200 p-6 hover:border-violet-300 hover:shadow-md transition-all group"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`w-12 h-12 rounded-xl ${platform.color} flex items-center justify-center shrink-0`}>
|
||||
<platform.icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-xl font-semibold text-gray-900 group-hover:text-violet-600 transition-colors">
|
||||
{platform.name}
|
||||
</h2>
|
||||
<ChevronRight className="w-5 h-5 text-gray-400 group-hover:text-violet-600 transition-colors" />
|
||||
</div>
|
||||
<p className="text-gray-600 mt-1">{platform.description}</p>
|
||||
<div className="flex flex-wrap gap-2 mt-3">
|
||||
{platform.features.map((feature) => (
|
||||
<span
|
||||
key={feature}
|
||||
className="px-2 py-1 bg-gray-100 text-gray-600 text-xs rounded-md"
|
||||
>
|
||||
{feature}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Cross-Platform Note */}
|
||||
<div className="mt-8 p-4 bg-blue-50 border border-blue-200 rounded-xl">
|
||||
<h3 className="font-medium text-blue-900">Cross-Platform Konsistenz</h3>
|
||||
<p className="text-sm text-blue-700 mt-1">
|
||||
Alle Mobile SDKs bieten dieselbe API-Oberflaeche wie das Web SDK.
|
||||
Consent-Daten werden ueber die API synchronisiert, sodass Benutzer auf allen Geraeten
|
||||
denselben Consent-Status haben.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
262
admin-v2/app/(admin)/developers/sdk/consent/page.tsx
Normal file
262
admin-v2/app/(admin)/developers/sdk/consent/page.tsx
Normal file
@@ -0,0 +1,262 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import {
|
||||
Shield, Code, Download, Smartphone, FileCode, Lock,
|
||||
ChevronRight, Copy, Check, Zap, Globe, Layers,
|
||||
BookOpen, Terminal
|
||||
} from 'lucide-react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
|
||||
type Framework = 'npm' | 'yarn' | 'pnpm'
|
||||
|
||||
const installCommands: Record<Framework, string> = {
|
||||
npm: 'npm install @breakpilot/consent-sdk',
|
||||
yarn: 'yarn add @breakpilot/consent-sdk',
|
||||
pnpm: 'pnpm add @breakpilot/consent-sdk',
|
||||
}
|
||||
|
||||
function CopyButton({ text }: { text: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="p-2 hover:bg-gray-700 rounded transition-colors"
|
||||
title="Kopieren"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-gray-400" />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ConsentSDKHubPage() {
|
||||
const [selectedPM, setSelectedPM] = useState<Framework>('npm')
|
||||
|
||||
const quickLinks = [
|
||||
{
|
||||
title: 'Installation',
|
||||
description: 'SDK in wenigen Minuten einrichten',
|
||||
href: '/developers/sdk/consent/installation',
|
||||
icon: Download,
|
||||
color: 'bg-blue-500',
|
||||
},
|
||||
{
|
||||
title: 'API Referenz',
|
||||
description: 'Vollstaendige API-Dokumentation',
|
||||
href: '/developers/sdk/consent/api-reference',
|
||||
icon: FileCode,
|
||||
color: 'bg-purple-500',
|
||||
},
|
||||
{
|
||||
title: 'Frameworks',
|
||||
description: 'React, Vue, Angular Integration',
|
||||
href: '/developers/sdk/consent/frameworks',
|
||||
icon: Layers,
|
||||
color: 'bg-green-500',
|
||||
},
|
||||
{
|
||||
title: 'Mobile SDKs',
|
||||
description: 'iOS, Android, Flutter',
|
||||
href: '/developers/sdk/consent/mobile',
|
||||
icon: Smartphone,
|
||||
color: 'bg-orange-500',
|
||||
},
|
||||
{
|
||||
title: 'Sicherheit',
|
||||
description: 'Best Practices & Compliance',
|
||||
href: '/developers/sdk/consent/security',
|
||||
icon: Lock,
|
||||
color: 'bg-red-500',
|
||||
},
|
||||
]
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'DSGVO & TTDSG Konform',
|
||||
description: 'Vollstaendige Unterstuetzung fuer EU-Datenschutzverordnungen mit revisionssicherer Consent-Speicherung.',
|
||||
icon: Shield,
|
||||
},
|
||||
{
|
||||
title: 'Google Consent Mode v2',
|
||||
description: 'Native Integration mit automatischer Synchronisation zu Google Analytics und Ads.',
|
||||
icon: Globe,
|
||||
},
|
||||
{
|
||||
title: 'Script Blocking',
|
||||
description: 'Automatisches Blockieren von Third-Party Scripts bis zur Einwilligung.',
|
||||
icon: Code,
|
||||
},
|
||||
{
|
||||
title: 'Multi-Platform',
|
||||
description: 'Unterstuetzung fuer Web, PWA, iOS, Android und Flutter aus einer Codebasis.',
|
||||
icon: Smartphone,
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-5xl mx-auto px-8 py-12">
|
||||
{/* Header */}
|
||||
<div className="mb-12">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-violet-600 to-purple-600 flex items-center justify-center">
|
||||
<Shield className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Consent SDK</h1>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="px-2 py-0.5 bg-green-100 text-green-800 text-xs font-medium rounded-full">
|
||||
v1.0.0
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">DSGVO/TTDSG Compliant</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 max-w-3xl">
|
||||
Das Consent SDK ermoeglicht DSGVO-konforme Einwilligungsverwaltung fuer Web, PWA und Mobile Apps.
|
||||
Mit nativer Unterstuetzung fuer React, Vue, Angular und Mobile Platforms.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Quick Install */}
|
||||
<div className="mb-12 bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 className="font-semibold text-gray-900">Schnellinstallation</h2>
|
||||
<div className="flex gap-1 bg-gray-100 rounded-lg p-1">
|
||||
{(['npm', 'yarn', 'pnpm'] as const).map((pm) => (
|
||||
<button
|
||||
key={pm}
|
||||
onClick={() => setSelectedPM(pm)}
|
||||
className={`px-3 py-1 text-sm rounded-md transition-colors ${
|
||||
selectedPM === pm
|
||||
? 'bg-white text-gray-900 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
{pm}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-900 px-6 py-4 flex items-center justify-between">
|
||||
<code className="text-green-400 font-mono text-sm">
|
||||
$ {installCommands[selectedPM]}
|
||||
</code>
|
||||
<CopyButton text={installCommands[selectedPM]} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Links */}
|
||||
<div className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Dokumentation</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{quickLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="group p-4 bg-white rounded-xl border border-gray-200 hover:border-violet-300 hover:shadow-md transition-all"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={`w-10 h-10 rounded-lg ${link.color} flex items-center justify-center shrink-0`}>
|
||||
<link.icon className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-medium text-gray-900 group-hover:text-violet-600 transition-colors flex items-center gap-1">
|
||||
{link.title}
|
||||
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">{link.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Start Code */}
|
||||
<div className="mb-12 bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-gray-200">
|
||||
<h2 className="font-semibold text-gray-900">Schnellstart</h2>
|
||||
</div>
|
||||
<div className="bg-gray-900 p-6">
|
||||
<pre className="text-sm text-gray-300 font-mono overflow-x-auto">
|
||||
{`import { ConsentManager } from '@breakpilot/consent-sdk';
|
||||
|
||||
// Manager initialisieren
|
||||
const consent = new ConsentManager({
|
||||
apiEndpoint: 'https://api.example.com/consent',
|
||||
siteId: 'your-site-id',
|
||||
});
|
||||
|
||||
// SDK starten
|
||||
await consent.init();
|
||||
|
||||
// Consent pruefen
|
||||
if (consent.hasConsent('analytics')) {
|
||||
// Analytics laden
|
||||
}
|
||||
|
||||
// Events abonnieren
|
||||
consent.on('change', (state) => {
|
||||
console.log('Consent geaendert:', state);
|
||||
});`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Features */}
|
||||
<div className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Features</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{features.map((feature) => (
|
||||
<div
|
||||
key={feature.title}
|
||||
className="p-4 bg-white rounded-xl border border-gray-200"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-violet-100 flex items-center justify-center shrink-0">
|
||||
<feature.icon className="w-5 h-5 text-violet-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-gray-900">{feature.title}</h3>
|
||||
<p className="text-sm text-gray-500 mt-1">{feature.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Compliance Notice */}
|
||||
<div className="p-4 bg-blue-50 border border-blue-200 rounded-xl">
|
||||
<div className="flex items-start gap-3">
|
||||
<Shield className="w-5 h-5 text-blue-600 mt-0.5" />
|
||||
<div>
|
||||
<h3 className="font-medium text-blue-900">DSGVO & TTDSG Compliance</h3>
|
||||
<p className="text-sm text-blue-700 mt-1">
|
||||
Das Consent SDK erfuellt alle Anforderungen der DSGVO (Art. 6, 7, 13, 14, 17, 20) und des TTDSG (§ 25).
|
||||
Alle Einwilligungen werden revisionssicher gespeichert und koennen jederzeit exportiert werden.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
290
admin-v2/app/(admin)/developers/sdk/consent/security/page.tsx
Normal file
290
admin-v2/app/(admin)/developers/sdk/consent/security/page.tsx
Normal file
@@ -0,0 +1,290 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { SDKDocsSidebar } from '@/components/developers/SDKDocsSidebar'
|
||||
import { Shield, Lock, Eye, Database, Key, AlertTriangle, CheckCircle } from 'lucide-react'
|
||||
|
||||
function SecurityCard({
|
||||
title,
|
||||
description,
|
||||
icon: Icon,
|
||||
items,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
items: string[]
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-violet-100 flex items-center justify-center shrink-0">
|
||||
<Icon className="w-5 h-5 text-violet-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900">{title}</h3>
|
||||
<p className="text-sm text-gray-600 mt-1">{description}</p>
|
||||
<ul className="mt-3 space-y-1">
|
||||
{items.map((item, i) => (
|
||||
<li key={i} className="flex items-center gap-2 text-sm text-gray-600">
|
||||
<CheckCircle className="w-4 h-4 text-green-500 shrink-0" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function SecurityPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<SDKDocsSidebar />
|
||||
|
||||
<main className="flex-1 ml-64">
|
||||
<div className="max-w-4xl mx-auto px-8 py-12">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Sicherheit & Compliance</h1>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Best Practices fuer sichere Implementierung und DSGVO-konforme Nutzung des Consent SDK.
|
||||
</p>
|
||||
|
||||
{/* Security Features */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-6">Sicherheits-Features</h2>
|
||||
<div className="grid gap-4">
|
||||
<SecurityCard
|
||||
title="Datenverschluesselung"
|
||||
description="Alle Daten werden verschluesselt uebertragen und gespeichert."
|
||||
icon={Lock}
|
||||
items={[
|
||||
'TLS 1.3 fuer alle API-Kommunikation',
|
||||
'HMAC-Signatur fuer lokale Storage-Integritaet',
|
||||
'Keine Klartextspeicherung sensibler Daten',
|
||||
]}
|
||||
/>
|
||||
|
||||
<SecurityCard
|
||||
title="Datenschutzkonformes Fingerprinting"
|
||||
description="Anonymisiertes Fingerprinting ohne invasive Techniken."
|
||||
icon={Eye}
|
||||
items={[
|
||||
'Kein Canvas/WebGL/Audio Fingerprinting',
|
||||
'Nur anonymisierte Browser-Eigenschaften',
|
||||
'SHA-256 Hash der Komponenten',
|
||||
'Nicht eindeutig identifizierend',
|
||||
]}
|
||||
/>
|
||||
|
||||
<SecurityCard
|
||||
title="Sichere Speicherung"
|
||||
description="Lokale Speicherung mit Manipulationsschutz."
|
||||
icon={Database}
|
||||
items={[
|
||||
'Signierte localStorage-Eintraege',
|
||||
'Automatische Signaturverifikation',
|
||||
'HttpOnly Cookies fuer SSR',
|
||||
'SameSite=Lax gegen CSRF',
|
||||
]}
|
||||
/>
|
||||
|
||||
<SecurityCard
|
||||
title="API-Sicherheit"
|
||||
description="Sichere Backend-Kommunikation."
|
||||
icon={Key}
|
||||
items={[
|
||||
'Request-Signierung mit Timestamp',
|
||||
'Credentials-Include fuer Session-Cookies',
|
||||
'CORS-Konfiguration erforderlich',
|
||||
'Rate-Limiting auf Server-Seite',
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* DSGVO Compliance */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-6">DSGVO Compliance</h2>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
DSGVO Artikel
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
Anforderung
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
|
||||
SDK-Unterstuetzung
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Art. 6</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Rechtmaessigkeit der Verarbeitung</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
|
||||
Vollstaendig
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Art. 7</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Bedingungen fuer Einwilligung</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
|
||||
Vollstaendig
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Art. 13/14</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Informationspflichten</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
|
||||
Vollstaendig
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Art. 17</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Recht auf Loeschung</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
|
||||
Vollstaendig
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="px-6 py-4 text-sm font-medium text-gray-900">Art. 20</td>
|
||||
<td className="px-6 py-4 text-sm text-gray-600">Datenportabilitaet</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
|
||||
Vollstaendig
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* TTDSG Compliance */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-6">TTDSG Compliance</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center shrink-0">
|
||||
<Shield className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900">§ 25 TTDSG - Schutz der Privatsphaere</h3>
|
||||
<p className="text-sm text-gray-600 mt-1">
|
||||
Das SDK erfuellt alle Anforderungen des § 25 TTDSG (Telemediengesetz):
|
||||
</p>
|
||||
<ul className="mt-3 space-y-2">
|
||||
<li className="flex items-start gap-2 text-sm text-gray-600">
|
||||
<CheckCircle className="w-4 h-4 text-green-500 shrink-0 mt-0.5" />
|
||||
<span>
|
||||
<strong>Einwilligung vor Speicherung:</strong> Cookies und localStorage werden erst nach
|
||||
Einwilligung gesetzt (ausser technisch notwendige).
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-sm text-gray-600">
|
||||
<CheckCircle className="w-4 h-4 text-green-500 shrink-0 mt-0.5" />
|
||||
<span>
|
||||
<strong>Informierte Einwilligung:</strong> Klare Kategorisierung und Beschreibung
|
||||
aller Cookies und Tracker.
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2 text-sm text-gray-600">
|
||||
<CheckCircle className="w-4 h-4 text-green-500 shrink-0 mt-0.5" />
|
||||
<span>
|
||||
<strong>Widerrufsrecht:</strong> Jederzeit widerrufbare Einwilligung mit einem Klick.
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Best Practices */}
|
||||
<section className="mb-12">
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-6">Best Practices</h2>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="bg-green-50 border border-green-200 rounded-xl p-4">
|
||||
<h3 className="font-medium text-green-900 flex items-center gap-2">
|
||||
<CheckCircle className="w-5 h-5" />
|
||||
Empfohlen
|
||||
</h3>
|
||||
<ul className="mt-2 space-y-1 text-sm text-green-800">
|
||||
<li>• HTTPS fuer alle API-Aufrufe verwenden</li>
|
||||
<li>• Consent-Banner vor dem Laden von Third-Party Scripts anzeigen</li>
|
||||
<li>• Alle Kategorien klar und verstaendlich beschreiben</li>
|
||||
<li>• Ablehnen-Button gleichwertig zum Akzeptieren-Button darstellen</li>
|
||||
<li>• Consent-Aenderungen serverseitig protokollieren</li>
|
||||
<li>• Regelmaessige Ueberpruefung der Consent-Gultigkeit (recheckAfterDays)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="bg-red-50 border border-red-200 rounded-xl p-4">
|
||||
<h3 className="font-medium text-red-900 flex items-center gap-2">
|
||||
<AlertTriangle className="w-5 h-5" />
|
||||
Vermeiden
|
||||
</h3>
|
||||
<ul className="mt-2 space-y-1 text-sm text-red-800">
|
||||
<li>• Dark Patterns (versteckte Ablehnen-Buttons)</li>
|
||||
<li>• Pre-checked Consent-Optionen</li>
|
||||
<li>• Tracking vor Einwilligung</li>
|
||||
<li>• Cookie-Walls ohne echte Alternative</li>
|
||||
<li>• Unklare oder irrefuehrende Kategoriebezeichnungen</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Audit Trail */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-gray-900 mb-6">Audit Trail</h2>
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<p className="text-gray-600 mb-4">
|
||||
Das SDK speichert fuer jeden Consent-Vorgang revisionssichere Daten:
|
||||
</p>
|
||||
<div className="bg-gray-50 rounded-lg p-4 font-mono text-sm">
|
||||
<pre className="text-gray-700">
|
||||
{`{
|
||||
"consentId": "consent_abc123...",
|
||||
"timestamp": "2024-01-15T10:30:00.000Z",
|
||||
"categories": {
|
||||
"essential": true,
|
||||
"analytics": true,
|
||||
"marketing": false
|
||||
},
|
||||
"metadata": {
|
||||
"userAgent": "Mozilla/5.0...",
|
||||
"language": "de-DE",
|
||||
"platform": "web",
|
||||
"screenResolution": "1920x1080"
|
||||
},
|
||||
"signature": "sha256=...",
|
||||
"version": "1.0.0"
|
||||
}`}
|
||||
</pre>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-4">
|
||||
Diese Daten werden sowohl lokal als auch auf dem Server gespeichert und koennen
|
||||
jederzeit fuer Audits exportiert werden.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
186
admin-v2/app/(admin)/developers/sdk/installation/page.tsx
Normal file
186
admin-v2/app/(admin)/developers/sdk/installation/page.tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
import { DevPortalLayout, CodeBlock, InfoBox, ParameterTable } from '@/components/developers/DevPortalLayout'
|
||||
|
||||
export default function SDKInstallationPage() {
|
||||
return (
|
||||
<DevPortalLayout
|
||||
title="SDK Installation"
|
||||
description="Installationsanleitung fuer das AI Compliance SDK"
|
||||
>
|
||||
<h2>Voraussetzungen</h2>
|
||||
<ul>
|
||||
<li>Node.js 18 oder hoeher</li>
|
||||
<li>React 18+ / Next.js 14+</li>
|
||||
<li>TypeScript 5.0+ (empfohlen)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Installation</h2>
|
||||
<p>
|
||||
Installieren Sie das SDK ueber Ihren bevorzugten Paketmanager:
|
||||
</p>
|
||||
<CodeBlock language="bash" filename="npm">
|
||||
{`npm install @breakpilot/compliance-sdk`}
|
||||
</CodeBlock>
|
||||
<CodeBlock language="bash" filename="yarn">
|
||||
{`yarn add @breakpilot/compliance-sdk`}
|
||||
</CodeBlock>
|
||||
<CodeBlock language="bash" filename="pnpm">
|
||||
{`pnpm add @breakpilot/compliance-sdk`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Peer Dependencies</h2>
|
||||
<p>
|
||||
Das SDK hat folgende Peer Dependencies, die automatisch installiert werden sollten:
|
||||
</p>
|
||||
<CodeBlock language="json" filename="package.json">
|
||||
{`{
|
||||
"peerDependencies": {
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0"
|
||||
}
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Zusaetzliche Pakete (optional)</h2>
|
||||
<p>
|
||||
Fuer erweiterte Funktionen koennen Sie folgende Pakete installieren:
|
||||
</p>
|
||||
<ParameterTable
|
||||
parameters={[
|
||||
{
|
||||
name: 'jspdf',
|
||||
type: 'npm package',
|
||||
required: false,
|
||||
description: 'Fuer PDF-Export (wird automatisch geladen wenn verfuegbar)',
|
||||
},
|
||||
{
|
||||
name: 'jszip',
|
||||
type: 'npm package',
|
||||
required: false,
|
||||
description: 'Fuer ZIP-Export aller Dokumente',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<h2>TypeScript Konfiguration</h2>
|
||||
<p>
|
||||
Das SDK ist vollstaendig in TypeScript geschrieben. Stellen Sie sicher,
|
||||
dass Ihre tsconfig.json folgende Optionen enthaelt:
|
||||
</p>
|
||||
<CodeBlock language="json" filename="tsconfig.json">
|
||||
{`{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Next.js Integration</h2>
|
||||
<p>
|
||||
Fuer Next.js 14+ mit App Router, fuegen Sie den Provider in Ihr Root-Layout ein:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="app/layout.tsx">
|
||||
{`import { SDKProvider } from '@breakpilot/compliance-sdk'
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="de">
|
||||
<body>
|
||||
<SDKProvider
|
||||
tenantId={process.env.NEXT_PUBLIC_TENANT_ID!}
|
||||
apiKey={process.env.BREAKPILOT_API_KEY}
|
||||
enableBackendSync={true}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<InfoBox type="warning" title="Wichtig fuer Server Components">
|
||||
Der SDKProvider ist ein Client-Component. Wenn Sie Server Components
|
||||
verwenden, wrappen Sie nur die Teile der App, die das SDK benoetigen.
|
||||
</InfoBox>
|
||||
|
||||
<h2>Umgebungsvariablen</h2>
|
||||
<p>
|
||||
Erstellen Sie eine .env.local Datei mit folgenden Variablen:
|
||||
</p>
|
||||
<CodeBlock language="bash" filename=".env.local">
|
||||
{`# Pflicht
|
||||
NEXT_PUBLIC_TENANT_ID=your-tenant-id
|
||||
|
||||
# Optional (fuer Backend-Sync)
|
||||
BREAKPILOT_API_KEY=sk_live_...
|
||||
|
||||
# Optional (fuer Self-Hosted)
|
||||
NEXT_PUBLIC_SDK_API_URL=https://your-server.com/sdk/v1`}
|
||||
</CodeBlock>
|
||||
|
||||
<InfoBox type="info" title="API Key Sicherheit">
|
||||
Der API Key sollte niemals im Frontend-Code oder in NEXT_PUBLIC_ Variablen
|
||||
erscheinen. Verwenden Sie Server-Side API Routes fuer authentifizierte Anfragen.
|
||||
</InfoBox>
|
||||
|
||||
<h2>Verifizierung</h2>
|
||||
<p>
|
||||
Testen Sie die Installation mit einer einfachen Komponente:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="app/test/page.tsx">
|
||||
{`'use client'
|
||||
|
||||
import { useSDK } from '@breakpilot/compliance-sdk'
|
||||
|
||||
export default function TestPage() {
|
||||
const { state, completionPercentage } = useSDK()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>SDK Test</h1>
|
||||
<p>Fortschritt: {completionPercentage}%</p>
|
||||
<p>Aktuelle Phase: {state.currentPhase}</p>
|
||||
<p>Use Cases: {state.useCases.length}</p>
|
||||
</div>
|
||||
)
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Fehlerbehebung</h2>
|
||||
|
||||
<h3>Error: useSDK must be used within SDKProvider</h3>
|
||||
<p>
|
||||
Stellen Sie sicher, dass der SDKProvider das gesamte Layout umschliesst
|
||||
und dass Sie {'\'use client\''} in Client-Komponenten verwenden.
|
||||
</p>
|
||||
|
||||
<h3>Error: Module not found</h3>
|
||||
<p>
|
||||
Loeschen Sie node_modules und package-lock.json, dann reinstallieren:
|
||||
</p>
|
||||
<CodeBlock language="bash" filename="Terminal">
|
||||
{`rm -rf node_modules package-lock.json
|
||||
npm install`}
|
||||
</CodeBlock>
|
||||
|
||||
<h3>TypeScript Errors</h3>
|
||||
<p>
|
||||
Stellen Sie sicher, dass TypeScript 5.0+ installiert ist:
|
||||
</p>
|
||||
<CodeBlock language="bash" filename="Terminal">
|
||||
{`npm install typescript@latest`}
|
||||
</CodeBlock>
|
||||
</DevPortalLayout>
|
||||
)
|
||||
}
|
||||
281
admin-v2/app/(admin)/developers/sdk/page.tsx
Normal file
281
admin-v2/app/(admin)/developers/sdk/page.tsx
Normal file
@@ -0,0 +1,281 @@
|
||||
import Link from 'next/link'
|
||||
import { DevPortalLayout, CodeBlock, InfoBox } from '@/components/developers/DevPortalLayout'
|
||||
|
||||
export default function SDKOverviewPage() {
|
||||
return (
|
||||
<DevPortalLayout
|
||||
title="SDK Documentation"
|
||||
description="TypeScript SDK für React und Next.js Integration"
|
||||
>
|
||||
<h2>Übersicht</h2>
|
||||
<p>
|
||||
Das AI Compliance SDK ist ein TypeScript-Paket für die Integration des
|
||||
Compliance-Workflows in React und Next.js Anwendungen. Es bietet:
|
||||
</p>
|
||||
<ul>
|
||||
<li>React Context Provider für State Management</li>
|
||||
<li>Hooks für einfachen Zugriff auf Compliance-Daten</li>
|
||||
<li>Automatische Synchronisation mit dem Backend</li>
|
||||
<li>Offline-Support mit localStorage Fallback</li>
|
||||
<li>Export-Funktionen (PDF, JSON, ZIP)</li>
|
||||
</ul>
|
||||
|
||||
<h2>Kernkomponenten</h2>
|
||||
|
||||
<h3>SDKProvider</h3>
|
||||
<p>
|
||||
Der Provider wrappet Ihre App und stellt den SDK-Kontext bereit:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="app/layout.tsx">
|
||||
{`import { SDKProvider } from '@breakpilot/compliance-sdk'
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return (
|
||||
<SDKProvider
|
||||
tenantId="your-tenant"
|
||||
enableBackendSync={true}
|
||||
>
|
||||
{children}
|
||||
</SDKProvider>
|
||||
)
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h3>useSDK Hook</h3>
|
||||
<p>
|
||||
Der Haupt-Hook für den Zugriff auf alle SDK-Funktionen:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="component.tsx">
|
||||
{`import { useSDK } from '@breakpilot/compliance-sdk'
|
||||
|
||||
function MyComponent() {
|
||||
const {
|
||||
// State
|
||||
state,
|
||||
dispatch,
|
||||
|
||||
// Navigation
|
||||
currentStep,
|
||||
goToStep,
|
||||
goToNextStep,
|
||||
goToPreviousStep,
|
||||
canGoNext,
|
||||
canGoPrevious,
|
||||
|
||||
// Progress
|
||||
completionPercentage,
|
||||
phase1Completion,
|
||||
phase2Completion,
|
||||
|
||||
// Checkpoints
|
||||
validateCheckpoint,
|
||||
overrideCheckpoint,
|
||||
getCheckpointStatus,
|
||||
|
||||
// Data Updates
|
||||
updateUseCase,
|
||||
addRisk,
|
||||
updateControl,
|
||||
|
||||
// Persistence
|
||||
saveState,
|
||||
loadState,
|
||||
|
||||
// Demo Data
|
||||
seedDemoData,
|
||||
clearDemoData,
|
||||
isDemoDataLoaded,
|
||||
|
||||
// Sync
|
||||
syncState,
|
||||
forceSyncToServer,
|
||||
isOnline,
|
||||
|
||||
// Export
|
||||
exportState,
|
||||
|
||||
// Command Bar
|
||||
isCommandBarOpen,
|
||||
setCommandBarOpen,
|
||||
} = useSDK()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Progress: {completionPercentage}%</h1>
|
||||
<button onClick={() => goToStep('risks')}>
|
||||
Zur Risikoanalyse
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Types</h2>
|
||||
<p>
|
||||
Das SDK exportiert alle TypeScript-Types für volle Typsicherheit:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="types.ts">
|
||||
{`import type {
|
||||
// Core Types
|
||||
SDKState,
|
||||
SDKAction,
|
||||
SDKStep,
|
||||
SDKPhase,
|
||||
|
||||
// Use Cases
|
||||
UseCaseAssessment,
|
||||
AssessmentResult,
|
||||
|
||||
// Risk Management
|
||||
Risk,
|
||||
RiskSeverity,
|
||||
RiskMitigation,
|
||||
|
||||
// Controls & Evidence
|
||||
Control,
|
||||
Evidence,
|
||||
Requirement,
|
||||
|
||||
// Checkpoints
|
||||
Checkpoint,
|
||||
CheckpointStatus,
|
||||
ValidationError,
|
||||
|
||||
// DSFA
|
||||
DSFA,
|
||||
DSFASection,
|
||||
DSFAApproval,
|
||||
|
||||
// TOMs & VVT
|
||||
TOM,
|
||||
ProcessingActivity,
|
||||
RetentionPolicy,
|
||||
|
||||
// AI Act
|
||||
AIActResult,
|
||||
AIActRiskCategory,
|
||||
} from '@breakpilot/compliance-sdk'`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Utility Functions</h2>
|
||||
<p>
|
||||
Hilfreiche Funktionen für die Arbeit mit dem SDK:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="utils.ts">
|
||||
{`import {
|
||||
// Step Navigation
|
||||
getStepById,
|
||||
getStepByUrl,
|
||||
getNextStep,
|
||||
getPreviousStep,
|
||||
getStepsForPhase,
|
||||
|
||||
// Risk Calculation
|
||||
calculateRiskScore,
|
||||
getRiskSeverityFromScore,
|
||||
calculateResidualRisk,
|
||||
|
||||
// Progress
|
||||
getCompletionPercentage,
|
||||
getPhaseCompletionPercentage,
|
||||
} from '@breakpilot/compliance-sdk'
|
||||
|
||||
// Beispiel: Risk Score berechnen
|
||||
const inherentRisk = calculateRiskScore(4, 5) // likelihood * impact = 20
|
||||
const severity = getRiskSeverityFromScore(20) // 'CRITICAL'`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>API Client</h2>
|
||||
<p>
|
||||
Für direkten API-Zugriff ohne React Context:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="api.ts">
|
||||
{`import {
|
||||
getSDKApiClient,
|
||||
SDKApiClient,
|
||||
} from '@breakpilot/compliance-sdk'
|
||||
|
||||
const client = getSDKApiClient('your-tenant-id')
|
||||
|
||||
// State laden
|
||||
const state = await client.getState()
|
||||
|
||||
// State speichern
|
||||
await client.saveState(updatedState)
|
||||
|
||||
// Checkpoint validieren
|
||||
const result = await client.validateCheckpoint('CP-UC', state)
|
||||
|
||||
// Export
|
||||
const blob = await client.exportState('pdf')`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>RAG & LLM Client</h2>
|
||||
<p>
|
||||
Zugriff auf die RAG-Suche und Dokumentengenerierung:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="rag.ts">
|
||||
{`import {
|
||||
getSDKBackendClient,
|
||||
isLegalQuery,
|
||||
} from '@breakpilot/compliance-sdk'
|
||||
|
||||
const client = getSDKBackendClient()
|
||||
|
||||
// RAG-Suche
|
||||
const results = await client.search('DSGVO Art. 5', 5)
|
||||
console.log(results) // SearchResult[]
|
||||
|
||||
// Dokumentengenerierung
|
||||
const dsfa = await client.generateDSFA(context)
|
||||
const toms = await client.generateTOM(context)
|
||||
const vvt = await client.generateVVT(context)
|
||||
|
||||
// Prüfen ob eine Query rechtliche Inhalte betrifft
|
||||
if (isLegalQuery('Einwilligung DSGVO')) {
|
||||
// RAG-Suche durchführen
|
||||
}`}
|
||||
</CodeBlock>
|
||||
|
||||
<h2>Export</h2>
|
||||
<p>
|
||||
Exportieren Sie Compliance-Daten in verschiedenen Formaten:
|
||||
</p>
|
||||
<CodeBlock language="typescript" filename="export.ts">
|
||||
{`import { exportToPDF, exportToZIP, downloadExport } from '@breakpilot/compliance-sdk'
|
||||
|
||||
// PDF Export
|
||||
const pdfBlob = await exportToPDF(state)
|
||||
downloadExport(pdfBlob, 'compliance-report.pdf')
|
||||
|
||||
// ZIP Export (alle Dokumente)
|
||||
const zipBlob = await exportToZIP(state)
|
||||
downloadExport(zipBlob, 'compliance-export.zip')
|
||||
|
||||
// Über den Hook
|
||||
const { exportState } = useSDK()
|
||||
const blob = await exportState('pdf') // 'json' | 'pdf' | 'zip'`}
|
||||
</CodeBlock>
|
||||
|
||||
<InfoBox type="success" title="Weitere Dokumentation">
|
||||
<ul className="list-disc list-inside space-y-1">
|
||||
<li>
|
||||
<Link href="/developers/sdk/installation" className="text-blue-600 hover:underline">
|
||||
Installation Guide
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/developers/sdk/configuration" className="text-blue-600 hover:underline">
|
||||
Konfigurationsoptionen
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/developers/guides/phase1" className="text-blue-600 hover:underline">
|
||||
Phase 1 Workflow Guide
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</InfoBox>
|
||||
</DevPortalLayout>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user