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

@@ -28,18 +28,20 @@ const FormFields: FC<FormFieldsProps> = ({ userSettings, errors, message }) => {
// Track current form values for real-time validation
const [formValues, setFormValues] = useState({
enableIbanPayment: userSettings?.enableIbanPayment ?? false,
ownerName: userSettings?.ownerName ?? "",
ownerStreet: userSettings?.ownerStreet ?? "",
ownerTown: userSettings?.ownerTown ?? "",
ownerIBAN: formatIban(userSettings?.ownerIBAN) ?? "",
currency: userSettings?.currency ?? "EUR",
enableRevolutPayment: userSettings?.enableRevolutPayment ?? false,
ownerRevolutProfileName: userSettings?.ownerRevolutProfileName ?? "",
showPaymentInstructions: userSettings?.showPaymentInstructionsInMonthlyStatement ?? "disabled",
});
// https://revolut.me/aderezic?currency=EUR&amount=70000
const handleInputChange = (field: keyof typeof formValues, value: string) => {
const handleInputChange = (field: keyof typeof formValues, value: string | boolean) => {
setFormValues(prev => ({ ...prev, [field]: value }));
};
@@ -106,39 +108,38 @@ const FormFields: FC<FormFieldsProps> = ({ userSettings, errors, message }) => {
</div>
</div>
</fieldset>
<fieldset className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 pb-2 mt-4">
<legend className="fieldset-legend font-semibold uppercase">{t("tenant-payment-instructions--legend")}</legend>
<legend className="fieldset-legend font-semibold uppercase">{t("iban-payment-instructions--legend")}</legend>
<InfoBox className="p-1 mb-1">{t("info-box-message")}</InfoBox>
<InfoBox className="p-1 mb-1">{t("iban-payment-instructions--intro-message")}</InfoBox>
<fieldset className="form-control w-full">
<select
id="showPaymentInstructions"
name="showPaymentInstructions"
className="select select-bordered w-full"
defaultValue={formValues.showPaymentInstructions}
onChange={(e) => handleInputChange("showPaymentInstructions", e.target.value)}
disabled={pending}
>
<option value="disabled">{t("tenant-payment-instructions--show-no-instructions")}</option>
<option value="iban">{t("tenant-payment-instructions--show-iban-instructions")}</option>
<option value="revolut">{t("tenant-payment-instructions--show-revolut-instructions")}</option>
</select>
<fieldset className="fieldset">
<label className="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
name="enableIbanPayment"
className="toggle toggle-primary"
checked={formValues.enableIbanPayment}
onChange={(e) => handleInputChange("enableIbanPayment", e.target.checked)}
/>
<legend className="fieldset-legend">{t("iban-payment-instructions--toggle-label")}</legend>
</label>
</fieldset>
{formValues.showPaymentInstructions === "iban" && (
{ formValues.enableIbanPayment ? (
<>
<div className="divider mt-6 mb-2 font-bold uppercase">Informacije za uplatu</div>
<div className="divider mt-2 mb-2 font-bold uppercase">{t("iban-form-title")}</div>
<div className="form-control w-full">
<label className="label">
<span className="label-text">{t("owner-name-label")}</span>
<span className="label-text">{t("iban-owner-name-label")}</span>
</label>
<input
id="ownerName"
name="ownerName"
type="text"
maxLength={25}
placeholder={t("owner-name-placeholder")}
placeholder={t("iban-owner-name-placeholder")}
className="input input-bordered w-full placeholder:text-gray-600"
defaultValue={formValues.ownerName}
onChange={(e) => handleInputChange("ownerName", e.target.value)}
@@ -156,14 +157,14 @@ const FormFields: FC<FormFieldsProps> = ({ userSettings, errors, message }) => {
<div className="form-control w-full">
<label className="label">
<span className="label-text">{t("owner-street-label")} </span>
<span className="label-text">{t("iban-owner-street-label")} </span>
</label>
<input
id="ownerStreet"
name="ownerStreet"
type="text"
maxLength={25}
placeholder={t("owner-street-placeholder")}
placeholder={t("iban-owner-street-placeholder")}
className="input input-bordered w-full placeholder:text-gray-600"
defaultValue={formValues.ownerStreet}
onChange={(e) => handleInputChange("ownerStreet", e.target.value)}
@@ -181,14 +182,14 @@ const FormFields: FC<FormFieldsProps> = ({ userSettings, errors, message }) => {
<div className="form-control w-full">
<label className="label">
<span className="label-text">{t("owner-town-label")}</span>
<span className="label-text">{t("iban-owner-town-label")}</span>
</label>
<input
id="ownerTown"
name="ownerTown"
type="text"
maxLength={27}
placeholder={t("owner-town-placeholder")}
placeholder={t("iban-owner-town-placeholder")}
className="input input-bordered w-full placeholder:text-gray-600"
defaultValue={formValues.ownerTown}
onChange={(e) => handleInputChange("ownerTown", e.target.value)}
@@ -206,13 +207,13 @@ const FormFields: FC<FormFieldsProps> = ({ userSettings, errors, message }) => {
<div className="form-control w-full">
<label className="label">
<span className="label-text">{t("owner-iban-label")}</span>
<span className="label-text">{t("iban-owner-iban-label")}</span>
</label>
<input
id="ownerIBAN"
name="ownerIBAN"
type="text"
placeholder={t("owner-iban-placeholder")}
placeholder={t("iban-owner-iban-placeholder")}
className="input input-bordered w-full placeholder:text-gray-600"
defaultValue={formValues.ownerIBAN}
onChange={(e) => handleInputChange("ownerIBAN", e.target.value)}
@@ -229,21 +230,68 @@ const FormFields: FC<FormFieldsProps> = ({ userSettings, errors, message }) => {
</div>
<NoteBox className="p-1 mt-1">{t("payment-additional-notes")}</NoteBox>
</>
)}
{formValues.showPaymentInstructions === "revolut" && (
</>
) : // ELSE include hidden inputs to preserve existing values
<>
<div className="divider mt-6 mb-2 font-bold uppercase">Informacije za uplatu</div>
<input
id="ownerName"
name="ownerName"
type="hidden"
value={formValues.ownerName}
/>
<input
id="ownerStreet"
name="ownerStreet"
type="hidden"
value={formValues.ownerStreet}
/>
<input
id="ownerTown"
name="ownerTown"
type="hidden"
defaultValue={formValues.ownerTown}
/>
<input
id="ownerIBAN"
name="ownerIBAN"
type="hidden"
defaultValue={formValues.ownerIBAN}
/>
</>
}
</fieldset>
<fieldset className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4 pb-2 mt-4">
<legend className="fieldset-legend font-semibold uppercase">{t("revolut-payment-instructions--legend")}</legend>
<InfoBox className="p-1 mb-1">{t("revolut-payment-instructions--intro-message")}</InfoBox>
<fieldset className="fieldset">
<label className="label cursor-pointer justify-start gap-3">
<input
type="checkbox"
name="enableRevolutPayment"
className="toggle toggle-primary"
checked={formValues.enableRevolutPayment}
onChange={(e) => handleInputChange("enableRevolutPayment", e.target.checked)}
/>
<legend className="fieldset-legend">{t("revolut-payment-instructions--toggle-label")}</legend>
</label>
</fieldset>
{ formValues.enableRevolutPayment ? (
<>
<div className="divider mt-2 mb-2 font-bold uppercase">{t("revolut-form-title")}</div>
<div className="form-control w-full">
<label className="label">
<span className="label-text">{t("owner-revolut-link-label")}</span>
<span className="label-text">{t("revolut-profile-label")}</span>
</label>
<input
id="ownerRevolutProfileName"
name="ownerRevolutProfileName"
type="text"
maxLength={25}
placeholder={t("owner-revolut-link-placeholder")}
placeholder={t("revolut-profile-placeholder")}
className="input input-bordered w-full placeholder:text-gray-600"
defaultValue={formValues.ownerRevolutProfileName}
onChange={(e) => handleInputChange("ownerRevolutProfileName", e.target.value)}
@@ -260,7 +308,17 @@ const FormFields: FC<FormFieldsProps> = ({ userSettings, errors, message }) => {
</div>
<NoteBox className="p-1 mt-1">{t("payment-additional-notes")}</NoteBox>
</>
)}
)
: // ELSE include hidden input to preserve existing value
<>
<input
id="ownerRevolutProfileName"
name="ownerRevolutProfileName"
type="hidden"
value={formValues.ownerRevolutProfileName}
/>
</>
}
</fieldset>
<div id="general-error" aria-live="polite" aria-atomic="true">

View File

@@ -79,7 +79,7 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
const { hub3aText, paymentParams } = useMemo(() => {
if(!userSettings?.show2dCodeInMonthlyStatement || !generateTenantCode) {
if(!userSettings?.enableIbanPayment || !generateTenantCode) {
return {
hub3aText: "",
paymentParams: {} as PaymentParams
@@ -107,7 +107,7 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
hub3aText: EncodePayment(paymentParams),
paymentParams
});
}, [userSettings?.show2dCodeInMonthlyStatement, generateTenantCode, locationName, tenantName, tenantStreet, tenantTown, userSettings, monthlyExpense, yearMonth]);
}, []);
return(
<div data-key={_id } className="card card-compact card-bordered max-w-[30em] min-w-[330px] bg-base-100 border-1 border-neutral my-1">
@@ -126,7 +126,7 @@ export const ViewLocationCard:FC<ViewLocationCardProps> = ({location, userSettin
: null
}
{
userSettings?.show2dCodeInMonthlyStatement && generateTenantCode ?
userSettings?.enableIbanPayment && generateTenantCode ?
<>
<p className="max-w-[25em] ml-1 mt-1 mb-1">{t("payment-info-header")}</p>
<ul className="ml-4 mb-3">