Extract nav tabs, detail modal, table row, stats grid, search/filter, records table, pagination, and data-loading hook into _components/ and _hooks/. page.tsx reduced from 833 to 114 LOC. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
115 lines
3.8 KiB
TypeScript
115 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
|
|
import { History } from 'lucide-react'
|
|
|
|
import { ConsentRecord } from './_types'
|
|
import { useConsents } from './_hooks/useConsents'
|
|
import { EinwilligungenNavTabs } from './_components/EinwilligungenNavTabs'
|
|
import { StatsGrid } from './_components/StatsGrid'
|
|
import { SearchAndFilter } from './_components/SearchAndFilter'
|
|
import { RecordsTable } from './_components/RecordsTable'
|
|
import { Pagination } from './_components/Pagination'
|
|
import { ConsentDetailModal } from './_components/ConsentDetailModal'
|
|
|
|
export default function EinwilligungenPage() {
|
|
const {
|
|
records,
|
|
currentPage,
|
|
setCurrentPage,
|
|
totalRecords,
|
|
globalStats,
|
|
handleRevoke,
|
|
} = useConsents()
|
|
|
|
const [filter, setFilter] = useState<string>('all')
|
|
const [searchQuery, setSearchQuery] = useState('')
|
|
const [selectedRecord, setSelectedRecord] = useState<ConsentRecord | null>(null)
|
|
|
|
const filteredRecords = records.filter(record => {
|
|
const matchesFilter = filter === 'all' || record.consentType === filter || record.status === filter
|
|
const matchesSearch = searchQuery === '' ||
|
|
record.email.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
record.identifier.toLowerCase().includes(searchQuery.toLowerCase())
|
|
return matchesFilter && matchesSearch
|
|
})
|
|
|
|
const versionUpdates = records.reduce(
|
|
(acc, r) => acc + r.history.filter(h => h.action === 'version_update').length,
|
|
0,
|
|
)
|
|
|
|
const stepInfo = STEP_EXPLANATIONS['einwilligungen']
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Step Header */}
|
|
<StepHeader
|
|
stepId="einwilligungen"
|
|
title={stepInfo.title}
|
|
description={stepInfo.description}
|
|
explanation={stepInfo.explanation}
|
|
tips={stepInfo.tips}
|
|
>
|
|
<button className="flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors">
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
|
</svg>
|
|
Export
|
|
</button>
|
|
</StepHeader>
|
|
|
|
{/* 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>
|
|
</div>
|
|
|
|
{/* Search and Filter */}
|
|
<SearchAndFilter
|
|
searchQuery={searchQuery}
|
|
onSearchChange={setSearchQuery}
|
|
filter={filter}
|
|
onFilterChange={setFilter}
|
|
/>
|
|
|
|
{/* 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>
|
|
)
|
|
}
|