/* Admin Settings with Bento Grid Layout & Motion Animations */ 'use client'; import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Switch } from '@/components/ui/switch'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Download, Upload, Database, Loader2, KeyRound, Server, CheckCircle2, XCircle, Eye, EyeOff, Wifi, Sliders, ShieldAlert, FileArchive, Building2, MapPin, Receipt, Palette, Save } from 'lucide-react'; import { createClient } from '@/lib/supabase/client'; import { saveLicServerConfig, testLicServerConnection } from '@/lib/actions/licserver-config'; import { getBrandingSettings, saveBrandingSettings } from '@/lib/actions/branding'; import type { BrandingSettings } from '@/lib/constants/branding'; import { ColorThemePicker } from '@/components/admin/ColorThemePicker'; import { useTheme } from '@/components/ThemeProvider'; import { useRouter } from 'next/navigation'; const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.1 } } }; const cardVariants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { type: 'spring' as const, stiffness: 80, damping: 15 } } }; export default function AdminSettings() { const [demoActive, setDemoActive] = useState(true); const [exporting, setExporting] = useState(false); const [importing, setImporting] = useState(false); const [statusMsg, setStatusMsg] = useState(''); const [statusType, setStatusType] = useState<'success' | 'error' | 'info' | ''>(''); const [selectedFile, setSelectedFile] = useState(null); const [loading, setLoading] = useState(true); // LicServer Config State const [licUrl, setLicUrl] = useState(''); const [licKey, setLicKey] = useState(''); const [showKey, setShowKey] = useState(false); const [licSaving, setLicSaving] = useState(false); const [licTesting, setLicTesting] = useState(false); const [licStatus, setLicStatus] = useState<{ ok: boolean; message: string } | null>(null); const [licMsg, setLicMsg] = useState(''); const [licMsgType, setLicMsgType] = useState<'success' | 'error' | ''>(''); // Branding State const [branding, setBranding] = useState({ companyName: '', street: '', zip: '', city: '', billingStreet: '', billingZip: '', billingCity: '', sameBillingAddress: true, colorScheme: 'modern_blue', primaryColor: '#2563eb', accentColor: '#38bdf8', }); const [brandingSaving, setBrandingSaving] = useState(false); const [brandingMsg, setBrandingMsg] = useState(''); const [brandingMsgType, setBrandingMsgType] = useState<'success' | 'error' | ''>(''); const router = useRouter(); useEffect(() => { async function checkAccess() { try { const supabase = createClient(); const { data: { user } } = await supabase.auth.getUser(); if (!user) { router.push('/auth/login'); return; } const [userRes, licRes, brandRes] = await Promise.all([ supabase.from('users').select('role').eq('id', user.id).single(), supabase.from('settings').select('licserver_base_url, licserver_api_key').eq('id', 'licserver').maybeSingle(), getBrandingSettings() ]); if (userRes.error || !userRes.data || userRes.data.role === 'verwaltung') { router.push('/admin'); return; } const state = localStorage.getItem('demo_banner_disabled') !== 'true'; setDemoActive(state); if (licRes.data) { setLicUrl(licRes.data.licserver_base_url || ''); setLicKey(licRes.data.licserver_api_key || ''); } if (brandRes) { setBranding(brandRes); } } catch (err) { console.error("Fehler bei checkAccess in Einstellungen:", err); } finally { setLoading(false); } } checkAccess(); }, [router]); const toggleDemo = (checked: boolean) => { setDemoActive(checked); localStorage.setItem('demo_banner_disabled', (!checked).toString()); window.dispatchEvent(new Event('storage_demo_changed')); }; const handleExport = async () => { setExporting(true); setStatusMsg('Export läuft...'); setStatusType('info'); try { const res = await fetch('/api/admin/db-backup'); if (!res.ok) throw new Error('Export fehlgeschlagen'); const blob = await res.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `webshop_backup_${new Date().toISOString().split('T')[0]}.zip`; document.body.appendChild(a); a.click(); a.remove(); window.URL.revokeObjectURL(url); setStatusMsg('Export erfolgreich abgeschlossen.'); setStatusType('success'); } catch (err: any) { console.error(err); setStatusMsg(`Fehler beim Export: ${err.message}`); setStatusType('error'); } finally { setExporting(false); } }; const handleImport = async () => { if (!selectedFile) return; if (!confirm('ACHTUNG: Dies löscht und überschreibt alle aktuellen Datenbankinhalte in dieser Instanz. Möchten Sie wirklich fortfahren?')) { return; } setImporting(true); setStatusMsg('Import läuft...'); setStatusType('info'); try { const formData = new FormData(); formData.append('file', selectedFile); const res = await fetch('/api/admin/db-backup', { method: 'POST', body: formData, }); const data = await res.json(); if (!res.ok) throw new Error(data.error || 'Import fehlgeschlagen'); setStatusMsg('Datenbank erfolgreich importiert!'); setStatusType('success'); setSelectedFile(null); } catch (err: any) { console.error(err); setStatusMsg(`Fehler beim Import: ${err.message}`); setStatusType('error'); } finally { setImporting(false); } }; const { refreshBranding } = useTheme(); const handleSaveBranding = async () => { setBrandingSaving(true); setBrandingMsg(''); try { const res = await saveBrandingSettings(branding); if (res.success) { await refreshBranding(); setBrandingMsgType('success'); setBrandingMsg('Firmendaten & Farbschema erfolgreich gespeichert.'); } else { setBrandingMsgType('error'); setBrandingMsg(res.error || 'Fehler beim Speichern.'); } } catch (err: any) { setBrandingMsgType('error'); setBrandingMsg(err.message || 'Fehler beim Speichern.'); } finally { setBrandingSaving(false); } }; if (loading) { return
; } return (
{/* Header */}

Admin Einstellungen

Zentrales Dashboard für Firmendaten, Branding, Backup & Lizenz-Konfigurationen.

{/* Dynamic Status Notification */} {statusMsg && ( {statusMsg} )} {/* Bento Grid Container */} {/* Bento Item 1: System Modus & Banner */}

System Banner

Gelbe Demo-Warnmeldungen im gesamten Shop aktivieren oder stummschalten.

Banner aktiv
{/* Bento Item 2: Daten Export */}

DB Backup

Lädt alle Produkte, Firmen, Lizenzen & Einstellungen als ZIP-Archiv herunter.

{/* Bento Item 3: Daten Import */}
Überschreibt DB

Daten Wiederherstellung

Spielen Sie eine gesicherte ZIP-Sicherungsdatei ein. Warnung: Überschreibt aktuelle Datenbankinhalte.

setSelectedFile(e.target.files?.[0] || null)} disabled={exporting || importing} className="absolute inset-0 w-full h-full opacity-0 cursor-pointer disabled:cursor-not-allowed" /> {selectedFile ? selectedFile.name : 'ZIP-Datei auswählen'}
{/* Bento Item 4: Firmendaten & Anschriften (4 Spalten) */}

