Each page.tsx was 750-780 LOC. Extracted React components to _components/ and custom hooks to _hooks/ next to each page.tsx. All three pages are now under 215 LOC (well within the 500 LOC hard cap). Zero behavior changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
142 lines
5.5 KiB
TypeScript
142 lines
5.5 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { CookieBannerConfig, SupportedLanguage } from '@/lib/sdk/einwilligungen/types'
|
|
|
|
interface BannerPreviewProps {
|
|
config: CookieBannerConfig | null
|
|
language: SupportedLanguage
|
|
device: 'desktop' | 'tablet' | 'mobile'
|
|
}
|
|
|
|
export function BannerPreview({ config, language, device }: BannerPreviewProps) {
|
|
const [showDetails, setShowDetails] = useState(false)
|
|
|
|
if (!config) {
|
|
return (
|
|
<div className="flex items-center justify-center h-64 bg-slate-100 rounded-xl">
|
|
<p className="text-slate-400">Konfiguration wird geladen...</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const isDark = config.styling.theme === 'DARK'
|
|
const bgColor = isDark ? '#1e293b' : config.styling.backgroundColor || '#ffffff'
|
|
const textColor = isDark ? '#f1f5f9' : config.styling.textColor || '#1e293b'
|
|
|
|
const deviceWidths = { desktop: '100%', tablet: '768px', mobile: '375px' }
|
|
|
|
return (
|
|
<div
|
|
className="border rounded-xl overflow-hidden"
|
|
style={{ maxWidth: deviceWidths[device], margin: '0 auto' }}
|
|
>
|
|
<div className="bg-slate-100 h-8 flex items-center px-3 gap-2">
|
|
<div className="w-3 h-3 rounded-full bg-red-400" />
|
|
<div className="w-3 h-3 rounded-full bg-yellow-400" />
|
|
<div className="w-3 h-3 rounded-full bg-green-400" />
|
|
<div className="flex-1 bg-white rounded h-5 mx-4" />
|
|
</div>
|
|
|
|
<div className="relative bg-slate-50 min-h-[400px]">
|
|
<div className="p-6 space-y-4">
|
|
<div className="h-4 bg-slate-200 rounded w-3/4" />
|
|
<div className="h-4 bg-slate-200 rounded w-1/2" />
|
|
<div className="h-32 bg-slate-200 rounded" />
|
|
<div className="h-4 bg-slate-200 rounded w-2/3" />
|
|
<div className="h-4 bg-slate-200 rounded w-1/2" />
|
|
</div>
|
|
|
|
<div className="absolute inset-0 bg-black/40" />
|
|
|
|
<div
|
|
className={`absolute ${
|
|
config.styling.position === 'TOP'
|
|
? 'top-0 left-0 right-0'
|
|
: config.styling.position === 'CENTER'
|
|
? 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'
|
|
: 'bottom-0 left-0 right-0'
|
|
}`}
|
|
style={{
|
|
maxWidth: config.styling.maxWidth,
|
|
margin: config.styling.position === 'CENTER' ? '0' : '16px auto',
|
|
}}
|
|
>
|
|
<div
|
|
className="shadow-xl"
|
|
style={{
|
|
background: bgColor,
|
|
color: textColor,
|
|
borderRadius: config.styling.borderRadius,
|
|
padding: '20px',
|
|
}}
|
|
>
|
|
<h3 className="font-semibold text-lg mb-2">{config.texts.title[language]}</h3>
|
|
<p className="text-sm opacity-80 mb-4">{config.texts.description[language]}</p>
|
|
|
|
<div className="flex flex-wrap gap-2 mb-3">
|
|
<button
|
|
style={{ background: config.styling.secondaryColor }}
|
|
className="flex-1 min-w-[100px] px-4 py-2 rounded-lg text-sm font-medium"
|
|
>
|
|
{config.texts.rejectAll[language]}
|
|
</button>
|
|
<button
|
|
onClick={() => setShowDetails(!showDetails)}
|
|
style={{ background: config.styling.secondaryColor }}
|
|
className="flex-1 min-w-[100px] px-4 py-2 rounded-lg text-sm font-medium"
|
|
>
|
|
{config.texts.customize[language]}
|
|
</button>
|
|
<button
|
|
style={{ background: config.styling.primaryColor, color: 'white' }}
|
|
className="flex-1 min-w-[100px] px-4 py-2 rounded-lg text-sm font-medium"
|
|
>
|
|
{config.texts.acceptAll[language]}
|
|
</button>
|
|
</div>
|
|
|
|
{showDetails && (
|
|
<div className="border-t pt-3 mt-3 space-y-2" style={{ borderColor: 'rgba(128,128,128,0.2)' }}>
|
|
{config.categories.map((cat) => (
|
|
<div key={cat.id} className="flex items-center justify-between py-2">
|
|
<div>
|
|
<div className="font-medium text-sm">{cat.name[language]}</div>
|
|
<div className="text-xs opacity-60">{cat.description[language]}</div>
|
|
</div>
|
|
<div
|
|
className={`w-10 h-6 rounded-full relative ${
|
|
cat.isRequired || cat.defaultEnabled ? '' : 'opacity-50'
|
|
}`}
|
|
style={{
|
|
background: cat.isRequired || cat.defaultEnabled
|
|
? config.styling.primaryColor
|
|
: 'rgba(128,128,128,0.3)',
|
|
}}
|
|
>
|
|
<div
|
|
className="absolute top-1 w-4 h-4 bg-white rounded-full transition-all"
|
|
style={{ left: cat.isRequired || cat.defaultEnabled ? '20px' : '4px' }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
<button
|
|
style={{ background: config.styling.primaryColor, color: 'white' }}
|
|
className="w-full px-4 py-2 rounded-lg text-sm font-medium mt-2"
|
|
>
|
|
{config.texts.save[language]}
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
<a href="#" className="block text-xs mt-3" style={{ color: config.styling.primaryColor }}>
|
|
{config.texts.privacyPolicyLink[language]}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|