refactor: replace payment dropdown with independent toggles

- Replace showPaymentInstructionsInMonthlyStatement dropdown with enableIbanPayment and enableRevolutPayment boolean toggles
- Update UserSettingsForm to use separate fieldsets for IBAN and Revolut with independent toggle switches
- Add hidden inputs to preserve values when toggles are disabled
- Update validation logic to check enableIbanPayment instead of show2dCodeInMonthlyStatement
- Reorganize translation keys to match new structure (iban-* and revolut-* prefixes)
- Update ViewLocationCard to use enableIbanPayment field

This provides better UX by allowing users to enable both payment methods simultaneously if needed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-24 14:45:59 +01:00
parent 686bec6c10
commit 632f8888b5
6 changed files with 168 additions and 101 deletions

View File

@@ -19,7 +19,6 @@ export type State = {
ownerTown?: string[];
ownerIBAN?: string[];
currency?: string[];
show2dCodeInMonthlyStatement?: string[];
};
message?: string | null;
success?: boolean;
@@ -29,6 +28,8 @@ export type State = {
* Schema for validating user settings form fields
*/
const FormSchema = (t: IntlTemplateFn) => z.object({
currency: z.string().optional(),
enableIbanPayment: z.boolean().optional(),
ownerName: z.string().max(25).optional(),
ownerStreet: z.string().max(25).optional(),
ownerTown: z.string().max(27).optional(),
@@ -43,11 +44,11 @@ const FormSchema = (t: IntlTemplateFn) => z.object({
},
{ message: t("owner-iban-invalid") }
),
currency: z.string().optional(),
show2dCodeInMonthlyStatement: z.boolean().optional().nullable(),
enableRevolutPayment: z.boolean().optional(),
ownerRevolutProfileName: z.string().max(25).optional(),
})
.refine((data) => {
if (data.show2dCodeInMonthlyStatement) {
if (data.enableIbanPayment) {
return !!data.ownerName && data.ownerName.trim().length > 0;
}
return true;
@@ -56,7 +57,7 @@ const FormSchema = (t: IntlTemplateFn) => z.object({
path: ["ownerName"],
})
.refine((data) => {
if (data.show2dCodeInMonthlyStatement) {
if (data.enableIbanPayment) {
return !!data.ownerStreet && data.ownerStreet.trim().length > 0;
}
return true;
@@ -65,7 +66,7 @@ const FormSchema = (t: IntlTemplateFn) => z.object({
path: ["ownerStreet"],
})
.refine((data) => {
if (data.show2dCodeInMonthlyStatement) {
if (data.enableIbanPayment) {
return !!data.ownerTown && data.ownerTown.trim().length > 0;
}
return true;
@@ -74,7 +75,7 @@ const FormSchema = (t: IntlTemplateFn) => z.object({
path: ["ownerTown"],
})
.refine((data) => {
if (data.show2dCodeInMonthlyStatement) {
if (data.enableIbanPayment) {
if (!data.ownerIBAN || data.ownerIBAN.trim().length === 0) {
return false;
}
@@ -88,7 +89,7 @@ const FormSchema = (t: IntlTemplateFn) => z.object({
path: ["ownerIBAN"],
})
.refine((data) => {
if (data.show2dCodeInMonthlyStatement) {
if (data.enableIbanPayment) {
return !!data.currency && data.currency.trim().length > 0;
}
return true;
@@ -141,7 +142,9 @@ export const updateUserSettings = withUser(async (user: AuthenticatedUser, prevS
ownerTown: formData.get('ownerTown') || undefined,
ownerIBAN: formData.get('ownerIBAN') || undefined,
currency: formData.get('currency') || undefined,
show2dCodeInMonthlyStatement: formData.get('generateTenantCode') === 'on',
enableIbanPayment: formData.get('enableIbanPayment') === 'on' ? true : false,
enableRevolutPayment: formData.get('enableRevolutPayment') === 'on' ? true : false,
ownerRevolutProfileName: formData.get('ownerRevolutProfileName') || undefined,
});
// If form validation fails, return errors early. Otherwise, continue...
@@ -153,7 +156,7 @@ export const updateUserSettings = withUser(async (user: AuthenticatedUser, prevS
};
}
const { ownerName, ownerStreet, ownerTown, ownerIBAN, currency, show2dCodeInMonthlyStatement } = validatedFields.data;
const { enableIbanPayment, ownerName, ownerStreet, ownerTown, ownerIBAN, currency, enableRevolutPayment, ownerRevolutProfileName } = validatedFields.data;
// Normalize IBAN: remove spaces and convert to uppercase
const normalizedOwnerIBAN = ownerIBAN ? ownerIBAN.replace(/\s/g, '').toUpperCase() : null;
@@ -164,12 +167,14 @@ export const updateUserSettings = withUser(async (user: AuthenticatedUser, prevS
const userSettings: UserSettings = {
userId,
ownerName: ownerName || null,
ownerStreet: ownerStreet || null,
ownerTown: ownerTown || null,
enableIbanPayment: enableIbanPayment ?? false,
ownerName: ownerName ?? undefined,
ownerStreet: ownerStreet ?? undefined,
ownerTown: ownerTown ?? undefined,
ownerIBAN: normalizedOwnerIBAN,
currency: currency || null,
show2dCodeInMonthlyStatement: show2dCodeInMonthlyStatement ?? false,
currency: currency ?? undefined,
enableRevolutPayment: enableRevolutPayment ?? false,
ownerRevolutProfileName: ownerRevolutProfileName ?? undefined,
};
await dbClient.collection<UserSettings>("userSettings")

View File

@@ -18,6 +18,8 @@ export interface YearMonth {
export interface UserSettings {
/** user's ID */
userId: string;
/** whether enableshow IBAN payment instructions in monthly statement */
enableIbanPayment?: boolean | null;
/** owner name */
ownerName?: string | null;
/** owner street */
@@ -28,18 +30,10 @@ export interface UserSettings {
ownerIBAN?: string | null;
/** currency (ISO 4217) */
currency?: string | null;
/** whether to enable Revolut payment instructions in monthly statement */
enableRevolutPayment?: boolean | null;
/** owner Revolut payment link */
ownerRevolutProfileName?: string | null;
/** whether to show 2D code in monthly statement */
show2dCodeInMonthlyStatement?: boolean | null;
/** whether to show payment instructions in monthly statement */
showPaymentInstructionsInMonthlyStatement?: "disabled" | "iban" | "revolut" | null;
// /** whether enableshow IBAN payment instructions in monthly statement */
// enableIbanPaymentInstructionsInMonthlyStatement?: boolean | null;
// /** whether to enable Revolut payment instructions in monthly statement */
// enableRevolutPaymentInstructionsInMonthlyStatement?: boolean | null;
};
/** bill object in the form returned by MongoDB */