feat(cmp): unified consent view — Website-Besucher + Login-Nutzer tabs
Merges two separate consent views into one unified page at /sdk/einwilligungen: - Tab "Website-Besucher": device-based banner consents with site selector - Tab "Login-Nutzer": user-based DSGVO consents (existing, unchanged) Backend: - New endpoint GET /admin/consents for paginated banner consent records - Fix: categories JSON string parsing (was iterating chars instead of array) CMP Dashboard: - Dynamic site selector replacing hardcoded "preview-test-site" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState } from 'react'
|
||||
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
|
||||
import { History } from 'lucide-react'
|
||||
import { History, Globe, User } from 'lucide-react'
|
||||
|
||||
import { ConsentRecord } from './_types'
|
||||
import { useConsents } from './_hooks/useConsents'
|
||||
@@ -12,8 +12,13 @@ import { SearchAndFilter } from './_components/SearchAndFilter'
|
||||
import { RecordsTable } from './_components/RecordsTable'
|
||||
import { Pagination } from './_components/Pagination'
|
||||
import { ConsentDetailModal } from './_components/ConsentDetailModal'
|
||||
import BannerConsentsTab from './_components/BannerConsentsTab'
|
||||
|
||||
type ConsentTab = 'visitors' | 'users'
|
||||
|
||||
export default function EinwilligungenPage() {
|
||||
const [activeTab, setActiveTab] = useState<ConsentTab>('visitors')
|
||||
|
||||
const {
|
||||
records,
|
||||
currentPage,
|
||||
@@ -63,51 +68,84 @@ export default function EinwilligungenPage() {
|
||||
{/* Navigation Tabs */}
|
||||
<EinwilligungenNavTabs />
|
||||
|
||||
{/* Stats */}
|
||||
<StatsGrid
|
||||
total={globalStats.total}
|
||||
active={globalStats.active}
|
||||
revoked={globalStats.revoked}
|
||||
versionUpdates={versionUpdates}
|
||||
/>
|
||||
|
||||
{/* Info Banner */}
|
||||
<div className="bg-gradient-to-r from-purple-50 to-indigo-50 rounded-xl border border-purple-200 p-4 flex items-start gap-3">
|
||||
<History className="w-5 h-5 text-purple-600 mt-0.5" />
|
||||
<div>
|
||||
<div className="font-medium text-purple-900">Consent-Historie aktiviert</div>
|
||||
<div className="text-sm text-purple-700">
|
||||
Alle Änderungen an Einwilligungen werden protokolliert, inkl. Zustimmungen zu neuen Versionen von AGB, DSI und anderen Dokumenten.
|
||||
Klicken Sie auf "Details" um die vollständige Historie eines Nutzers einzusehen.
|
||||
</div>
|
||||
</div>
|
||||
{/* Consent Type Tabs: Website-Besucher / Login-Nutzer */}
|
||||
<div className="flex gap-1 p-1 bg-gray-100 rounded-xl w-fit">
|
||||
<button
|
||||
onClick={() => setActiveTab('visitors')}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
activeTab === 'visitors'
|
||||
? 'bg-white text-purple-700 shadow-sm'
|
||||
: 'text-gray-500 hover:text-gray-700'
|
||||
}`}
|
||||
>
|
||||
<Globe className="w-4 h-4" />
|
||||
Website-Besucher
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('users')}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
activeTab === 'users'
|
||||
? 'bg-white text-purple-700 shadow-sm'
|
||||
: 'text-gray-500 hover:text-gray-700'
|
||||
}`}
|
||||
>
|
||||
<User className="w-4 h-4" />
|
||||
Login-Nutzer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Search and Filter */}
|
||||
<SearchAndFilter
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={setSearchQuery}
|
||||
filter={filter}
|
||||
onFilterChange={setFilter}
|
||||
/>
|
||||
{/* Tab Content */}
|
||||
{activeTab === 'visitors' ? (
|
||||
<BannerConsentsTab />
|
||||
) : (
|
||||
<>
|
||||
{/* Stats */}
|
||||
<StatsGrid
|
||||
total={globalStats.total}
|
||||
active={globalStats.active}
|
||||
revoked={globalStats.revoked}
|
||||
versionUpdates={versionUpdates}
|
||||
/>
|
||||
|
||||
{/* Records Table */}
|
||||
<RecordsTable records={filteredRecords} onShowDetails={setSelectedRecord} />
|
||||
{/* Info Banner */}
|
||||
<div className="bg-gradient-to-r from-purple-50 to-indigo-50 rounded-xl border border-purple-200 p-4 flex items-start gap-3">
|
||||
<History className="w-5 h-5 text-purple-600 mt-0.5" />
|
||||
<div>
|
||||
<div className="font-medium text-purple-900">Consent-Historie aktiviert</div>
|
||||
<div className="text-sm text-purple-700">
|
||||
Alle Änderungen an Einwilligungen werden protokolliert, inkl. Zustimmungen zu neuen Versionen von AGB, DSI und anderen Dokumenten.
|
||||
Klicken Sie auf "Details" um die vollständige Historie eines Nutzers einzusehen.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalRecords={totalRecords}
|
||||
onPageChange={setCurrentPage}
|
||||
/>
|
||||
{/* Search and Filter */}
|
||||
<SearchAndFilter
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={setSearchQuery}
|
||||
filter={filter}
|
||||
onFilterChange={setFilter}
|
||||
/>
|
||||
|
||||
{/* Detail Modal */}
|
||||
{selectedRecord && (
|
||||
<ConsentDetailModal
|
||||
record={selectedRecord}
|
||||
onClose={() => setSelectedRecord(null)}
|
||||
onRevoke={handleRevoke}
|
||||
/>
|
||||
{/* Records Table */}
|
||||
<RecordsTable records={filteredRecords} onShowDetails={setSelectedRecord} />
|
||||
|
||||
{/* Pagination */}
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalRecords={totalRecords}
|
||||
onPageChange={setCurrentPage}
|
||||
/>
|
||||
|
||||
{/* Detail Modal */}
|
||||
{selectedRecord && (
|
||||
<ConsentDetailModal
|
||||
record={selectedRecord}
|
||||
onClose={() => setSelectedRecord(null)}
|
||||
onRevoke={handleRevoke}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user