36d9f929c6
F9: Verarbeiter-Tabelle - VendorTable.tsx: 82+ vendors grouped by category with expandable cookie details - EmbeddableVendorHTML.tsx: Copy-pasteable HTML table for privacy policy - Tab system: Konfiguration | Verarbeiter | Einbettung F3: Multi-Site UI - SiteSelector.tsx: Domain dropdown with "Neue Seite anlegen" dialog - useCookieBanner hook extended with sites management - Config/vendors reload per selected site Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
77 lines
3.1 KiB
TypeScript
77 lines
3.1 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
|
|
interface Site {
|
|
id: string
|
|
site_id: string
|
|
site_name: string
|
|
site_url: string
|
|
is_active: boolean
|
|
}
|
|
|
|
interface SiteSelectorProps {
|
|
sites: Site[]
|
|
activeSiteId: string | null
|
|
onSiteChange: (siteId: string) => void
|
|
onCreateSite: (data: { site_id: string; site_name: string; site_url: string }) => Promise<void>
|
|
}
|
|
|
|
export function SiteSelector({ sites, activeSiteId, onSiteChange, onCreateSite }: SiteSelectorProps) {
|
|
const [showCreate, setShowCreate] = useState(false)
|
|
const [newSite, setNewSite] = useState({ site_id: '', site_name: '', site_url: '' })
|
|
const [creating, setCreating] = useState(false)
|
|
|
|
const handleCreate = async () => {
|
|
if (!newSite.site_id || !newSite.site_name) return
|
|
setCreating(true)
|
|
try {
|
|
await onCreateSite(newSite)
|
|
setNewSite({ site_id: '', site_name: '', site_url: '' })
|
|
setShowCreate(false)
|
|
} finally {
|
|
setCreating(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="bg-white rounded-xl border border-gray-200 p-4">
|
|
<div className="flex items-center gap-4">
|
|
<div className="flex-1">
|
|
<label className="block text-xs font-medium text-gray-500 mb-1">Website / Domain</label>
|
|
<select value={activeSiteId || ''} onChange={e => onSiteChange(e.target.value)}
|
|
className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:ring-1 focus:ring-purple-500 bg-white">
|
|
{sites.length === 0 && <option value="">Keine Sites konfiguriert</option>}
|
|
{sites.map(s => (
|
|
<option key={s.site_id} value={s.site_id}>
|
|
{s.site_name} ({s.site_url || s.site_id})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<button onClick={() => setShowCreate(!showCreate)}
|
|
className="mt-5 px-3 py-2 text-sm bg-purple-50 text-purple-600 border border-purple-200 rounded-lg hover:bg-purple-100">
|
|
+ Neue Seite
|
|
</button>
|
|
</div>
|
|
|
|
{showCreate && (
|
|
<div className="mt-4 pt-4 border-t border-gray-100 grid grid-cols-3 gap-3">
|
|
<input value={newSite.site_id} onChange={e => setNewSite({ ...newSite, site_id: e.target.value })}
|
|
placeholder="Site-ID (z.B. main-website)" className="px-3 py-2 text-sm border border-gray-200 rounded-lg" />
|
|
<input value={newSite.site_name} onChange={e => setNewSite({ ...newSite, site_name: e.target.value })}
|
|
placeholder="Name (z.B. Hauptwebsite)" className="px-3 py-2 text-sm border border-gray-200 rounded-lg" />
|
|
<div className="flex gap-2">
|
|
<input value={newSite.site_url} onChange={e => setNewSite({ ...newSite, site_url: e.target.value })}
|
|
placeholder="URL (z.B. https://example.com)" className="flex-1 px-3 py-2 text-sm border border-gray-200 rounded-lg" />
|
|
<button onClick={handleCreate} disabled={creating || !newSite.site_id}
|
|
className="px-3 py-2 text-sm bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50">
|
|
{creating ? '...' : 'Anlegen'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|