Compare commits
3
Commits
e49133fd64
...
eeb89e09db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eeb89e09db | ||
|
|
e7f9aa65d4 | ||
|
|
616cb77c0d |
@@ -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?
|
||||
expiryDate DateTime?
|
||||
documentPath String? // Pfad zur hochgeladenen PDF
|
||||
description String? @db.Text // Freitext-Notiz (z.B. "Geschäftskonto", "gemeinsames Konto mit Partner")
|
||||
isActive Boolean @default(true)
|
||||
contracts Contract[]
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@ -317,6 +317,7 @@ export async function createBankCard(
|
||||
bic?: string;
|
||||
bankName?: string;
|
||||
expiryDate?: Date;
|
||||
description?: string;
|
||||
}
|
||||
) {
|
||||
return prisma.bankCard.create({
|
||||
@@ -336,6 +337,7 @@ export async function updateBankCard(
|
||||
bic?: string;
|
||||
bankName?: string;
|
||||
expiryDate?: Date;
|
||||
description?: string;
|
||||
isActive?: boolean;
|
||||
}
|
||||
) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { buildContractLabelParts } from '../../utils/contractLabel';
|
||||
import type { ContractType } from '../../types';
|
||||
import { formatDate } from '../../utils/dateFormat';
|
||||
import { useProviderSettings } from '../../hooks/useProviderSettings';
|
||||
import { Plus, Trash2, Eye, EyeOff, Info, X, ArrowLeft, ExternalLink } from 'lucide-react';
|
||||
import { Plus, Trash2, Eye, EyeOff, Info, X, ArrowLeft, ExternalLink, Mail } from 'lucide-react';
|
||||
|
||||
// Kleine Helper-Komponente: Label + Mini-Link im neuen Tab. Wird neben
|
||||
// Select-Feldern eingesetzt, deren Stammdaten an anderer Stelle gepflegt
|
||||
@@ -1116,15 +1116,43 @@ export default function ContractForm() {
|
||||
<span className="text-sm">{customerEmailLabel} Adresse</span>
|
||||
</label>
|
||||
{usernameType === 'stressfrei' && (
|
||||
<Select
|
||||
value={selectedStressfreiEmailId}
|
||||
onChange={(e) => setSelectedStressfreiEmailId(e.target.value)}
|
||||
options={stressfreiEmails.map((e: { id: number; email: string; notes?: string }) => ({
|
||||
value: e.id,
|
||||
label: e.email + (e.notes ? ` (${e.notes})` : ''),
|
||||
}))}
|
||||
placeholder={stressfreiEmails.length === 0 ? 'Keine Stressfrei-Adressen vorhanden' : 'Adresse auswählen...'}
|
||||
/>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
<Select
|
||||
value={selectedStressfreiEmailId}
|
||||
onChange={(e) => setSelectedStressfreiEmailId(e.target.value)}
|
||||
options={stressfreiEmails.map((e: { id: number; email: string; notes?: string }) => ({
|
||||
value: e.id,
|
||||
label: e.email + (e.notes ? ` (${e.notes})` : ''),
|
||||
}))}
|
||||
placeholder={stressfreiEmails.length === 0 ? 'Keine Stressfrei-Adressen vorhanden' : 'Adresse auswählen...'}
|
||||
/>
|
||||
</div>
|
||||
{customerId && (
|
||||
<div className="flex items-center gap-1 pt-1.5 flex-shrink-0">
|
||||
<a
|
||||
href={`/customers/${customerId}?tab=stressfrei`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={`${customerEmailLabel} Adressen des Kunden in neuem Tab öffnen`}
|
||||
aria-label={`${customerEmailLabel} Adressen des Kunden in neuem Tab öffnen`}
|
||||
className="text-gray-400 hover:text-blue-600 p-1 rounded"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
<a
|
||||
href={`/customers/${customerId}?tab=emails`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="E-Mail-Postfach des Kunden in neuem Tab öffnen"
|
||||
aria-label="E-Mail-Postfach des Kunden in neuem Tab öffnen"
|
||||
className="text-gray-400 hover:text-blue-600 p-1 rounded"
|
||||
>
|
||||
<Mail className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{usernameType === 'stressfrei' && stressfreiEmails.length === 0 && (
|
||||
<p className="text-xs text-amber-600">
|
||||
|
||||
@@ -961,6 +961,11 @@ function BankCardsTab({
|
||||
Gültig bis: {formatDate(card.expiryDate)}
|
||||
</p>
|
||||
)}
|
||||
{card.description && (
|
||||
<p className="text-sm text-gray-600 mt-1 italic whitespace-pre-line">
|
||||
{card.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Dokument-Upload Bereich */}
|
||||
<div className="mt-3 pt-3 border-t">
|
||||
@@ -2657,6 +2662,7 @@ function BankCardModal({
|
||||
bic: bankCard?.bic || '',
|
||||
bankName: bankCard?.bankName || '',
|
||||
expiryDate: bankCard?.expiryDate ? new Date(bankCard.expiryDate).toISOString().split('T')[0] : '',
|
||||
description: bankCard?.description || '',
|
||||
isActive: bankCard?.isActive ?? true,
|
||||
});
|
||||
|
||||
@@ -2672,7 +2678,7 @@ function BankCardModal({
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['customer', customerId] });
|
||||
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: '' })}
|
||||
/>
|
||||
|
||||
<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 && (
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -4057,7 +4074,11 @@ function StressfreiEmailModal({
|
||||
checked={provisionAtProvider}
|
||||
onChange={(e) => {
|
||||
setProvisionAtProvider(e.target.checked);
|
||||
if (!e.target.checked) setCreateMailbox(false);
|
||||
// Sobald "Beim Provider anlegen" aktiv ist, wollen wir
|
||||
// per Default direkt eine echte Mailbox mit Passwort –
|
||||
// sonst müsste man zwei Häkchen setzen, um ein
|
||||
// vollwertiges Postfach zu bekommen.
|
||||
setCreateMailbox(e.target.checked);
|
||||
}}
|
||||
className="mt-1 rounded border-gray-300"
|
||||
/>
|
||||
|
||||
@@ -150,6 +150,7 @@ export interface BankCard {
|
||||
bankName?: string;
|
||||
expiryDate?: string;
|
||||
documentPath?: string;
|
||||
description?: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user