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:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user