Datenschutz vollmacht fixed, two time counter added

This commit is contained in:
2026-03-21 16:42:31 +01:00
parent 0121c82412
commit 4f359df161
56 changed files with 4401 additions and 789 deletions
+152 -51
View File
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { useParams, Link, useNavigate, useSearchParams, useLocation } from 'react-router-dom';
import { pushHistory, popHistory } from '../../utils/navigation';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { customerApi, addressApi, bankCardApi, documentApi, meterApi, uploadApi, contractApi, stressfreiEmailApi, emailProviderApi, gdprApi, StressfreiEmail, ContractTreeNode } from '../../services/api';
import { EmailClientTab } from '../../components/email';
@@ -22,7 +23,7 @@ export default function CustomerDetail({ portalCustomerId }: { portalCustomerId?
const queryClient = useQueryClient();
const { hasPermission, isCustomerPortal } = useAuth();
const location = useLocation();
const backTo = (location.state as any)?.from as string | undefined;
const back = popHistory(location.state, isCustomerPortal ? '/' : '/customers');
const [searchParams, setSearchParams] = useSearchParams();
const customerId = portalCustomerId || parseInt(id!);
const defaultTab = searchParams.get('tab') || 'addresses';
@@ -204,7 +205,7 @@ export default function CustomerDetail({ portalCustomerId }: { portalCustomerId?
<div>
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<Button variant="ghost" size="sm" onClick={() => navigate(backTo || (isCustomerPortal ? '/' : '/customers'))}>
<Button variant="ghost" size="sm" onClick={() => navigate(back.to, { state: back.state })}>
<ArrowLeft className="w-4 h-4" />
</Button>
<div>
@@ -221,7 +222,7 @@ export default function CustomerDetail({ portalCustomerId }: { portalCustomerId?
</div>
<div className="flex gap-2">
{hasPermission('customers:update') && (
<Link to={`/customers/${id}/edit`} state={{ from: `/customers/${id}` }}>
<Link to={`/customers/${id}/edit`} state={pushHistory(location.pathname + location.search, (location as any).state)}>
<Button variant="secondary">
<Edit className="w-4 h-4 mr-2" />
Bearbeiten
@@ -649,7 +650,7 @@ function AddressesTab({
const queryClient = useQueryClient();
const deleteMutation = useMutation({
mutationFn: addressApi.delete,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
return (
@@ -746,18 +747,18 @@ function BankCardsTab({
const updateMutation = useMutation({
mutationFn: ({ id, data }: { id: number; data: Partial<BankCard> }) =>
bankCardApi.update(id, data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const deleteMutation = useMutation({
mutationFn: bankCardApi.delete,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const handleDocumentUpload = async (cardId: number, file: File) => {
try {
await uploadApi.uploadBankCardDocument(cardId, file);
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
} catch (error) {
console.error('Upload fehlgeschlagen:', error);
alert('Upload fehlgeschlagen');
@@ -768,7 +769,7 @@ function BankCardsTab({
if (!confirm('Dokument wirklich löschen?')) return;
try {
await uploadApi.deleteBankCardDocument(cardId);
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
} catch (error) {
console.error('Löschen fehlgeschlagen:', error);
alert('Löschen fehlgeschlagen');
@@ -972,18 +973,18 @@ function DocumentsTab({
const updateMutation = useMutation({
mutationFn: ({ id, data }: { id: number; data: Partial<IdentityDocument> }) =>
documentApi.update(id, data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const deleteMutation = useMutation({
mutationFn: documentApi.delete,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const handleDocumentUpload = async (docId: number, file: File) => {
try {
await uploadApi.uploadIdentityDocument(docId, file);
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
} catch (error) {
console.error('Upload fehlgeschlagen:', error);
alert('Upload fehlgeschlagen');
@@ -994,7 +995,7 @@ function DocumentsTab({
if (!confirm('Dokument wirklich löschen?')) return;
try {
await uploadApi.deleteIdentityDocument(docId);
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
} catch (error) {
console.error('Löschen fehlgeschlagen:', error);
alert('Löschen fehlgeschlagen');
@@ -1204,26 +1205,34 @@ function MetersTab({
onAdd: () => void;
onEdit: (meter: Meter) => void;
}) {
const [showReadingModal, setShowReadingModal] = useState<{ meterId: number; meterType: 'ELECTRICITY' | 'GAS' } | null>(null);
const [showReadingModal, setShowReadingModal] = useState<{ meterId: number; meterType: 'ELECTRICITY' | 'GAS'; tariffModel?: string } | null>(null);
const [expandedMeter, setExpandedMeter] = useState<number | null>(null);
const [editingReading, setEditingReading] = useState<{ meterId: number; meterType: 'ELECTRICITY' | 'GAS'; reading: any } | null>(null);
const [editingReading, setEditingReading] = useState<{ meterId: number; meterType: 'ELECTRICITY' | 'GAS'; tariffModel?: string; reading: any } | null>(null);
const queryClient = useQueryClient();
const updateMutation = useMutation({
mutationFn: ({ id, data }: { id: number; data: Partial<Meter> }) =>
meterApi.update(id, data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const [deleteError, setDeleteError] = useState<string | null>(null);
const deleteMutation = useMutation({
mutationFn: meterApi.delete,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
setDeleteError(null);
},
onError: (err) => {
setDeleteError(err instanceof Error ? err.message : 'Fehler beim Löschen');
},
});
const deleteReadingMutation = useMutation({
mutationFn: ({ meterId, readingId }: { meterId: number; readingId: number }) =>
meterApi.deleteReading(meterId, readingId),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const filtered = showInactive ? meters : meters.filter((m) => m.isActive);
@@ -1273,6 +1282,9 @@ function MetersTab({
<Badge variant={meter.type === 'ELECTRICITY' ? 'warning' : 'info'}>
{meter.type === 'ELECTRICITY' ? 'Strom' : 'Gas'}
</Badge>
{meter.tariffModel === 'DUAL' && (
<Badge variant="default">HT/NT</Badge>
)}
{!meter.isActive && <Badge variant="danger">Inaktiv</Badge>}
</div>
{canEdit && (
@@ -1281,7 +1293,7 @@ function MetersTab({
<Button
variant="ghost"
size="sm"
onClick={() => setShowReadingModal({ meterId: meter.id, meterType: meter.type })}
onClick={() => setShowReadingModal({ meterId: meter.id, meterType: meter.type, tariffModel: meter.tariffModel })}
title="Zählerstand hinzufügen"
>
<Plus className="w-4 h-4" />
@@ -1371,14 +1383,17 @@ function MetersTab({
</span>
<div className="flex items-center gap-2">
<span className="font-mono flex items-center gap-1">
{reading.value.toLocaleString('de-DE')} {reading.unit}
<CopyButton value={reading.value.toString()} title="Nur Wert kopieren" />
<CopyButton value={`${reading.value.toLocaleString('de-DE')} ${reading.unit}`} title="Mit Einheit kopieren" />
{reading.valueNt !== undefined && reading.valueNt !== null ? (
<>HT: {reading.value.toLocaleString('de-DE')} / NT: {reading.valueNt.toLocaleString('de-DE')} {reading.unit}</>
) : (
<>{reading.value.toLocaleString('de-DE')} {reading.unit}</>
)}
<CopyButton value={reading.value.toString()} title="Wert kopieren" />
</span>
{canEdit && (
<div className="opacity-0 group-hover:opacity-100 flex gap-1">
<button
onClick={() => setEditingReading({ meterId: meter.id, meterType: meter.type, reading })}
onClick={() => setEditingReading({ meterId: meter.id, meterType: meter.type, tariffModel: meter.tariffModel, reading })}
className="text-gray-400 hover:text-blue-600"
title="Bearbeiten"
>
@@ -1417,6 +1432,7 @@ function MetersTab({
onClose={() => setShowReadingModal(null)}
meterId={showReadingModal.meterId}
meterType={showReadingModal.meterType}
tariffModel={showReadingModal.tariffModel as any}
customerId={customerId}
/>
)}
@@ -1427,10 +1443,43 @@ function MetersTab({
onClose={() => setEditingReading(null)}
meterId={editingReading.meterId}
meterType={editingReading.meterType}
tariffModel={editingReading.tariffModel as any}
customerId={customerId}
reading={editingReading.reading}
/>
)}
{/* Fehler-Modal beim Löschen (z.B. Zähler noch an Vertrag) */}
{deleteError && (
<Modal isOpen={true} onClose={() => setDeleteError(null)} title="Zähler kann nicht gelöscht werden">
<p className="text-sm text-gray-600 mb-4">
Der Zähler ist noch folgenden Verträgen zugeordnet und kann daher nicht gelöscht werden:
</p>
<div className="space-y-2">
{deleteError.match(/[A-Z]+-[A-Z0-9]+/g)?.map((contractNumber) => (
<Link
key={contractNumber}
to={`/contracts?search=${contractNumber}`}
onClick={() => setDeleteError(null)}
className="flex items-center gap-2 p-3 bg-gray-50 border rounded-lg text-blue-600 hover:bg-blue-50 hover:border-blue-300 transition-colors"
>
<FileText className="w-4 h-4" />
<span className="font-mono">{contractNumber}</span>
</Link>
)) ?? (
<p className="text-sm text-red-600">{deleteError}</p>
)}
</div>
<p className="text-xs text-gray-500 mt-4">
Bitte entfernen Sie den Zähler zuerst aus den oben genannten Verträgen.
</p>
<div className="flex justify-end mt-4">
<Button variant="secondary" onClick={() => setDeleteError(null)}>
Schließen
</Button>
</div>
</Modal>
)}
</div>
);
}
@@ -1457,7 +1506,7 @@ function ContractsTab({
const deleteMutation = useMutation({
mutationFn: contractApi.delete,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
queryClient.invalidateQueries({ queryKey: ['customers'] });
queryClient.invalidateQueries({ queryKey: ['contracts'] });
queryClient.invalidateQueries({ queryKey: ['contract-tree', customerId] });
@@ -1549,7 +1598,7 @@ function ContractsTab({
<div className="w-6" /> // Platzhalter für Ausrichtung
) : null}
<Link to={`/contracts/${contract.id}`} state={{ from: `/customers/${customerId}?tab=contracts` }} className="font-mono flex items-center gap-1 text-blue-600 hover:underline">
<Link to={`/contracts/${contract.id}`} state={pushHistory(location.pathname + location.search, (location as any).state)} className="font-mono flex items-center gap-1 text-blue-600 hover:underline">
{contract.contractNumber}
<CopyButton value={contract.contractNumber} />
</Link>
@@ -2054,7 +2103,7 @@ function AddressModal({
const createMutation = useMutation({
mutationFn: (data: typeof formData) => addressApi.create(customerId, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
setFormData({
type: 'DELIVERY_RESIDENCE',
@@ -2071,7 +2120,7 @@ function AddressModal({
const updateMutation = useMutation({
mutationFn: (data: typeof formData) => addressApi.update(address!.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
},
});
@@ -2201,7 +2250,7 @@ function BankCardModal({
const createMutation = useMutation({
mutationFn: (data: any) => bankCardApi.create(customerId, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
setFormData({ accountHolder: '', iban: '', bic: '', bankName: '', expiryDate: '', isActive: true });
},
@@ -2210,7 +2259,7 @@ function BankCardModal({
const updateMutation = useMutation({
mutationFn: (data: any) => bankCardApi.update(bankCard!.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
},
});
@@ -2333,7 +2382,7 @@ function DocumentModal({
const createMutation = useMutation({
mutationFn: (data: any) => documentApi.create(customerId, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
setFormData({
type: 'ID_CARD',
@@ -2351,7 +2400,7 @@ function DocumentModal({
const updateMutation = useMutation({
mutationFn: (data: any) => documentApi.update(document!.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
},
});
@@ -2498,6 +2547,7 @@ function MeterModal({
const getInitialFormData = () => ({
meterNumber: meter?.meterNumber || '',
type: meter?.type || 'ELECTRICITY' as const,
tariffModel: meter?.tariffModel || 'SINGLE' as const,
location: meter?.location || '',
isActive: meter?.isActive ?? true,
});
@@ -2507,16 +2557,16 @@ function MeterModal({
const createMutation = useMutation({
mutationFn: (data: any) => meterApi.create(customerId, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
setFormData({ meterNumber: '', type: 'ELECTRICITY', location: '', isActive: true });
setFormData({ meterNumber: '', type: 'ELECTRICITY', tariffModel: 'SINGLE', location: '', isActive: true });
},
});
const updateMutation = useMutation({
mutationFn: (data: any) => meterApi.update(meter!.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
},
});
@@ -2557,6 +2607,18 @@ function MeterModal({
]}
/>
{formData.type === 'ELECTRICITY' && (
<Select
label="Tarifmodell"
value={formData.tariffModel}
onChange={(e) => setFormData({ ...formData, tariffModel: e.target.value as any })}
options={[
{ value: 'SINGLE', label: 'Eintarifzähler (Standard)' },
{ value: 'DUAL', label: 'Zweitarifzähler (HT/NT)' },
]}
/>
)}
<Input
label="Standort"
value={formData.location}
@@ -2594,6 +2656,7 @@ function MeterReadingModal({
onClose,
meterId,
meterType,
tariffModel,
customerId,
reading,
}: {
@@ -2601,47 +2664,63 @@ function MeterReadingModal({
onClose: () => void;
meterId: number;
meterType: 'ELECTRICITY' | 'GAS';
tariffModel?: 'SINGLE' | 'DUAL';
customerId: number;
reading?: { id: number; readingDate: string; value: number; unit: string; notes?: string } | null;
reading?: { id: number; readingDate: string; value: number; valueNt?: number; unit: string; notes?: string } | null;
}) {
const queryClient = useQueryClient();
const isEditing = !!reading;
const defaultUnit = meterType === 'ELECTRICITY' ? 'kWh' : 'm³';
const isDualTariff = tariffModel === 'DUAL';
const getInitialFormData = () => ({
readingDate: reading?.readingDate
? new Date(reading.readingDate).toISOString().split('T')[0]
: new Date().toISOString().split('T')[0],
value: reading?.value?.toString() || '',
valueNt: reading?.valueNt?.toString() || '',
notes: reading?.notes || '',
});
const [formData, setFormData] = useState(getInitialFormData);
const [error, setError] = useState<string | null>(null);
const createMutation = useMutation({
mutationFn: (data: any) => meterApi.addReading(meterId, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
setError(null);
onClose();
},
onError: (err) => {
setError(err instanceof Error ? err.message : 'Fehler beim Speichern');
},
});
const updateMutation = useMutation({
mutationFn: (data: any) => meterApi.updateReading(meterId, reading!.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
setError(null);
onClose();
},
onError: (err) => {
setError(err instanceof Error ? err.message : 'Fehler beim Speichern');
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const data = {
const data: Record<string, unknown> = {
readingDate: new Date(formData.readingDate),
value: parseFloat(formData.value),
unit: defaultUnit,
notes: formData.notes || undefined,
};
if (isDualTariff && formData.valueNt) {
data.valueNt = parseFloat(formData.valueNt);
}
if (isEditing) {
updateMutation.mutate(data);
} else {
@@ -2667,10 +2746,10 @@ function MeterReadingModal({
required
/>
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2">
<div className={`grid ${isDualTariff ? 'grid-cols-2' : 'grid-cols-3'} gap-4`}>
<div className={isDualTariff ? '' : 'col-span-2'}>
<Input
label="Zählerstand"
label={isDualTariff ? 'HT-Stand (Hochtarif)' : 'Zählerstand'}
type="number"
step="0.01"
value={formData.value}
@@ -2678,12 +2757,26 @@ function MeterReadingModal({
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Einheit</label>
<div className="h-10 flex items-center px-3 bg-gray-100 border border-gray-300 rounded-md text-gray-700">
{defaultUnit}
{isDualTariff && (
<div>
<Input
label="NT-Stand (Niedertarif)"
type="number"
step="0.01"
value={formData.valueNt}
onChange={(e) => setFormData({ ...formData, valueNt: e.target.value })}
required
/>
</div>
</div>
)}
{!isDualTariff && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Einheit</label>
<div className="h-10 flex items-center px-3 bg-gray-100 border border-gray-300 rounded-md text-gray-700">
{defaultUnit}
</div>
</div>
)}
</div>
<Input
@@ -2693,6 +2786,12 @@ function MeterReadingModal({
placeholder="Optionale Notizen..."
/>
{error && (
<div className="p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
{error}
</div>
)}
<div className="flex justify-end gap-2">
<Button type="button" variant="secondary" onClick={onClose}>
Abbrechen
@@ -2732,12 +2831,12 @@ function StressfreiEmailsTab({
const updateMutation = useMutation({
mutationFn: ({ id, data }: { id: number; data: Partial<StressfreiEmail> }) =>
stressfreiEmailApi.update(id, data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const deleteMutation = useMutation({
mutationFn: stressfreiEmailApi.delete,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['customer', customerId] }),
});
const filtered = showInactive ? emails : emails.filter((e) => e.isActive);
@@ -3087,7 +3186,7 @@ function StressfreiEmailModal({
const result = await stressfreiEmailApi.enableMailbox(email.id);
if (result.success) {
setMailboxEnabled(true);
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
queryClient.invalidateQueries({ queryKey: ['mailbox-accounts', customerId] });
} else {
setProvisionError(result.error || 'Mailbox-Aktivierung fehlgeschlagen');
@@ -3108,7 +3207,7 @@ function StressfreiEmailModal({
setMailboxEnabled(result.data.hasMailbox);
if (result.data.wasUpdated) {
// DB wurde aktualisiert, Query invalidieren
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
}
}
} catch (error) {
@@ -3199,7 +3298,7 @@ function StressfreiEmailModal({
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
queryClient.invalidateQueries({ queryKey: ['mailbox-accounts', customerId] });
setLocalPart('');
setNotes('');
@@ -3216,7 +3315,7 @@ function StressfreiEmailModal({
mutationFn: (data: Partial<StressfreiEmail>) =>
stressfreiEmailApi.update(email!.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['customer', customerId.toString()] });
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
onClose();
},
});
@@ -3664,6 +3763,7 @@ function ConsentTab({
try {
await uploadApi.uploadPrivacyPolicy(customerId, file);
onUpdate?.();
queryClient.invalidateQueries({ queryKey: ['customer-consents', customerId] });
} catch (error) {
console.error('Upload fehlgeschlagen:', error);
alert('Upload fehlgeschlagen');
@@ -3675,6 +3775,7 @@ function ConsentTab({
try {
await uploadApi.deletePrivacyPolicy(customerId);
onUpdate?.();
queryClient.invalidateQueries({ queryKey: ['customer-consents', customerId] });
} catch (error) {
console.error('Löschen fehlgeschlagen:', error);
alert('Löschen fehlgeschlagen');
@@ -1,5 +1,6 @@
import { useEffect } from 'react';
import { useNavigate, useParams, useLocation } from 'react-router-dom';
import { popHistory } from '../../utils/navigation';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useForm } from 'react-hook-form';
import { customerApi } from '../../services/api';
@@ -17,7 +18,7 @@ export default function CustomerForm() {
const location = useLocation();
const queryClient = useQueryClient();
const isEdit = !!id;
const backTo = (location.state as any)?.from as string | undefined;
const back = popHistory(location.state, isEdit ? `/customers/${id}` : '/customers');
const { register, handleSubmit, reset, watch, setValue, formState: { errors } } = useForm<CustomerFormData>();
const customerType = watch('type');
@@ -234,7 +235,7 @@ export default function CustomerForm() {
</Card>
<div className="flex justify-end gap-4">
<Button type="button" variant="secondary" onClick={() => navigate(backTo || (isEdit ? `/customers/${id}` : '/customers'))}>
<Button type="button" variant="secondary" onClick={() => navigate(back.to, { state: back.state })}>
Abbrechen
</Button>
<Button type="submit" disabled={isLoading}>
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Link } from 'react-router-dom';
import { pushHistory } from '../../utils/navigation';
import { customerApi } from '../../services/api';
import { useAuth } from '../../context/AuthContext';
import Card from '../../components/ui/Card';
@@ -78,12 +79,12 @@ export default function CustomerList() {
{data.data.map((customer) => (
<tr key={customer.id} className="border-b hover:bg-gray-50">
<td className="py-3 px-4 font-mono text-sm">
<Link to={`/customers/${customer.id}`} state={{ from: '/customers' }} className="text-blue-600 hover:underline">
<Link to={`/customers/${customer.id}`} state={pushHistory('/customers')} className="text-blue-600 hover:underline">
{customer.customerNumber}
</Link>
</td>
<td className="py-3 px-4">
<Link to={`/customers/${customer.id}`} state={{ from: '/customers' }} className="text-blue-600 hover:underline">
<Link to={`/customers/${customer.id}`} state={pushHistory('/customers')} className="text-blue-600 hover:underline">
{customer.type === 'BUSINESS' && customer.companyName
? customer.companyName
: `${customer.firstName} ${customer.lastName}`}