Bankkarte: optionales Beschreibung-Feld
Neues optionales Freitext-Feld `description` (TEXT NULL) auf BankCard – z.B. "Geschäftskonto", "gemeinsames Konto mit Partner". Migration mit IF NOT EXISTS. Backend service create/update nimmt description entgegen. Frontend: - BankCard-Type um description ergänzt. - BankCardModal: neue Textarea zwischen Ablaufdatum und Aktiv- Checkbox mit Placeholder-Hilfetext. - Bank-Kartenübersicht zeigt die Beschreibung kursiv unter den bestehenden Feldern, falls gesetzt (whitespace-pre-line für mehrzeilige Notizen). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
-- Optionale Freitext-Beschreibung pro Bankkarte (z.B. "Geschäftskonto",
|
||||||
|
-- "gemeinsames Konto mit Partner"). TEXT statt VARCHAR, weil Notizen
|
||||||
|
-- gelegentlich länger werden dürfen.
|
||||||
|
|
||||||
|
ALTER TABLE `BankCard`
|
||||||
|
ADD COLUMN IF NOT EXISTS `description` TEXT NULL;
|
||||||
@@ -327,6 +327,7 @@ model BankCard {
|
|||||||
bankName String?
|
bankName String?
|
||||||
expiryDate DateTime?
|
expiryDate DateTime?
|
||||||
documentPath String? // Pfad zur hochgeladenen PDF
|
documentPath String? // Pfad zur hochgeladenen PDF
|
||||||
|
description String? @db.Text // Freitext-Notiz (z.B. "Geschäftskonto", "gemeinsames Konto mit Partner")
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
contracts Contract[]
|
contracts Contract[]
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|||||||
@@ -317,6 +317,7 @@ export async function createBankCard(
|
|||||||
bic?: string;
|
bic?: string;
|
||||||
bankName?: string;
|
bankName?: string;
|
||||||
expiryDate?: Date;
|
expiryDate?: Date;
|
||||||
|
description?: string;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
return prisma.bankCard.create({
|
return prisma.bankCard.create({
|
||||||
@@ -336,6 +337,7 @@ export async function updateBankCard(
|
|||||||
bic?: string;
|
bic?: string;
|
||||||
bankName?: string;
|
bankName?: string;
|
||||||
expiryDate?: Date;
|
expiryDate?: Date;
|
||||||
|
description?: string;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -961,6 +961,11 @@ function BankCardsTab({
|
|||||||
Gültig bis: {formatDate(card.expiryDate)}
|
Gültig bis: {formatDate(card.expiryDate)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
{card.description && (
|
||||||
|
<p className="text-sm text-gray-600 mt-1 italic whitespace-pre-line">
|
||||||
|
{card.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Dokument-Upload Bereich */}
|
{/* Dokument-Upload Bereich */}
|
||||||
<div className="mt-3 pt-3 border-t">
|
<div className="mt-3 pt-3 border-t">
|
||||||
@@ -2657,6 +2662,7 @@ function BankCardModal({
|
|||||||
bic: bankCard?.bic || '',
|
bic: bankCard?.bic || '',
|
||||||
bankName: bankCard?.bankName || '',
|
bankName: bankCard?.bankName || '',
|
||||||
expiryDate: bankCard?.expiryDate ? new Date(bankCard.expiryDate).toISOString().split('T')[0] : '',
|
expiryDate: bankCard?.expiryDate ? new Date(bankCard.expiryDate).toISOString().split('T')[0] : '',
|
||||||
|
description: bankCard?.description || '',
|
||||||
isActive: bankCard?.isActive ?? true,
|
isActive: bankCard?.isActive ?? true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2672,7 +2678,7 @@ function BankCardModal({
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
|
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
|
||||||
onClose();
|
onClose();
|
||||||
setFormData({ accountHolder: '', iban: '', bic: '', bankName: '', expiryDate: '', isActive: true });
|
setFormData({ accountHolder: '', iban: '', bic: '', bankName: '', expiryDate: '', description: '', isActive: true });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2743,6 +2749,17 @@ function BankCardModal({
|
|||||||
onClear={() => setFormData({ ...formData, expiryDate: '' })}
|
onClear={() => setFormData({ ...formData, expiryDate: '' })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">Beschreibung</label>
|
||||||
|
<textarea
|
||||||
|
value={formData.description}
|
||||||
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||||
|
rows={2}
|
||||||
|
placeholder="z.B. Geschäftskonto, gemeinsames Konto mit Partner…"
|
||||||
|
className="block w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<label className="flex items-center gap-2">
|
<label className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ export interface BankCard {
|
|||||||
bankName?: string;
|
bankName?: string;
|
||||||
expiryDate?: string;
|
expiryDate?: string;
|
||||||
documentPath?: string;
|
documentPath?: string;
|
||||||
|
description?: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user