Firmendaten & Rechnungsadresse

Verwaltung der eigenen Unternehmensanschrift für Rechnungen und Dokumente

{brandingMsg && (
{brandingMsg}
)}
setBranding({ ...branding, companyName: e.target.value })} placeholder="z. B. Meine Firma GmbH" className="bg-slate-950/80 border-slate-800 text-white text-sm" />
setBranding({ ...branding, logoUrl: e.target.value })} placeholder="https://domain.de/logo.png oder /assets/logo.png" className="bg-slate-950/80 border-slate-800 text-white text-sm" />
setBranding({ ...branding, developerFooter: e.target.value })} placeholder="B2B Shop made by hephex" className="bg-slate-950/80 border-slate-800 text-white text-sm" />
{/* Anschrift */}
Hauptanschrift
setBranding({ ...branding, street: e.target.value })} placeholder="Musterstraße 12" className="bg-slate-950/80 border-slate-800 text-white text-xs" />
setBranding({ ...branding, zip: e.target.value })} placeholder="12345" className="bg-slate-950/80 border-slate-800 text-white text-xs" />
setBranding({ ...branding, city: e.target.value })} placeholder="Musterstadt" className="bg-slate-950/80 border-slate-800 text-white text-xs" />
{/* Rechnungsadresse */}
Rechnungsadresse
{!branding.sameBillingAddress ? (
setBranding({ ...branding, billingStreet: e.target.value })} placeholder="Rechnungsstraße 45" className="bg-slate-950/80 border-slate-800 text-white text-xs" />
setBranding({ ...branding, billingZip: e.target.value })} placeholder="54321" className="bg-slate-950/80 border-slate-800 text-white text-xs" />
setBranding({ ...branding, billingCity: e.target.value })} placeholder="Rechnungsstadt" className="bg-slate-950/80 border-slate-800 text-white text-xs" />
) : (
Verwendet automatisch die Hauptanschrift oben.
)}
{/* Bento Item 5: Farbschema & Design-System (4 Spalten) */}

Farbschema & Webshop Styling

Wählen Sie das primäre Farbschema für Buttons, Akzente und Highlights

setBranding({ ...branding, colorScheme: scheme, primaryColor: primary, accentColor: accent, }) } />
{/* Bento Item 6: LicServer Konfiguration */}

CASPOS Lizenzserver (LicServer)

REST API-Anbindung für automatisierte Lizenzierung

{licStatus && (
{licStatus.ok ? : } {licStatus.message}
)}
{licMsg && (
{licMsg}
)}
setLicUrl(e.target.value)} placeholder="http://192.168.178.174:9980" className="text-sm font-mono bg-slate-950/80 border-slate-800 text-white rounded-xl" />
setLicKey(e.target.value)} placeholder="Ihr X-Api-Key" className="text-sm font-mono pr-10 bg-slate-950/80 border-slate-800 text-white rounded-xl" />
); }