Files
webshop/shop/app/my-customers/[id]/page.tsx

225 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import Link from 'next/link'
import { ArrowLeft, Save, AlertTriangle, Loader2, Building2, ShoppingBag, Trash2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { getEndCustomer, updateEndCustomer, anonymizeEndCustomer } from '@/lib/actions/end-customers'
import type { EndCustomer } from '@/lib/types'
export default function CustomerDetailPage({ params }: { params: Promise<{ id: string }> }) {
const router = useRouter()
const [customer, setCustomer] = useState<EndCustomer | null>(null)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [anonymizing, setAnonymizing] = useState(false)
const [showGdprConfirm, setShowGdprConfirm] = useState(false)
const [form, setForm] = useState({
company_name: '', vat_id: '', first_name: '', last_name: '', street: '', zip: '', city: '', email: '',
bank_iban: '', bank_bic: '', bank_name: '', bank_owner: '',
})
useEffect(() => {
params.then(({ id }) => {
getEndCustomer(id).then(c => {
if (!c) { router.push('/my-customers'); return }
setCustomer(c)
setForm({
company_name: c.company_name ?? '',
vat_id: c.vat_id ?? '',
first_name: c.first_name ?? '',
last_name: c.last_name ?? '',
street: c.street ?? '',
zip: c.zip ?? '',
city: c.city ?? '',
email: c.email ?? '',
bank_iban: c.bank_iban ?? '',
bank_bic: c.bank_bic ?? '',
bank_name: c.bank_name ?? '',
bank_owner: c.bank_owner ?? '',
})
setLoading(false)
})
})
}, [params, router])
const handleSave = async () => {
if (!customer || !form.company_name.trim()) return
setSaving(true)
try {
await updateEndCustomer(customer.id, form)
setCustomer(prev => prev ? { ...prev, ...form } : prev)
alert('Kundendaten gespeichert.')
} catch (e) {
alert('Fehler beim Speichern.')
} finally {
setSaving(false)
}
}
const handleAnonymize = async () => {
if (!customer) return
setAnonymizing(true)
try {
await anonymizeEndCustomer(customer.id)
router.push('/my-customers')
} catch (e) {
alert('Fehler bei der Anonymisierung.')
setAnonymizing(false)
}
}
if (loading) {
return (
<div className="min-h-screen bg-[#020617] flex items-center justify-center">
<Loader2 className="w-8 h-8 animate-spin text-primary" />
</div>
)
}
if (!customer) return null
return (
<div className="min-h-screen bg-[#020617] text-white px-4 py-12">
<div className="max-w-3xl mx-auto space-y-6">
{/* Header */}
<div className="flex items-center gap-4">
<Link href="/my-customers">
<Button variant="ghost" size="sm" className="text-slate-400 hover:text-white">
<ArrowLeft className="w-4 h-4 mr-2" /> Meine Kunden
</Button>
</Link>
<div>
<h1 className="text-2xl font-extrabold tracking-tight flex items-center gap-2">
<Building2 className="w-6 h-6 text-primary" />
{customer.is_anonymized ? '[Anonymisiert]' : customer.company_name}
</h1>
<p className="text-slate-400 text-xs mt-0.5">
Kunden-ID: <span className="font-mono">{customer.id.slice(0, 8)}</span>
</p>
</div>
{customer.is_anonymized && (
<Badge className="bg-red-500/10 text-red-400 border-red-500/20 gap-1 ml-auto">
<AlertTriangle className="w-3 h-3" /> Anonymisiert
</Badge>
)}
</div>
{/* Bearbeitungsformular */}
{!customer.is_anonymized && (
<Card className="glass-dark border-white/10">
<CardHeader>
<CardTitle className="text-white">Stammdaten bearbeiten</CardTitle>
<CardDescription className="text-slate-400">
Änderungen wirken sich nur auf zukünftige Bestellungen aus. Bestehende Snapshots bleiben unverändert.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid md:grid-cols-2 gap-4">
{([
{ label: 'Firmenname *', key: 'company_name', span: true },
{ label: 'USt-IdNr.', key: 'vat_id', span: false },
{ label: 'Vorname', key: 'first_name', span: false },
{ label: 'Nachname', key: 'last_name', span: false },
{ label: 'E-Mail', key: 'email', span: true },
{ label: 'Straße & Hausnummer', key: 'street', span: true },
{ label: 'PLZ', key: 'zip', span: false },
{ label: 'Ort', key: 'city', span: false },
{ label: 'Kontoinhaber', key: 'bank_owner', span: false },
{ label: 'IBAN', key: 'bank_iban', span: false },
{ label: 'BIC', key: 'bank_bic', span: false },
{ label: 'Bankname', key: 'bank_name', span: false },
] as const).map(({ label, key, span }) => (
<div key={key} className={`space-y-1.5 ${span ? 'md:col-span-2' : ''}`}>
<Label className="text-slate-300 text-sm">{label}</Label>
<Input
value={form[key]}
onChange={e => setForm(prev => ({ ...prev, [key]: e.target.value }))}
className="bg-white/5 border-white/10 text-white"
/>
</div>
))}
</div>
<div className="flex gap-3 pt-2">
<Button onClick={handleSave} disabled={saving || !form.company_name.trim()} className="gap-2">
{saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
Speichern
</Button>
<Link href={`/my-orders`}>
<Button variant="outline" className="border-white/10 hover:bg-white/5 gap-2">
<ShoppingBag className="w-4 h-4" /> Bestellungen ansehen
</Button>
</Link>
</div>
</CardContent>
</Card>
)}
{/* DSGVO Anonymisierung */}
{!customer.is_anonymized && (
<Card className="glass-dark border-red-500/20 bg-red-500/5">
<CardHeader>
<CardTitle className="text-red-400 flex items-center gap-2">
<Trash2 className="w-5 h-5" />
Recht auf Vergessenwerden (DSGVO)
</CardTitle>
<CardDescription className="text-slate-400">
Auf Wunsch des Endkunden können Sie dessen personenbezogene Daten unwiderruflich anonymisieren.
Bestehende Bestelldaten (PDF-Rechnungen, Snapshots) werden aus steuerrechtlichen Gründen
<strong className="text-white"> nicht </strong> gelöscht.
</CardDescription>
</CardHeader>
<CardContent>
{!showGdprConfirm ? (
<Button
variant="outline"
className="border-red-500/30 text-red-400 hover:bg-red-500/10 gap-2"
onClick={() => setShowGdprConfirm(true)}
>
<Trash2 className="w-4 h-4" /> Kundendaten anonymisieren
</Button>
) : (
<div className="space-y-3 p-4 rounded-lg bg-red-500/10 border border-red-500/30">
<p className="text-sm text-red-300 font-semibold">
Diese Aktion ist unwiderruflich. Alle personenbezogenen Daten werden überschrieben.
</p>
<div className="flex gap-3">
<Button
onClick={handleAnonymize}
disabled={anonymizing}
className="bg-red-600 hover:bg-red-700 gap-2"
>
{anonymizing ? <Loader2 className="w-4 h-4 animate-spin" /> : <Trash2 className="w-4 h-4" />}
Ja, unwiderruflich anonymisieren
</Button>
<Button variant="ghost" onClick={() => setShowGdprConfirm(false)}>
Abbrechen
</Button>
</div>
</div>
)}
</CardContent>
</Card>
)}
{/* Bereits anonymisiert */}
{customer.is_anonymized && (
<Card className="glass-dark border-white/10">
<CardContent className="py-8 text-center space-y-2">
<AlertTriangle className="w-10 h-10 text-amber-400 mx-auto" />
<p className="text-slate-300">Dieser Endkunde wurde bereits anonymisiert.</p>
<p className="text-slate-500 text-sm">Die Daten können nicht wiederhergestellt werden.</p>
</CardContent>
</Card>
)}
</div>
</div>
)
}