feat: Add bank account details to admin profile

- Add accountHolder, bankName, iban, bic fields to AppUser domain
- Update UserOption, UserRow, UserMutation records in CatalogService
- Update all AppUser constructor calls to include new fields
- Add bank fields to frontend UserOption and UserRow types
- Add bank account form section to AdminProfilePage
This commit is contained in:
2026-03-18 09:31:32 +01:00
parent 8adc817428
commit 60e2f95637
4 changed files with 150 additions and 13 deletions

View File

@@ -19,6 +19,10 @@ public record AppUser(
String city,
String email,
String phoneNumber,
String accountHolder,
String bankName,
String iban,
String bic,
String passwordHash,
boolean active,
UserRole role,

View File

@@ -436,6 +436,10 @@ public class CatalogService {
adminManaged ? blankToNull(mutation.city()) : null,
normalizeEmail(mutation.email()),
adminManaged ? blankToNull(mutation.phoneNumber()) : null,
adminManaged ? blankToNull(mutation.accountHolder()) : null,
adminManaged ? blankToNull(mutation.bankName()) : null,
adminManaged ? blankToNull(mutation.iban()) : null,
adminManaged ? blankToNull(mutation.bic()) : null,
encodeIfPresent(mutation.password()),
mutation.active(),
adminManaged ? normalizeManagedRole(mutation.role()) : UserRole.CUSTOMER,
@@ -467,6 +471,10 @@ public class CatalogService {
isPrimaryUser(existing) || actor.role() == UserRole.ADMIN ? blankToNull(mutation.city()) : existing.city(),
normalizeEmail(mutation.email()),
isPrimaryUser(existing) || actor.role() == UserRole.ADMIN ? blankToNull(mutation.phoneNumber()) : existing.phoneNumber(),
isPrimaryUser(existing) || actor.role() == UserRole.ADMIN ? blankToNull(mutation.accountHolder()) : existing.accountHolder(),
isPrimaryUser(existing) || actor.role() == UserRole.ADMIN ? blankToNull(mutation.bankName()) : existing.bankName(),
isPrimaryUser(existing) || actor.role() == UserRole.ADMIN ? blankToNull(mutation.iban()) : existing.iban(),
isPrimaryUser(existing) || actor.role() == UserRole.ADMIN ? blankToNull(mutation.bic()) : existing.bic(),
isBlank(mutation.password()) ? existing.passwordHash() : passwordEncoder.encode(mutation.password()),
mutation.active(),
actor.role() == UserRole.ADMIN
@@ -516,6 +524,10 @@ public class CatalogService {
existing.city(),
existing.email(),
existing.phoneNumber(),
existing.accountHolder(),
existing.bankName(),
existing.iban(),
existing.bic(),
passwordEncoder.encode(newPassword),
existing.active(),
existing.role(),
@@ -580,6 +592,10 @@ public class CatalogService {
city,
normalizedEmail,
phoneNumber,
null,
null,
null,
null,
passwordEncoder.encode(mutation.password()),
false,
UserRole.CUSTOMER,
@@ -600,6 +616,10 @@ public class CatalogService {
created.city(),
created.email(),
created.phoneNumber(),
created.accountHolder(),
created.bankName(),
created.iban(),
created.bic(),
created.passwordHash(),
false,
created.role(),
@@ -708,6 +728,10 @@ public class CatalogService {
user.city(),
user.email(),
user.phoneNumber(),
user.accountHolder(),
user.bankName(),
user.iban(),
user.bic(),
user.active(),
normalizeStoredRole(user.role()),
user.updatedAt()
@@ -743,6 +767,10 @@ public class CatalogService {
user.city(),
user.email(),
user.phoneNumber(),
user.accountHolder(),
user.bankName(),
user.iban(),
user.bic(),
normalizeStoredRole(user.role())
);
}
@@ -835,6 +863,10 @@ public class CatalogService {
user.city(),
user.email(),
user.phoneNumber(),
user.accountHolder(),
user.bankName(),
user.iban(),
user.bic(),
user.passwordHash(),
user.active(),
normalizeStoredRole(user.role()),
@@ -870,6 +902,10 @@ public class CatalogService {
null,
email,
null,
null,
null,
null,
null,
passwordEncoder.encode(rawPassword),
true,
role,
@@ -997,6 +1033,10 @@ public class CatalogService {
String city,
String email,
String phoneNumber,
String accountHolder,
String bankName,
String iban,
String bic,
UserRole role
) {
}
@@ -1066,6 +1106,10 @@ public class CatalogService {
String city,
String email,
String phoneNumber,
String accountHolder,
String bankName,
String iban,
String bic,
boolean active,
UserRole role,
LocalDateTime updatedAt
@@ -1083,6 +1127,10 @@ public class CatalogService {
String city,
String email,
String phoneNumber,
String accountHolder,
String bankName,
String iban,
String bic,
String password,
boolean active,
UserRole role

View File

@@ -62,6 +62,10 @@ export interface UserOption {
city: string | null;
email: string | null;
phoneNumber: string | null;
accountHolder: string | null;
bankName: string | null;
iban: string | null;
bic: string | null;
role: UserRole;
}

View File

@@ -18,6 +18,10 @@ export default function AdminProfilePage() {
city: "",
email: "",
phoneNumber: "",
accountHolder: "",
bankName: "",
iban: "",
bic: "",
});
// Load current user data
@@ -36,6 +40,10 @@ export default function AdminProfilePage() {
city: currentUser.city || "",
email: currentUser.email || "",
phoneNumber: currentUser.phoneNumber || "",
accountHolder: currentUser.accountHolder || "",
bankName: currentUser.bankName || "",
iban: currentUser.iban || "",
bic: currentUser.bic || "",
});
}
} catch (error) {
@@ -77,6 +85,10 @@ export default function AdminProfilePage() {
city: formData.city.trim() || null,
email: formData.email.trim(),
phoneNumber: formData.phoneNumber.trim() || null,
accountHolder: formData.accountHolder.trim() || null,
bankName: formData.bankName.trim() || null,
iban: formData.iban.trim() || null,
bic: formData.bic.trim() || null,
active: true,
});
@@ -92,6 +104,10 @@ export default function AdminProfilePage() {
city: response.city,
email: response.email,
phoneNumber: response.phoneNumber,
accountHolder: response.accountHolder,
bankName: response.bankName,
iban: response.iban,
bic: response.bic,
});
}
@@ -122,7 +138,7 @@ export default function AdminProfilePage() {
<p className="eyebrow">Stammdaten</p>
<h3>Meine Stammdaten</h3>
<p className="muted-text">
Verwalten Sie hier Ihre persönlichen und Unternehmensdaten.
Verwalten Sie hier Ihre persönlichen, Unternehmens- und Bankdaten.
</p>
</div>
</section>
@@ -140,12 +156,12 @@ export default function AdminProfilePage() {
</div>
)}
{/* Stammdaten-Formular */}
{/* Persönliche Daten */}
<section className="section-card">
<div className="section-card__header">
<div>
<p className="eyebrow">Profil</p>
<h3>Stammdaten bearbeiten</h3>
<h3>Persönliche Daten</h3>
</div>
</div>
@@ -255,19 +271,84 @@ export default function AdminProfilePage() {
disabled={saving}
/>
</label>
<div className="field" style={{ gridColumn: "1 / -1" }}>
<button
type="submit"
className="accent-button"
disabled={saving}
>
{saving ? "Wird gespeichert..." : "Stammdaten speichern"}
</button>
</div>
</form>
</section>
{/* Bankverbindung */}
<section className="section-card">
<div className="section-card__header">
<div>
<p className="eyebrow">Zahlung</p>
<h3>Bankverbindung</h3>
</div>
</div>
<div className="field-grid field-grid--2col">
<label className="field">
<span>Kontoinhaber</span>
<input
type="text"
value={formData.accountHolder}
onChange={(e) =>
setFormData({ ...formData, accountHolder: e.target.value })
}
placeholder="Name des Kontoinhabers"
disabled={saving}
/>
</label>
<label className="field">
<span>Bankname</span>
<input
type="text"
value={formData.bankName}
onChange={(e) =>
setFormData({ ...formData, bankName: e.target.value })
}
placeholder="Name der Bank"
disabled={saving}
/>
</label>
<label className="field">
<span>IBAN</span>
<input
type="text"
value={formData.iban}
onChange={(e) =>
setFormData({ ...formData, iban: e.target.value })
}
placeholder="DE12 3456 7890 1234 5678 90"
disabled={saving}
/>
</label>
<label className="field">
<span>BIC</span>
<input
type="text"
value={formData.bic}
onChange={(e) =>
setFormData({ ...formData, bic: e.target.value })
}
placeholder="ABCDEFGHXXX"
disabled={saving}
/>
</label>
</div>
<div className="field" style={{ marginTop: "1rem" }}>
<button
type="button"
className="accent-button"
onClick={handleSubmit}
disabled={saving}
>
{saving ? "Wird gespeichert..." : "Stammdaten speichern"}
</button>
</div>
</section>
{/* Info-Box */}
<section className="section-card">
<div className="info-panel">