From 686bec6c103bbe0dccfac36c83f8119c895df2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 13:37:54 +0100 Subject: [PATCH 01/16] feat: add Revolut payment link support alongside IBAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add NoteBox component for displaying warning messages with icon - Add Revolut profile name field to user settings schema - Update UserSettingsForm to support payment instruction selection (disabled/IBAN/Revolut) - Add Croatian and English translations for new payment options - Reserve fields for future per-instruction enable/disable functionality 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/lib/db-types.ts | 10 +++++ app/ui/NoteBox.tsx | 7 +++ app/ui/UserSettingsForm.tsx | 87 ++++++++++++++++++++++++------------- messages/en.json | 16 ++++++- messages/hr.json | 17 +++++--- package-lock.json | 21 --------- 6 files changed, 101 insertions(+), 57 deletions(-) create mode 100644 app/ui/NoteBox.tsx diff --git a/app/lib/db-types.ts b/app/lib/db-types.ts index db92e21..e5f7e0a 100644 --- a/app/lib/db-types.ts +++ b/app/lib/db-types.ts @@ -28,8 +28,18 @@ export interface UserSettings { ownerIBAN?: string | null; /** currency (ISO 4217) */ currency?: string | 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 */ diff --git a/app/ui/NoteBox.tsx b/app/ui/NoteBox.tsx new file mode 100644 index 0000000..ce60aa8 --- /dev/null +++ b/app/ui/NoteBox.tsx @@ -0,0 +1,7 @@ +import { FC, ReactNode } from "react"; + +export const NoteBox: FC<{ children: ReactNode, className?: string }> = ({ children, className }) => +
+ ⚠️ + {children} +
\ No newline at end of file diff --git a/app/ui/UserSettingsForm.tsx b/app/ui/UserSettingsForm.tsx index 95a12fa..898191d 100644 --- a/app/ui/UserSettingsForm.tsx +++ b/app/ui/UserSettingsForm.tsx @@ -9,6 +9,7 @@ import Link from "next/link"; import SettingsIcon from "@mui/icons-material/Settings"; import { formatIban } from "../lib/formatStrings"; import { InfoBox } from "./InfoBox"; +import { NoteBox } from "./NoteBox"; export type UserSettingsFormProps = { userSettings: UserSettings | null; @@ -32,21 +33,16 @@ const FormFields: FC = ({ userSettings, errors, message }) => { ownerTown: userSettings?.ownerTown ?? "", ownerIBAN: formatIban(userSettings?.ownerIBAN) ?? "", currency: userSettings?.currency ?? "EUR", + ownerRevolutProfileName: userSettings?.ownerRevolutProfileName ?? "", + showPaymentInstructions: userSettings?.showPaymentInstructionsInMonthlyStatement ?? "disabled", }); + // https://revolut.me/aderezic?currency=EUR&amount=70000 + const handleInputChange = (field: keyof typeof formValues, value: string) => { setFormValues(prev => ({ ...prev, [field]: value })); }; - // Check if any required field is missing (clean IBAN of spaces for validation) - const cleanedOwnerIBAN = formValues.ownerIBAN.replace(/\s/g, ''); - const hasMissingData = !formValues.ownerName || !formValues.ownerStreet || !formValues.ownerTown || !cleanedOwnerIBAN || !formValues.currency; - - // Track whether to generate 2D code for tenant (use persisted value from database) - const [show2dCodeInMonthlyStatement, setShow2dCodeInMonthlyStatement] = useState( - userSettings?.show2dCodeInMonthlyStatement ?? false - ); - return ( <>
@@ -60,7 +56,7 @@ const FormFields: FC = ({ userSettings, errors, message }) => { id="currency" name="currency" className="select select-bordered w-full" - defaultValue={userSettings?.currency ?? "EUR"} + defaultValue={formValues.currency} onChange={(e) => handleInputChange("currency", e.target.value)} disabled={pending} > @@ -111,25 +107,28 @@ const FormFields: FC = ({ userSettings, errors, message }) => {
- {t("tenant-2d-code-legend")} + {t("tenant-payment-instructions--legend")} {t("info-box-message")} -
- +
+
- - {show2dCodeInMonthlyStatement && ( + {formValues.showPaymentInstructions === "iban" && ( <> +
Informacije za uplatu
- {t("additional-notes")} + {t("payment-additional-notes")} )} + {formValues.showPaymentInstructions === "revolut" && ( + <> +
Informacije za uplatu
+
+ + handleInputChange("ownerRevolutProfileName", e.target.value)} + disabled={pending} + /> +
+ {errors?.ownerRevolutProfileName && + errors.ownerRevolutProfileName.map((error: string) => ( +

+ {error} +

+ ))} +
+
+ {t("payment-additional-notes")} + + )}
diff --git a/messages/en.json b/messages/en.json index 087e2fd..d770e94 100644 --- a/messages/en.json +++ b/messages/en.json @@ -194,6 +194,13 @@ "info-box-message": "By activating this option, a 2D barcode will be included in the monthly statement sent to the tenant, allowing them to make a direct payment to your bank account.", "tenant-2d-code-legend": "TENANT 2D CODE", "tenant-2d-code-toggle-label": "include 2D code in monthly statements", + + "tenant-payment-instructions--legend": "Payment Instructions", + "tenant-payment-instructions--info": "If you enable this option, the monthly statement sent to the tenant will include payment instructions and a 2D barcode, allowing them to make a direct payment to your bank account.", + "tenant-payment-instructions--show-no-instructions": "🚫 - do not show payment instructions", + "tenant-payment-instructions--show-iban-instructions": "🏦 - show payment instructions for IBAN", + "tenant-payment-instructions--show-revolut-instructions": "🆁 - show payment instructions for Revolut", + "owner-name-label": "Your First and Last Name", "owner-name-placeholder": "enter your first and last name", "owner-street-label": "Your Street and House Number", @@ -201,7 +208,12 @@ "owner-town-label": "Your Postal Code and Town", "owner-town-placeholder": "enter your postal code and town", "owner-iban-label": "IBAN", - "owner-iban-placeholder": "enter your IBAN", + "owner-iban-placeholder": "enter your IBAN for receiving payments", + + "owner-revolut-profile-label": "Revolut profile name", + "owner-revolut-profile-placeholder": "enter your Revolut profile name for receiving payments", + "owner-revolut-profile-tooltip": "You can find your Revolut profile name in the Revolut app under your user profile. It is displayed below your name and starts with the '@' symbol (e.g., '@john123').", + "general-settings-legend": "General Settings", "currency-label": "Currency", "save-button": "Save", @@ -215,6 +227,6 @@ "currency-required": "Currency is mandatory", "validation-failed": "Validation failed. Please check the form and try again." }, - "additional-notes": "Note: For the 2D code to be displayed, you must enter both the tenant's first and last names in the settings of each property for which you want to use this functionality." + "payment-additional-notes": "IMPORTANT: For the payment instructions to be displayed to the tenant, you must also enable this option in the property's settings." } } \ No newline at end of file diff --git a/messages/hr.json b/messages/hr.json index 047c1e7..285d9ac 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -190,9 +190,12 @@ }, "user-settings-form": { "title": "Korisničke postavke", - "info-box-message": "Ako uključite ovu opciji na mjesečnom obračunu koji se šalje podstanaru biti će prikazan 2D bar kod, putem kojeg će moći izvršiti izravnu uplatu na vaš bankovni račun.", - "tenant-2d-code-legend": "2D BARKOD ZA PODSTANARA", - "tenant-2d-code-toggle-label": "prikazuj 2D barkod u mjesečnom obračunu", + "info-box-message": "Ako uključite ovu opciju na mjesečnom obračunu koji se šalje podstanaru biti će prikazane upute za uplatu i 2D bar kod, putem kojeg će moći izvršiti izravnu uplatu na vaš bankovni račun.", + "tenant-payment-instructions--legend": "Upute za uplatu", + "tenant-payment-instructions--info": "Ako uključite ovu opciju na mjesečnom obračunu koji se šalje podstanaru biti će prikazane upute za uplatu i 2D bar kod, putem kojeg će moći izvršiti izravnu uplatu na vaš bankovni račun.", + "tenant-payment-instructions--show-no-instructions": "🚫 - Ne prikazivati upute za uplatu", + "tenant-payment-instructions--show-iban-instructions": "🏦 - Prikazuj upute za uplatu na IBAN", + "tenant-payment-instructions--show-revolut-instructions": "🆁 - Prikazuj upute za uplatu na Revolut", "owner-name-label": "Vaše ime i prezime", "owner-name-placeholder": "unesite svoje ime i prezime", "owner-street-label": "Ulica i kućni broj", @@ -200,7 +203,11 @@ "owner-town-label": "Poštanski broj i Grad", "owner-town-placeholder": "unesite poštanski broj i grad", "owner-iban-label": "IBAN", - "owner-iban-placeholder": "unesite svoj IBAN", + "owner-iban-placeholder": "IBAN putem kojeg ćete primate uplate", + "owner-revolut-profile-label": "Naziv vašeg Revolut profila", + "owner-revolut-profile-placeholder": "profil putem kojeg ćete primati uplate", + "owner-revolut-profile-tooltip": "Naziv vašeg Revolute profila možete pronaći u aplikaciji Revolut u korisničkom profilu. Prikazan je ispod vašeg imena i prezimena - počinje sa znakom '@' (npr: '@ivan123').", + "general-settings-legend": "Opće postavke", "currency-label": "Valuta", "save-button": "Spremi", @@ -214,6 +221,6 @@ "currency-required": "Valuta je obavezna", "validation-failed": "Validacija nije uspjela. Molimo provjerite formu i pokušajte ponovno." }, - "additional-notes": "Napomena: da bi 2D koda bio prikazan, morate unijeti i ime i prezime podstanara u postavkama svake nekretnine za koju želite koristiti ovu funkcionalnost." + "payment-additional-notes": "VAŽNO: da bi upute za uplatu bile prikazane podstanaru, morate tu ovu opciju uključiti i u postavkama pripadajuće nekretnine." } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1a94b78..8562202 100644 --- a/package-lock.json +++ b/package-lock.json @@ -147,7 +147,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", @@ -501,7 +500,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -545,7 +543,6 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1072,7 +1069,6 @@ "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.5.tgz", "integrity": "sha512-8VVxFmp1GIm9PpmnQoCoYo0UWHoOrdA57tDL62vkpzEgvb/d71Wsbv4FRg7r1Gyx7PuSo0tflH34cdl/NvfHNQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "@mui/core-downloads-tracker": "^7.3.5", @@ -1473,7 +1469,6 @@ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.1.0.tgz", "integrity": "sha512-x4FavbNEeXx/baD/zC/SdrvkjSby8nBn8KcCREqk6UuwvwoAPZmaV8TFCAuo/cpovBRTIY67mHhe86MQQm/68Q==", "dev": true, - "peer": true, "dependencies": { "glob": "10.3.10" } @@ -1804,7 +1799,6 @@ "version": "18.2.21", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", - "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -1926,7 +1920,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -2226,7 +2219,6 @@ "version": "0.20.0", "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.20.0.tgz", "integrity": "sha512-6Ev6rcqVjMakZFIDvbUf0dtpPGeZMTfyxYg4HkVWioWeN7cRcnUWT3bU6sdohc82O1nPXcjq6WiGfXX2Pnit6A==", - "peer": true, "dependencies": { "ts-custom-error": "^3.2.1" }, @@ -2253,7 +2245,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2673,7 +2664,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -3352,7 +3342,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3548,7 +3537,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, - "peer": true, "dependencies": { "array-includes": "^3.1.7", "array.prototype.findlastindex": "^1.2.3", @@ -5988,7 +5976,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-14.2.33.tgz", "integrity": "sha512-GiKHLsD00t4ACm1p00VgrI0rUFAC9cRDGReKyERlM57aeEZkOQGcZTpIbsGn0b562FTPJWmYfKwplfO9EaT6ng==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "14.2.33", "@swc/helpers": "0.5.5", @@ -6506,7 +6493,6 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", - "peer": true, "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", @@ -6708,7 +6694,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -6894,7 +6879,6 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -6923,7 +6907,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7065,7 +7048,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -7077,7 +7059,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -8002,7 +7983,6 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -8308,7 +8288,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" From 632f8888b55c57c8ab767773099206a65c8cc9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 14:45:59 +0100 Subject: [PATCH 02/16] refactor: replace payment dropdown with independent toggles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- app/lib/actions/userSettingsActions.ts | 35 ++++--- app/lib/db-types.ts | 14 +-- app/ui/UserSettingsForm.tsx | 128 ++++++++++++++++++------- app/ui/ViewLocationCard.tsx | 6 +- messages/en.json | 45 +++++---- messages/hr.json | 41 ++++---- 6 files changed, 168 insertions(+), 101 deletions(-) diff --git a/app/lib/actions/userSettingsActions.ts b/app/lib/actions/userSettingsActions.ts index b767e79..62e175c 100644 --- a/app/lib/actions/userSettingsActions.ts +++ b/app/lib/actions/userSettingsActions.ts @@ -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") diff --git a/app/lib/db-types.ts b/app/lib/db-types.ts index e5f7e0a..7196b99 100644 --- a/app/lib/db-types.ts +++ b/app/lib/db-types.ts @@ -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 */ diff --git a/app/ui/UserSettingsForm.tsx b/app/ui/UserSettingsForm.tsx index 898191d..4233ecd 100644 --- a/app/ui/UserSettingsForm.tsx +++ b/app/ui/UserSettingsForm.tsx @@ -28,18 +28,20 @@ const FormFields: FC = ({ 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 = ({ userSettings, errors, message }) => {
+
- {t("tenant-payment-instructions--legend")} + {t("iban-payment-instructions--legend")} - {t("info-box-message")} + {t("iban-payment-instructions--intro-message")} -
- +
+
- {formValues.showPaymentInstructions === "iban" && ( + + { formValues.enableIbanPayment ? ( <> -
Informacije za uplatu
+
{t("iban-form-title")}
handleInputChange("ownerName", e.target.value)} @@ -156,14 +157,14 @@ const FormFields: FC = ({ userSettings, errors, message }) => {
handleInputChange("ownerStreet", e.target.value)} @@ -181,14 +182,14 @@ const FormFields: FC = ({ userSettings, errors, message }) => {
handleInputChange("ownerTown", e.target.value)} @@ -206,13 +207,13 @@ const FormFields: FC = ({ userSettings, errors, message }) => {
handleInputChange("ownerIBAN", e.target.value)} @@ -229,21 +230,68 @@ const FormFields: FC = ({ userSettings, errors, message }) => {
{t("payment-additional-notes")} - - )} - {formValues.showPaymentInstructions === "revolut" && ( + + ) : // ELSE include hidden inputs to preserve existing values <> -
Informacije za uplatu
+ + + + + + } +
+ +
+ {t("revolut-payment-instructions--legend")} + + {t("revolut-payment-instructions--intro-message")} + +
+ +
+ + { formValues.enableRevolutPayment ? ( + <> +
{t("revolut-form-title")}
handleInputChange("ownerRevolutProfileName", e.target.value)} @@ -260,7 +308,17 @@ const FormFields: FC = ({ userSettings, errors, message }) => {
{t("payment-additional-notes")} - )} + ) + : // ELSE include hidden input to preserve existing value + <> + + + }
diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index bede467..b2eb33c 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -79,7 +79,7 @@ export const ViewLocationCard:FC = ({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 = ({location, userSettin hub3aText: EncodePayment(paymentParams), paymentParams }); - }, [userSettings?.show2dCodeInMonthlyStatement, generateTenantCode, locationName, tenantName, tenantStreet, tenantTown, userSettings, monthlyExpense, yearMonth]); + }, []); return(
@@ -126,7 +126,7 @@ export const ViewLocationCard:FC = ({location, userSettin : null } { - userSettings?.show2dCodeInMonthlyStatement && generateTenantCode ? + userSettings?.enableIbanPayment && generateTenantCode ? <>

{t("payment-info-header")}

    diff --git a/messages/en.json b/messages/en.json index d770e94..61ff01e 100644 --- a/messages/en.json +++ b/messages/en.json @@ -191,28 +191,32 @@ }, "user-settings-form": { "title": "User settings", - "info-box-message": "By activating this option, a 2D barcode will be included in the monthly statement sent to the tenant, allowing them to make a direct payment to your bank account.", - "tenant-2d-code-legend": "TENANT 2D CODE", - "tenant-2d-code-toggle-label": "include 2D code in monthly statements", - "tenant-payment-instructions--legend": "Payment Instructions", - "tenant-payment-instructions--info": "If you enable this option, the monthly statement sent to the tenant will include payment instructions and a 2D barcode, allowing them to make a direct payment to your bank account.", - "tenant-payment-instructions--show-no-instructions": "🚫 - do not show payment instructions", - "tenant-payment-instructions--show-iban-instructions": "🏦 - show payment instructions for IBAN", - "tenant-payment-instructions--show-revolut-instructions": "🆁 - show payment instructions for Revolut", + "iban-payment-instructions--legend": "Payment to Your IBAN", + "iban-payment-instructions--intro-message": "By activating this option, payment instructions will be included in the monthly statement sent to the tenant, allowing a direct payment via IBAN to be made to your bank account.", + "iban-payment-instructions--toggle-label": "enable IBAN payment instructions", - "owner-name-label": "Your First and Last Name", - "owner-name-placeholder": "enter your first and last name", - "owner-street-label": "Your Street and House Number", - "owner-street-placeholder": "enter your street and house number", - "owner-town-label": "Your Postal Code and Town", - "owner-town-placeholder": "enter your postal code and town", - "owner-iban-label": "IBAN", - "owner-iban-placeholder": "enter your IBAN for receiving payments", + "iban-form-title": "Payment Information for IBAN", + "iban-owner-name-label": "Your First and Last Name", + "iban-owner-name-placeholder": "enter your first and last name", + "iban-owner-street-label": "Your Street and House Number", + "iban-owner-street-placeholder": "enter your street and house number", + "iban-owner-town-label": "Your Postal Code and Town", + "iban-owner-town-placeholder": "enter your postal code and town", + "iban-owner-iban-label": "IBAN", + "iban-owner-iban-placeholder": "enter your IBAN for receiving payments", - "owner-revolut-profile-label": "Revolut profile name", - "owner-revolut-profile-placeholder": "enter your Revolut profile name for receiving payments", - "owner-revolut-profile-tooltip": "You can find your Revolut profile name in the Revolut app under your user profile. It is displayed below your name and starts with the '@' symbol (e.g., '@john123').", + + "revolut-form-title": "Payment Information for Revolut", + "revolut-payment-instructions--legend": "Payment to Your Revolut Profile", + "revolut-payment-instructions--intro-message": "By activating this option, payment instructions will be included in the monthly statement sent to the tenant, allowing a direct payment via Revolut to be made to your Revolut profile.", + "revolut-payment-instructions--toggle-label": "enable Revolut payment instructions", + + "revolut-profile-label": "Revolut profile name", + "revolut-profile-placeholder": "enter your Revolut profile name for receiving payments", + "revolut-profile-tooltip": "You can find your Revolut profile name in the Revolut app under your user profile. It is displayed below your name and starts with the '@' symbol (e.g., '@john123').", + + "payment-additional-notes": "IMPORTANT: For the payment instructions to be displayed to the tenant, you must also enable this option in the property's settings.", "general-settings-legend": "General Settings", "currency-label": "Currency", @@ -226,7 +230,6 @@ "owner-iban-invalid": "Invalid IBAN format. Please enter a valid IBAN", "currency-required": "Currency is mandatory", "validation-failed": "Validation failed. Please check the form and try again." - }, - "payment-additional-notes": "IMPORTANT: For the payment instructions to be displayed to the tenant, you must also enable this option in the property's settings." + } } } \ No newline at end of file diff --git a/messages/hr.json b/messages/hr.json index 285d9ac..43fd3ef 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -190,23 +190,30 @@ }, "user-settings-form": { "title": "Korisničke postavke", - "info-box-message": "Ako uključite ovu opciju na mjesečnom obračunu koji se šalje podstanaru biti će prikazane upute za uplatu i 2D bar kod, putem kojeg će moći izvršiti izravnu uplatu na vaš bankovni račun.", - "tenant-payment-instructions--legend": "Upute za uplatu", - "tenant-payment-instructions--info": "Ako uključite ovu opciju na mjesečnom obračunu koji se šalje podstanaru biti će prikazane upute za uplatu i 2D bar kod, putem kojeg će moći izvršiti izravnu uplatu na vaš bankovni račun.", - "tenant-payment-instructions--show-no-instructions": "🚫 - Ne prikazivati upute za uplatu", - "tenant-payment-instructions--show-iban-instructions": "🏦 - Prikazuj upute za uplatu na IBAN", - "tenant-payment-instructions--show-revolut-instructions": "🆁 - Prikazuj upute za uplatu na Revolut", - "owner-name-label": "Vaše ime i prezime", - "owner-name-placeholder": "unesite svoje ime i prezime", - "owner-street-label": "Ulica i kućni broj", - "owner-street-placeholder": "unesite ulicu i kućni broj", - "owner-town-label": "Poštanski broj i Grad", - "owner-town-placeholder": "unesite poštanski broj i grad", - "owner-iban-label": "IBAN", - "owner-iban-placeholder": "IBAN putem kojeg ćete primate uplate", - "owner-revolut-profile-label": "Naziv vašeg Revolut profila", - "owner-revolut-profile-placeholder": "profil putem kojeg ćete primati uplate", - "owner-revolut-profile-tooltip": "Naziv vašeg Revolute profila možete pronaći u aplikaciji Revolut u korisničkom profilu. Prikazan je ispod vašeg imena i prezimena - počinje sa znakom '@' (npr: '@ivan123').", + + + "iban-payment-instructions--legend": "Uplata na vaš IBAN", + "iban-payment-instructions--intro-message": "Aktiviranjem ove opcije, upute za uplatu bit će uključene u mjesečni izvještaj poslan podstanaru, omogućujući im da izvrše izravnu uplatu putem IBAN-a na vaš bankovni račun.", + "iban-payment-instructions--toggle-label": "uključi IBAN uplatu", + + "iban-form-title": "Informacije za uplatu na IBAN", + "iban-owner-name-label": "Vaše ime i prezime", + "iban-owner-name-placeholder": "unesite svoje ime i prezime", + "iban-owner-street-label": "Ulica i kućni broj", + "iban-owner-street-placeholder": "unesite ulicu i kućni broj", + "iban-owner-town-label": "Poštanski broj i Grad", + "iban-owner-town-placeholder": "unesite poštanski broj i grad", + "iban-owner-iban-label": "IBAN", + "iban-owner-iban-placeholder": "IBAN putem kojeg ćete primate uplate", + + "revolut-payment-instructions--legend": "Uplata na vaš Revolut profil", + "revolut-payment-instructions--intro-message": "Aktiviranjem ove opcije, upute za uplatu bit će uključene u mjesečni izvještaj poslan podstanaru, omogućujući im da izvrše izravnu uplatu putem Revolut-a na vaš profil.", + "revolut-payment-instructions--toggle-label": "uključi Revolut uplatu", + + "revolut-form-title": "Informacije za uplatu na Revolut", + "revolut-profile-label": "Naziv vašeg Revolut profila", + "revolut-profile-placeholder": "profil putem kojeg ćete primati uplate", + "revolut-profile-tooltip": "Naziv vašeg Revolut profila možete pronaći u aplikaciji Revolut u korisničkom profilu. Prikazan je ispod vašeg imena i prezimena - počinje sa znakom '@' (npr: '@ivan123').", "general-settings-legend": "Opće postavke", "currency-label": "Valuta", From ead74511706a32048553f2980a083b54e754ea72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 14:50:54 +0100 Subject: [PATCH 03/16] (refactor) LocationEditForm: renaming localization string names --- app/ui/LocationEditForm.tsx | 20 ++++++++++---------- messages/en.json | 23 +++++++++++++---------- messages/hr.json | 23 +++++++++++++---------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index 9d21193..3267c99 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -80,8 +80,8 @@ export const LocationEditForm: FC = ({ location, yearMont
- {t("tenant-2d-code-legend")} - {t("tenant-2d-code-info")} + {t("tenant-payment-instructions-legend")} + {t("tenant-payment-instructions-code-info")}
@@ -100,14 +100,14 @@ export const LocationEditForm: FC = ({ location, yearMont <>
handleTenantFieldChange("tenantName", e.target.value)} @@ -124,14 +124,14 @@ export const LocationEditForm: FC = ({ location, yearMont
handleTenantFieldChange("tenantStreet", e.target.value)} @@ -148,14 +148,14 @@ export const LocationEditForm: FC = ({ location, yearMont
handleTenantFieldChange("tenantTown", e.target.value)} @@ -169,7 +169,7 @@ export const LocationEditForm: FC = ({ location, yearMont ))}
- {t("tenant-2d-code-note")} + {t("tenant--payment-instructions-note")} )}
diff --git a/messages/en.json b/messages/en.json index 61ff01e..6e116ef 100644 --- a/messages/en.json +++ b/messages/en.json @@ -141,16 +141,19 @@ "location-name-legend": "Realestate name", "location-name-placeholder": "enter realestate name", "notes-placeholder": "notes", - "tenant-2d-code-legend": "PAYMENT INSTRUCTIONS", - "tenant-2d-code-info": "When the tenant opens the link to the statement for the given month, the application can show payment instructions for utility costs to your IBAN, as well as a 2D code they can scan.", - "tenant-2d-code-toggle-label": "show payment instructions to the tenant", - "tenant-2d-code-note": "IMPORTANT: for this to work you will also need to go into app settings and enter your name and IBAN.", - "tenant-name-label": "Tenant First and Last Name", - "tenant-name-placeholder": "enter tenant's first and last name", - "tenant-street-label": "Tenant Street and House Number", - "tenant-street-placeholder": "enter tenant's street", - "tenant-town-label": "Tenant Postal Code and Town", - "tenant-town-placeholder": "enter tenant's town", + + "tenant-payment-instructions-legend": "PAYMENT INSTRUCTIONS", + "tenant-payment-instructions-code-info": "When the tenant opens the link to the statement for the given month, the application can show payment instructions for utility costs to your IBAN, as well as a 2D code they can scan.", + "tenant-payment-instructions-toggle-label": "show payment instructions to the tenant", + "tenant--payment-instructions-note": "IMPORTANT: for this to work you will also need to go into app settings and enter your name and IBAN.", + + "iban-payment--tenant-name-label": "Tenant First and Last Name", + "iban-payment--tenant-name-placeholder": "enter tenant's first and last name", + "iban-payment--tenant-street-label": "Tenant Street and House Number", + "iban-payment--tenant-street-placeholder": "enter tenant's street", + "iban-payment--tenant-town-label": "Tenant Postal Code and Town", + "iban-payment--tenant-town-placeholder": "enter tenant's town", + "auto-utility-bill-forwarding-legend": "AUTOMATIC UTILITY BILL FORWARDING", "auto-utility-bill-forwarding-info": "This option enables automatic forwarding of utility bills to the tenant via email according to the selected forwarding strategy.", "auto-utility-bill-forwarding-toggle-label": "forward utility bills", diff --git a/messages/hr.json b/messages/hr.json index 43fd3ef..635ca3c 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -140,16 +140,19 @@ "location-name-legend": "Realestate name", "location-name-placeholder": "unesite naziv nekretnine", "notes-placeholder": "bilješke", - "tenant-2d-code-legend": "UPUTE ZA UPLATU", - "tenant-2d-code-info": "Kada podstanar otvori poveznicu na obračun za zadani mjesec aplikacija mu može prikazati upute za uplatu troškova režija na vaš IBAN, kao i 2D koji može skenirati.", - "tenant-2d-code-toggle-label": "podstanaru prikaži upute za uplatu", - "tenant-2d-code-note": "VAŽNO: za ovu funkcionalnost potrebno je otvoriti postavke aplikacije, te unijeti vaše ime i IBAN.", - "tenant-name-label": "Ime i prezime podstanara", - "tenant-name-placeholder": "unesite ime i prezime podstanara", - "tenant-street-label": "Ulica podstanara i kućni broj", - "tenant-street-placeholder": "unesite ulicu podstanara", - "tenant-town-label": "Poštanski broj i Grad podstanara", - "tenant-town-placeholder": "unesite poštanski broj i grad podstanara", + + "tenant-payment-instructions-legend": "UPUTE ZA UPLATU", + "tenant-payment-instructions-code-info": "Kada podstanar otvori poveznicu na obračun za zadani mjesec aplikacija mu može prikazati upute za uplatu troškova režija na vaš IBAN, kao i 2D koji može skenirati.", + "tenant-payment-instructions-toggle-label": "podstanaru prikaži upute za uplatu", + "tenant--payment-instructions-note": "VAŽNO: za ovu funkcionalnost potrebno je otvoriti postavke aplikacije, te unijeti vaše ime i IBAN.", + + "iban-payment--tenant-name-label": "Ime i prezime podstanara", + "iban-payment--tenant-name-placeholder": "unesite ime i prezime podstanara", + "iban-payment--tenant-street-label": "Ulica podstanara i kućni broj", + "iban-payment--tenant-street-placeholder": "unesite ulicu podstanara", + "iban-payment--tenant-town-label": "Poštanski broj i Grad podstanara", + "iban-payment--tenant-town-placeholder": "unesite poštanski broj i grad podstanara", + "auto-utility-bill-forwarding-legend": "AUTOMATSKO PROSLJEĐIVANJE REŽIJA", "auto-utility-bill-forwarding-info": "Ova opcija omogućuje automatsko prosljeđivanje režija podstanaru putem emaila u skladu s odabranom strategijom.", "auto-utility-bill-forwarding-toggle-label": "proslijedi režije automatski", From 3e581d88788668bd94a9234305cfd2d97e0a2953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 15:34:59 +0100 Subject: [PATCH 04/16] refactor: replace generateTenantCode boolean with tenantPaymentMethod enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace generateTenantCode boolean field with tenantPaymentMethod enum ("none" | "iban" | "revolut") - Update LocationEditForm to use dropdown select instead of toggle for payment method selection - Consolidate multiple useState hooks into single formValues state object - Change from defaultValue to controlled components with value/onChange pattern - Add hidden inputs to preserve tenant data when payment method is not selected - Update validation logic to check tenantPaymentMethod === "iban" - Update ViewLocationCard to use new tenantPaymentMethod field - Add Croatian translations for new dropdown options This provides better scalability for adding future payment methods and improves form state management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/lib/actions/locationActions.ts | 25 +++--- app/lib/db-types.ts | 6 +- app/ui/LocationEditForm.tsx | 132 +++++++++++++++++------------ app/ui/ViewLocationCard.tsx | 6 +- messages/hr.json | 13 ++- 5 files changed, 108 insertions(+), 74 deletions(-) diff --git a/app/lib/actions/locationActions.ts b/app/lib/actions/locationActions.ts index 369f178..cbe0e17 100644 --- a/app/lib/actions/locationActions.ts +++ b/app/lib/actions/locationActions.ts @@ -14,7 +14,6 @@ import { getTranslations, getLocale } from "next-intl/server"; export type State = { errors?: { locationName?: string[]; - generateTenantCode?: string[]; tenantName?: string[]; tenantStreet?: string[]; tenantTown?: string[]; @@ -35,7 +34,7 @@ export type State = { const FormSchema = (t:IntlTemplateFn) => z.object({ _id: z.string(), locationName: z.coerce.string().min(1, t("location-name-required")), - generateTenantCode: z.boolean().optional().nullable(), + tenantPaymentMethod: z.enum(["none", "iban", "revolut"]).optional().nullable(), tenantName: z.string().max(30).optional().nullable(), tenantStreet: z.string().max(27).optional().nullable(), tenantTown: z.string().max(27).optional().nullable(), @@ -50,9 +49,9 @@ const FormSchema = (t:IntlTemplateFn) => z.object({ }) // dont include the _id field in the response .omit({ _id: true }) - // Add conditional validation: if generateTenantCode is true, tenant fields are required + // Add conditional validation: if `tenantPaymentMethod` is "iban", tenant fields are required .refine((data) => { - if (data.generateTenantCode) { + if (data.tenantPaymentMethod === "iban") { return !!data.tenantName && data.tenantName.trim().length > 0; } return true; @@ -61,7 +60,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({ path: ["tenantName"], }) .refine((data) => { - if (data.generateTenantCode) { + if (data.tenantPaymentMethod === "iban") { return !!data.tenantStreet && data.tenantStreet.trim().length > 0; } return true; @@ -70,7 +69,7 @@ const FormSchema = (t:IntlTemplateFn) => z.object({ path: ["tenantStreet"], }) .refine((data) => { - if (data.generateTenantCode) { + if (data.tenantPaymentMethod === "iban") { return !!data.tenantTown && data.tenantTown.trim().length > 0; } return true; @@ -112,7 +111,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat const validatedFields = FormSchema(t).safeParse({ locationName: formData.get('locationName'), - generateTenantCode: formData.get('generateTenantCode') === 'on', + tenantPaymentMethod: formData.get('tenantPaymentMethod') as "none" | "iban" | "revolut" | undefined, tenantName: formData.get('tenantName') || null, tenantStreet: formData.get('tenantStreet') || null, tenantTown: formData.get('tenantTown') || null, @@ -136,7 +135,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat const { locationName, - generateTenantCode, + tenantPaymentMethod, tenantName, tenantStreet, tenantTown, @@ -178,7 +177,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat { $set: { name: locationName, - generateTenantCode: generateTenantCode || false, + tenantPaymentMethod: tenantPaymentMethod || "none", tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, @@ -208,7 +207,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat { $set: { name: locationName, - generateTenantCode: generateTenantCode || false, + tenantPaymentMethod: tenantPaymentMethod || "none", tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, @@ -231,7 +230,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat { $set: { name: locationName, - generateTenantCode: generateTenantCode || false, + tenantPaymentMethod: tenantPaymentMethod || "none", tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, @@ -253,7 +252,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat userEmail, name: locationName, notes: null, - generateTenantCode: generateTenantCode || false, + tenantPaymentMethod: tenantPaymentMethod || "none", tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, @@ -327,7 +326,7 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat userEmail, name: locationName, notes: null, - generateTenantCode: generateTenantCode || false, + tenantPaymentMethod: tenantPaymentMethod || "none", tenantName: tenantName || null, tenantStreet: tenantStreet || null, tenantTown: tenantTown || null, diff --git a/app/lib/db-types.ts b/app/lib/db-types.ts index 7196b99..330c832 100644 --- a/app/lib/db-types.ts +++ b/app/lib/db-types.ts @@ -51,8 +51,10 @@ export interface BillingLocation { bills: Bill[]; /** (optional) notes */ notes: string|null; - /** (optional) whether to generate 2D code for tenant */ - generateTenantCode?: boolean | null; + + /** (optional) method for showing payment instructions to tenant */ + tenantPaymentMethod?: "none" | "iban" | "revolut" | null; + /** (optional) tenant name */ tenantName?: string | null; /** (optional) tenant street */ diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index 3267c99..8525413 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -23,36 +23,30 @@ export type LocationEditFormProps = { export const LocationEditForm: FC = ({ location, yearMonth }) => { const initialState = { message: null, errors: {} }; + const handleAction = updateOrAddLocation.bind(null, location?._id, location?.yearMonth ?? yearMonth); + const [state, dispatch] = useFormState(handleAction, initialState); const t = useTranslations("location-edit-form"); const locale = useLocale(); - // Track whether to generate 2D code for tenant (use persisted value from database) - const [generateTenantCode, setGenerateTenantCode] = useState( - location?.generateTenantCode ?? false - ); - - // Track whether to automatically notify tenant (use persisted value from database) - const [autoBillFwd, setautoBillFwd] = useState( - location?.autoBillFwd ?? false - ); - - // Track whether to automatically send rent notification (use persisted value from database) - const [rentDueNotification, setrentDueNotification] = useState( - location?.rentDueNotification ?? false - ); - // Track tenant field values for real-time validation - const [tenantFields, setTenantFields] = useState({ + const [formValues, setFormValues] = useState({ + locationName: location?.name ?? "", tenantName: location?.tenantName ?? "", tenantStreet: location?.tenantStreet ?? "", tenantTown: location?.tenantTown ?? "", tenantEmail: location?.tenantEmail ?? "", + tenantPaymentMethod: location?.tenantPaymentMethod ?? "none", + autoBillFwd: location?.autoBillFwd ?? false, + billFwdStrategy: location?.billFwdStrategy ?? "when-payed", + rentDueNotification: location?.rentDueNotification ?? false, + rentAmount: location?.rentAmount ?? "", + rentDueDay: location?.rentDueDay ?? 1, }); - const handleTenantFieldChange = (field: keyof typeof tenantFields, value: string) => { - setTenantFields(prev => ({ ...prev, [field]: value })); + const handleInputChange = (field: keyof typeof formValues, value: string | boolean | number) => { + setFormValues(prev => ({ ...prev, [field]: value })); }; let { year, month } = location ? location.yearMonth : yearMonth; @@ -69,7 +63,14 @@ export const LocationEditForm: FC = ({ location, yearMont }
{t("location-name-legend")} - + handleInputChange("locationName", e.target.value)} + />
{state.errors?.locationName && state.errors.locationName.map((error: string) => ( @@ -79,25 +80,24 @@ export const LocationEditForm: FC = ({ location, yearMont ))}
+
{t("tenant-payment-instructions-legend")} - {t("tenant-payment-instructions-code-info")} + + {t("tenant-payment-instructions-code-info")} -
- +
+ {t("tenant-payment-instructions-method--legend")} +
- {generateTenantCode && ( + { formValues.tenantPaymentMethod === "iban" ? ( <> +
{t("iban-payment--form-title")}
- -
{t("auto-utility-bill-forwarding-legend")} {t("auto-utility-bill-forwarding-info")} @@ -186,17 +207,17 @@ export const LocationEditForm: FC = ({ location, yearMont type="checkbox" name="autoBillFwd" className="toggle toggle-primary" - checked={autoBillFwd} - onChange={(e) => setautoBillFwd(e.target.checked)} + checked={formValues.autoBillFwd} + onChange={(e) => handleInputChange("autoBillFwd", e.target.checked)} /> {t("auto-utility-bill-forwarding-toggle-label")}
- {autoBillFwd && ( + {formValues.autoBillFwd && (
{t("utility-bill-forwarding-strategy-label")} - @@ -215,18 +236,22 @@ export const LocationEditForm: FC = ({ location, yearMont type="checkbox" name="rentDueNotification" className="toggle toggle-primary" - checked={rentDueNotification} - onChange={(e) => setrentDueNotification(e.target.checked)} + checked={formValues.rentDueNotification} + onChange={(e) => handleInputChange("rentDueNotification", e.target.checked)} /> {t("auto-rent-notification-toggle-label")}
- {rentDueNotification && ( + {formValues.rentDueNotification && ( <>
{t("rent-due-day-label")} - handleInputChange("rentDueDay", parseInt(e.target.value,10)) + }> {Array.from({ length: 28 }, (_, i) => i + 1).map(day => ( ))} @@ -242,7 +267,8 @@ export const LocationEditForm: FC = ({ location, yearMont step="0.01" placeholder={t("rent-amount-placeholder")} className="input input-bordered w-full placeholder:text-gray-600 text-right" - defaultValue={location?.rentAmount ?? ""} + defaultValue={formValues.rentAmount} + onChange={(e) => handleInputChange("rentAmount", parseFloat(e.target.value))} />
{state.errors?.rentAmount && @@ -257,7 +283,7 @@ export const LocationEditForm: FC = ({ location, yearMont )}
- {(autoBillFwd || rentDueNotification) && ( + {(formValues.autoBillFwd || formValues.rentDueNotification) && (
{t("tenant-email-legend")} = ({ location, yearMont type="email" placeholder={t("tenant-email-placeholder")} className="input input-bordered w-full placeholder:text-gray-600" - defaultValue={location?.tenantEmail ?? ""} - onChange={(e) => handleTenantFieldChange("tenantEmail", e.target.value)} + defaultValue={formValues.tenantEmail} + onChange={(e) => handleInputChange("tenantEmail", e.target.value)} />
{state.errors?.tenantEmail && diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index b2eb33c..6eca737 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -27,7 +27,7 @@ export const ViewLocationCard:FC = ({location, userSettin tenantName, tenantStreet, tenantTown, - generateTenantCode, + tenantPaymentMethod, // NOTE: only the fileName is projected from the DB to reduce data transfer utilBillsProofOfPaymentAttachment, utilBillsProofOfPaymentUploadedAt, @@ -79,7 +79,7 @@ export const ViewLocationCard:FC = ({location, userSettin const { hub3aText, paymentParams } = useMemo(() => { - if(!userSettings?.enableIbanPayment || !generateTenantCode) { + if(!userSettings?.enableIbanPayment || tenantPaymentMethod !== "iban") { return { hub3aText: "", paymentParams: {} as PaymentParams @@ -126,7 +126,7 @@ export const ViewLocationCard:FC = ({location, userSettin : null } { - userSettings?.enableIbanPayment && generateTenantCode ? + userSettings?.enableIbanPayment && tenantPaymentMethod === "iban" ? <>

{t("payment-info-header")}

    diff --git a/messages/hr.json b/messages/hr.json index 635ca3c..edc862b 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -142,10 +142,17 @@ "notes-placeholder": "bilješke", "tenant-payment-instructions-legend": "UPUTE ZA UPLATU", - "tenant-payment-instructions-code-info": "Kada podstanar otvori poveznicu na obračun za zadani mjesec aplikacija mu može prikazati upute za uplatu troškova režija na vaš IBAN, kao i 2D koji može skenirati.", + "tenant-payment-instructions-code-info": "Kada podstanar otvori poveznicu na obračun za zadani mjesec aplikacija mu može prikazati upute za uplatu troškova režija na vaš IBAN ili Revolut.", + + "tenant-payment-instructions-method--legend": "Podstanaru prikaži upute za uplatu:", + "tenant-payment-instructions-method--none": "ne prikazuj upute za uplatu", + "tenant-payment-instructions-method--iban": "uplata na IBAN", + "tenant-payment-instructions-method--revolut": "uplata na Revolut", + "tenant-payment-instructions-toggle-label": "podstanaru prikaži upute za uplatu", "tenant--payment-instructions-note": "VAŽNO: za ovu funkcionalnost potrebno je otvoriti postavke aplikacije, te unijeti vaše ime i IBAN.", + "iban-payment--form-title": "Informacije za uplatu na IBAN", "iban-payment--tenant-name-label": "Ime i prezime podstanara", "iban-payment--tenant-name-placeholder": "unesite ime i prezime podstanara", "iban-payment--tenant-street-label": "Ulica podstanara i kućni broj", @@ -159,9 +166,9 @@ "utility-bill-forwarding-strategy-label": "Režije proslijedi kada...", "utility-bill-forwarding-when-payed": "sve stavke označim kao plaćene", "utility-bill-forwarding-when-attached": "za sve stavke priložim račun (PDF)", - "auto-rent-notification-legend": "AUTOMATSKA OBAVIJEST O NAJAMNINI", + "auto-rent-notification-legend": "Automatska obavjest O najamnini", "auto-rent-notification-info": "Ova opcija omogućuje automatsko slanje mjesečnog računa za najamninu podstanaru putem emaila na zadani dan u mjesecu.", - "auto-rent-notification-toggle-label": "pošalji obavijest o najamnini", + "auto-rent-notification-toggle-label": "pošalji obavjest o najamnini", "rent-due-day-label": "Dan u mjesecu kada dospijeva najamnina", "rent-amount-label": "Iznos najamnine", "rent-amount-placeholder": "unesite iznos najamnine", From c4c9b409d09ef1ea8cf20ad7a2598397e79ae4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 15:35:12 +0100 Subject: [PATCH 05/16] style: normalize English translation capitalization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change uppercase section headers to title case for consistency - "AUTOMATIC UTILITY BILL FORWARDING" → "Automatic utility bill forwarding" - "AUTOMATIC RENT NOTIFICATION" → "Automatic rent notification" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- messages/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messages/en.json b/messages/en.json index 6e116ef..f67ccb3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -154,13 +154,13 @@ "iban-payment--tenant-town-label": "Tenant Postal Code and Town", "iban-payment--tenant-town-placeholder": "enter tenant's town", - "auto-utility-bill-forwarding-legend": "AUTOMATIC UTILITY BILL FORWARDING", + "auto-utility-bill-forwarding-legend": "Automatic utility bill forwarding", "auto-utility-bill-forwarding-info": "This option enables automatic forwarding of utility bills to the tenant via email according to the selected forwarding strategy.", "auto-utility-bill-forwarding-toggle-label": "forward utility bills", "utility-bill-forwarding-strategy-label": "Forward utility bills when ...", "utility-bill-forwarding-when-payed": "all items are marked as paid", "utility-bill-forwarding-when-attached": "a bill (PDF) is attached to all items", - "auto-rent-notification-legend": "AUTOMATIC RENT NOTIFICATION", + "auto-rent-notification-legend": "Automatic rent notification", "auto-rent-notification-info": "This option enables automatic sending of monthly rent bill to the tenant via email on the specified day of the month.", "auto-rent-notification-toggle-label": "send rent notification", "rent-due-day-label": "Day of month when rent is due", From 4e9e5ca8d24db65127789aa708528e997484085c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 15:39:37 +0100 Subject: [PATCH 06/16] feat: add validation for Revolut profile name format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ownerRevolutProfileName field validation when enableRevolutPayment is true - Validate profile name must start with '@' and contain only letters and numbers - Add required field validation for Revolut profile name - Add English and Croatian error messages for validation failures - Update State type to include ownerRevolutProfileName errors Validation regex: /^@[a-zA-Z0-9]+$/ Valid examples: @john123, @ivan, @user2024 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/lib/actions/userSettingsActions.ts | 21 +++++++++++++++++++++ messages/en.json | 2 ++ messages/hr.json | 2 ++ 3 files changed, 25 insertions(+) diff --git a/app/lib/actions/userSettingsActions.ts b/app/lib/actions/userSettingsActions.ts index 62e175c..96560b8 100644 --- a/app/lib/actions/userSettingsActions.ts +++ b/app/lib/actions/userSettingsActions.ts @@ -19,6 +19,7 @@ export type State = { ownerTown?: string[]; ownerIBAN?: string[]; currency?: string[]; + ownerRevolutProfileName?: string[]; }; message?: string | null; success?: boolean; @@ -96,6 +97,26 @@ const FormSchema = (t: IntlTemplateFn) => z.object({ }, { message: t("currency-required"), path: ["currency"], +}) +.refine((data) => { + if (data.enableRevolutPayment) { + return !!data.ownerRevolutProfileName && data.ownerRevolutProfileName.trim().length > 0; + } + return true; +}, { + message: t("owner-revolut-profile-required"), + path: ["ownerRevolutProfileName"], +}) +.refine((data) => { + if (data.enableRevolutPayment && data.ownerRevolutProfileName) { + const profileName = data.ownerRevolutProfileName.trim(); + // Must start with @ and contain only English letters and numbers + return /^@[a-zA-Z0-9]+$/.test(profileName); + } + return true; +}, { + message: t("owner-revolut-profile-invalid"), + path: ["ownerRevolutProfileName"], }); /** diff --git a/messages/en.json b/messages/en.json index f67ccb3..0117b53 100644 --- a/messages/en.json +++ b/messages/en.json @@ -232,6 +232,8 @@ "owner-iban-required": "Valid IBAN is mandatory", "owner-iban-invalid": "Invalid IBAN format. Please enter a valid IBAN", "currency-required": "Currency is mandatory", + "owner-revolut-profile-required": "Revolut profile name is mandatory", + "owner-revolut-profile-invalid": "Invalid Revolut profile format. Must start with '@' and contain only English letters and numbers (e.g., '@john123')", "validation-failed": "Validation failed. Please check the form and try again." } } diff --git a/messages/hr.json b/messages/hr.json index edc862b..629a9f0 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -236,6 +236,8 @@ "owner-iban-required": "Ispravan IBAN je obavezan", "owner-iban-invalid": "Neispravan IBAN format. Molimo unesite ispravan IBAN.", "currency-required": "Valuta je obavezna", + "owner-revolut-profile-required": "Naziv Revolut profila je obavezan", + "owner-revolut-profile-invalid": "Neispravan format Revolut profila. Mora počinjati sa '@' i sadržavati samo slova i brojeve (npr. '@ivan123')", "validation-failed": "Validacija nije uspjela. Molimo provjerite formu i pokušajte ponovno." }, "payment-additional-notes": "VAŽNO: da bi upute za uplatu bile prikazane podstanaru, morate tu ovu opciju uključiti i u postavkama pripadajuće nekretnine." From 7c8497ccfe286c27b02527e900add38eb1c7619d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 15:45:19 +0100 Subject: [PATCH 07/16] feat: add Revolut payment link to ViewLocationCard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Display Revolut payment link when enableRevolutPayment is enabled and tenantPaymentMethod is "revolut" - Generate payment link with profile name (@ symbol removed), amount, currency, and reference - Convert amount from cents to main currency unit (divide by 100, fixed to 2 decimals) - Add placeholder for QR code (to be implemented) - Remove unused import (inspector) Payment link format: https://revolut.me/{profile}?amount={amount}¤cy={currency}&reference={text} 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/ui/ViewLocationCard.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index 6eca737..697cb9f 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -145,6 +145,26 @@ export const ViewLocationCard:FC = ({location, userSettin : null } + { + userSettings?.enableRevolutPayment && tenantPaymentMethod === "revolut" ? + <> +

    {t("payment-info-header")}

    +

    + To pay via Revolut, please click the following link:{' '} + + Pay with Revolut + +

    + + + : null + }
    {t("upload-proof-of-payment-legend")} { From bd283ce6db4cfc44bb49d075715f6b64f98c7032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 15:53:01 +0100 Subject: [PATCH 08/16] (refactor) removed "reference" from revolute URL --- app/ui/ViewLocationCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index 697cb9f..9a4793e 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -152,7 +152,7 @@ export const ViewLocationCard:FC = ({location, userSettin

    To pay via Revolut, please click the following link:{' '} From df9a3596f3c03ced7a3538922ea7324748564105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 16:21:28 +0100 Subject: [PATCH 09/16] feat: add Revolut profile tooltip and test link to UserSettingsForm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tooltip under Revolut profile input explaining where to find the profile name - Add test payment link that appears when profile name is valid (>5 chars) - Reorder ViewLocationCard payment UI: QR code placeholder first, link below centered - Replace hardcoded text with translation keys for better i18n support - Add English and Croatian translations for test link and payment button 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/ui/UserSettingsForm.tsx | 14 ++++++++++++++ app/ui/ViewLocationCard.tsx | 11 +++++------ messages/en.json | 5 ++++- messages/hr.json | 6 ++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/ui/UserSettingsForm.tsx b/app/ui/UserSettingsForm.tsx index 4233ecd..fb5eec2 100644 --- a/app/ui/UserSettingsForm.tsx +++ b/app/ui/UserSettingsForm.tsx @@ -297,6 +297,9 @@ const FormFields: FC = ({ userSettings, errors, message }) => { onChange={(e) => handleInputChange("ownerRevolutProfileName", e.target.value)} disabled={pending} /> +

    {errors?.ownerRevolutProfileName && errors.ownerRevolutProfileName.map((error: string) => ( @@ -305,6 +308,17 @@ const FormFields: FC = ({ userSettings, errors, message }) => {

    ))}
    + { + !errors?.ownerRevolutProfileName && formValues.ownerRevolutProfileName.length > 5 ? ( +

    {t("revolut-profile--test-link-label")} + {t("revolut-profile--test-link-text")} +

    + ) : null + }
{t("payment-additional-notes")} diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index 9a4793e..f51be74 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -149,19 +149,18 @@ export const ViewLocationCard:FC = ({location, userSettin userSettings?.enableRevolutPayment && tenantPaymentMethod === "revolut" ? <>

{t("payment-info-header")}

-

- To pay via Revolut, please click the following link:{' '} + +

- Pay with Revolut + {t("revolut-link-text")}

- : null } diff --git a/messages/en.json b/messages/en.json index 0117b53..cb2c0a0 100644 --- a/messages/en.json +++ b/messages/en.json @@ -70,7 +70,8 @@ "payment-purpose-code-label": "Purpose code:", "payment-description-label": "Payment description:", "upload-proof-of-payment-legend": "Proof of payment", - "upload-proof-of-payment-label": "Here you can upload proof of payment:" + "upload-proof-of-payment-label": "Here you can upload proof of payment:", + "revolut-link-text": "Pay with Revolut" }, "month-card": { "payed-total-label": "Total monthly expenditure:", @@ -218,6 +219,8 @@ "revolut-profile-label": "Revolut profile name", "revolut-profile-placeholder": "enter your Revolut profile name for receiving payments", "revolut-profile-tooltip": "You can find your Revolut profile name in the Revolut app under your user profile. It is displayed below your name and starts with the '@' symbol (e.g., '@john123').", + "revolut-profile--test-link-label": "Test your Revolut link:", + "revolut-profile--test-link-text": "Pay with Revolut", "payment-additional-notes": "IMPORTANT: For the payment instructions to be displayed to the tenant, you must also enable this option in the property's settings.", diff --git a/messages/hr.json b/messages/hr.json index 629a9f0..9b23b42 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -70,7 +70,8 @@ "payment-purpose-code-label": "Šifra namjene:", "payment-description-label": "Opis plaćanja:", "upload-proof-of-payment-legend": "Potvrda o uplati", - "upload-proof-of-payment-label": "Ovdje možete priložiti potvrdu o uplati:" + "upload-proof-of-payment-label": "Ovdje možete priložiti potvrdu o uplati:", + "revolut-link-text": "Plati pomoću Revoluta" }, "month-card": { "payed-total-label": "Ukupni mjesečni trošak:", @@ -200,7 +201,6 @@ }, "user-settings-form": { "title": "Korisničke postavke", - "iban-payment-instructions--legend": "Uplata na vaš IBAN", "iban-payment-instructions--intro-message": "Aktiviranjem ove opcije, upute za uplatu bit će uključene u mjesečni izvještaj poslan podstanaru, omogućujući im da izvrše izravnu uplatu putem IBAN-a na vaš bankovni račun.", @@ -224,6 +224,8 @@ "revolut-profile-label": "Naziv vašeg Revolut profila", "revolut-profile-placeholder": "profil putem kojeg ćete primati uplate", "revolut-profile-tooltip": "Naziv vašeg Revolut profila možete pronaći u aplikaciji Revolut u korisničkom profilu. Prikazan je ispod vašeg imena i prezimena - počinje sa znakom '@' (npr: '@ivan123').", + "revolut-profile--test-link-label": "Testiraj svoju Revolut poveznicu:", + "revolut-profile--test-link-text": "Plati pomoću Revoluta", "general-settings-legend": "Opće postavke", "currency-label": "Valuta", From 3ef9e411e90e5b9ad51d8fe0b4c917e51b26c956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 16:25:06 +0100 Subject: [PATCH 10/16] feat: add QR code for Revolut payment link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Install react-qr-code package for QR code generation - Replace placeholder with actual QR code component in ViewLocationCard - QR code displays Revolut payment URL with amount, currency, and profile - Center-aligned QR code (200x200px) in white container with border - Extract revolutPaymentUrl to single variable for reuse in QR code and link 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/ui/ViewLocationCard.tsx | 37 +++++++++++++++++++++---------------- package-lock.json | 18 ++++++++++++++++++ package.json | 1 + 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index f51be74..5d7c8ef 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -11,6 +11,7 @@ import { EncodePayment, PaymentParams } from "hub-3a-payment-encoder"; import Link from "next/link"; import { DocumentIcon } from "@heroicons/react/24/outline"; import { uploadUtilBillsProofOfPayment } from "../lib/actions/locationActions"; +import QRCode from "react-qr-code"; export interface ViewLocationCardProps { location: BillingLocation; @@ -146,22 +147,26 @@ export const ViewLocationCard:FC = ({location, userSettin : null } { - userSettings?.enableRevolutPayment && tenantPaymentMethod === "revolut" ? - <> -

{t("payment-info-header")}

- -

- - {t("revolut-link-text")} - -

- + userSettings?.enableRevolutPayment && tenantPaymentMethod === "revolut" ? (() => { + const revolutPaymentUrl = `https://revolut.me/${userSettings.ownerRevolutProfileName?.replace('@', '')}?amount=${(monthlyExpense / 100).toFixed(2)}¤cy=${userSettings.currency}}`; + return ( + <> +

{t("payment-info-header")}

+
+ +
+

+ + {t("revolut-link-text")} + +

+ + ); + })() : null }
diff --git a/package-lock.json b/package-lock.json index 8562202..87993f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-infinite-scroll-component": "^6.1.0", + "react-qr-code": "^2.0.18", "react-toastify": "^10.0.6", "tailwindcss": "^3.4.0", "typescript": "5.2.2", @@ -7025,6 +7026,11 @@ "node": ">=6" } }, + "node_modules/qr.js": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", + "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7083,6 +7089,18 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-qr-code": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.18.tgz", + "integrity": "sha512-v1Jqz7urLMhkO6jkgJuBYhnqvXagzceg3qJUWayuCK/c6LTIonpWbwxR1f1APGd4xrW/QcQEovNrAojbUz65Tg==", + "dependencies": { + "prop-types": "^15.8.1", + "qr.js": "0.0.0" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-toastify": { "version": "10.0.6", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", diff --git a/package.json b/package.json index e5d84ae..dc45df6 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-infinite-scroll-component": "^6.1.0", + "react-qr-code": "^2.0.18", "react-toastify": "^10.0.6", "tailwindcss": "^3.4.0", "typescript": "5.2.2", From ab0b6615677c1fb36720d860b70355809d8a8e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 16:32:40 +0100 Subject: [PATCH 11/16] refactor: improve Revolut QR code styling and amount format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move styling classes (padding, background, border) directly to QRCode component - Keep amount in cents as integer instead of converting to decimal (Revolut API expects cents) - Fix URL template string (remove extra closing brace) - Remove empty line for cleaner code - Add space between size and className attributes Amount format: from (monthlyExpense / 100).toFixed(2) to monthlyExpense.toFixed(0) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/ui/ViewLocationCard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index 5d7c8ef..b17bafb 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -148,12 +148,12 @@ export const ViewLocationCard:FC = ({location, userSettin } { userSettings?.enableRevolutPayment && tenantPaymentMethod === "revolut" ? (() => { - const revolutPaymentUrl = `https://revolut.me/${userSettings.ownerRevolutProfileName?.replace('@', '')}?amount=${(monthlyExpense / 100).toFixed(2)}¤cy=${userSettings.currency}}`; + const revolutPaymentUrl = `https://revolut.me/${userSettings.ownerRevolutProfileName?.replace('@', '')}?amount=${(monthlyExpense).toFixed(0)}¤cy=${userSettings.currency}`; return ( <>

{t("payment-info-header")}

-
- +
+

Date: Mon, 24 Nov 2025 16:35:20 +0100 Subject: [PATCH 12/16] feat: formatting revolut link --- app/ui/ViewLocationCard.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/ui/ViewLocationCard.tsx b/app/ui/ViewLocationCard.tsx index b17bafb..7afc03c 100644 --- a/app/ui/ViewLocationCard.tsx +++ b/app/ui/ViewLocationCard.tsx @@ -9,7 +9,7 @@ import { ViewBillBadge } from "./ViewBillBadge"; import { Pdf417Barcode } from "./Pdf417Barcode"; import { EncodePayment, PaymentParams } from "hub-3a-payment-encoder"; import Link from "next/link"; -import { DocumentIcon } from "@heroicons/react/24/outline"; +import { DocumentIcon, LinkIcon } from "@heroicons/react/24/outline"; import { uploadUtilBillsProofOfPayment } from "../lib/actions/locationActions"; import QRCode from "react-qr-code"; @@ -156,10 +156,11 @@ export const ViewLocationCard:FC = ({location, userSettin

+ {t("revolut-link-text")} From f47693fdd6f651d28a8a83f36f0559fed3e33395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 16:39:10 +0100 Subject: [PATCH 13/16] (refactor) formatting revolut link --- app/ui/UserSettingsForm.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/ui/UserSettingsForm.tsx b/app/ui/UserSettingsForm.tsx index fb5eec2..42f310a 100644 --- a/app/ui/UserSettingsForm.tsx +++ b/app/ui/UserSettingsForm.tsx @@ -10,6 +10,7 @@ import SettingsIcon from "@mui/icons-material/Settings"; import { formatIban } from "../lib/formatStrings"; import { InfoBox } from "./InfoBox"; import { NoteBox } from "./NoteBox"; +import { LinkIcon } from "@heroicons/react/24/outline"; export type UserSettingsFormProps = { userSettings: UserSettings | null; @@ -310,13 +311,17 @@ const FormFields: FC = ({ userSettings, errors, message }) => { { !errors?.ownerRevolutProfileName && formValues.ownerRevolutProfileName.length > 5 ? ( -

{t("revolut-profile--test-link-label")} - {t("revolut-profile--test-link-text")} -

+

+ {t("revolut-profile--test-link-label")} {' '} + + + {t("revolut-profile--test-link-text")} + +

) : null } From 88b6b32c3e7097befd054bb444f51c7faae24eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 16:42:55 +0100 Subject: [PATCH 14/16] feat: update payment instructions for tenants in English and Croatian --- messages/en.json | 7 ++++++- messages/hr.json | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/messages/en.json b/messages/en.json index cb2c0a0..947fac3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -145,7 +145,12 @@ "tenant-payment-instructions-legend": "PAYMENT INSTRUCTIONS", "tenant-payment-instructions-code-info": "When the tenant opens the link to the statement for the given month, the application can show payment instructions for utility costs to your IBAN, as well as a 2D code they can scan.", - "tenant-payment-instructions-toggle-label": "show payment instructions to the tenant", + + "tenant-payment-instructions-method--legend": "Podstanaru prikaži upute za uplatu:", + "tenant-payment-instructions-method--none": "ne prikazuj upute za uplatu", + "tenant-payment-instructions-method--iban": "uplata na IBAN", + "tenant-payment-instructions-method--revolut": "uplata na Revolut", + "tenant--payment-instructions-note": "IMPORTANT: for this to work you will also need to go into app settings and enter your name and IBAN.", "iban-payment--tenant-name-label": "Tenant First and Last Name", diff --git a/messages/hr.json b/messages/hr.json index 9b23b42..299cab1 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -150,7 +150,6 @@ "tenant-payment-instructions-method--iban": "uplata na IBAN", "tenant-payment-instructions-method--revolut": "uplata na Revolut", - "tenant-payment-instructions-toggle-label": "podstanaru prikaži upute za uplatu", "tenant--payment-instructions-note": "VAŽNO: za ovu funkcionalnost potrebno je otvoriti postavke aplikacije, te unijeti vaše ime i IBAN.", "iban-payment--form-title": "Informacije za uplatu na IBAN", From 600e31e7b1478dc92e72efdc3826c42288576b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 16:48:23 +0100 Subject: [PATCH 15/16] feat: disable payment method select when no payment methods configured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add userSettings prop to LocationEditForm to check payment configuration - Disable payment method dropdown when neither IBAN nor Revolut is enabled - Force select value to "none" when both methods are disabled - Disable individual IBAN/Revolut options when not configured - Display NoteBox warning explaining why payment options are unavailable - Update LocationEditPage and LocationAddPage to fetch and pass userSettings - Add English and Croatian translations for disabled state message This prevents users from configuring location payment methods before setting up their own payment info. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../location/[id]/add/LocationAddPage.tsx | 4 ++- .../location/[id]/edit/LocationEditPage.tsx | 7 ++-- app/ui/LocationEditForm.tsx | 32 ++++++++++++++----- messages/en.json | 11 ++++--- messages/hr.json | 1 + 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/app/[locale]/location/[id]/add/LocationAddPage.tsx b/app/[locale]/location/[id]/add/LocationAddPage.tsx index 9556980..93c65ec 100644 --- a/app/[locale]/location/[id]/add/LocationAddPage.tsx +++ b/app/[locale]/location/[id]/add/LocationAddPage.tsx @@ -1,6 +1,8 @@ import { LocationEditForm } from '@/app/ui/LocationEditForm'; import { YearMonth } from '@/app/lib/db-types'; +import { getUserSettings } from '@/app/lib/actions/userSettingsActions'; export default async function LocationAddPage({ yearMonth }: { yearMonth:YearMonth }) { - return (); + const userSettings = await getUserSettings(); + return (); } \ No newline at end of file diff --git a/app/[locale]/location/[id]/edit/LocationEditPage.tsx b/app/[locale]/location/[id]/edit/LocationEditPage.tsx index 505f634..b99203e 100644 --- a/app/[locale]/location/[id]/edit/LocationEditPage.tsx +++ b/app/[locale]/location/[id]/edit/LocationEditPage.tsx @@ -1,6 +1,7 @@ import { notFound } from 'next/navigation'; import { LocationEditForm } from '@/app/ui/LocationEditForm'; import { fetchLocationById } from '@/app/lib/actions/locationActions'; +import { getUserSettings } from '@/app/lib/actions/userSettingsActions'; export default async function LocationEditPage({ locationId }: { locationId:string }) { @@ -10,7 +11,9 @@ export default async function LocationEditPage({ locationId }: { locationId:stri return(notFound()); } - const result = ; - + const userSettings = await getUserSettings(); + + const result = ; + return (result); } \ No newline at end of file diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index 8525413..00c5e1c 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -2,26 +2,31 @@ import { TrashIcon } from "@heroicons/react/24/outline"; import { FC, useState } from "react"; -import { BillingLocation, YearMonth } from "../lib/db-types"; +import { BillingLocation, UserSettings, YearMonth } from "../lib/db-types"; import { updateOrAddLocation } from "../lib/actions/locationActions"; import { useFormState } from "react-dom"; import Link from "next/link"; import { useLocale, useTranslations } from "next-intl"; import { InfoBox } from "./InfoBox"; +import { NoteBox } from "./NoteBox"; export type LocationEditFormProps = { /** location which should be edited */ location: BillingLocation, /** year adn month at a new billing location should be assigned */ - yearMonth?: undefined + yearMonth?: undefined, + /** user settings for payment configuration */ + userSettings: UserSettings | null } | { /** location which should be edited */ location?: undefined, /** year adn month at a new billing location should be assigned */ - yearMonth: YearMonth + yearMonth: YearMonth, + /** user settings for payment configuration */ + userSettings: UserSettings | null } -export const LocationEditForm: FC = ({ location, yearMonth }) => { +export const LocationEditForm: FC = ({ location, yearMonth, userSettings }) => { const initialState = { message: null, errors: {} }; const handleAction = updateOrAddLocation.bind(null, location?._id, location?.yearMonth ?? yearMonth); @@ -83,17 +88,28 @@ export const LocationEditForm: FC = ({ location, yearMont
{t("tenant-payment-instructions-legend")} - + {t("tenant-payment-instructions-code-info")}
{t("tenant-payment-instructions-method--legend")} - handleInputChange("tenantPaymentMethod", e.target.value)} + disabled={!userSettings?.enableIbanPayment && !userSettings?.enableRevolutPayment} + > - - + +
+ { + !userSettings?.enableIbanPayment && !userSettings?.enableRevolutPayment && ( + {t("tenant-payment-instructions-method--disabled-message")} + ) + } { formValues.tenantPaymentMethod === "iban" ? ( <> diff --git a/messages/en.json b/messages/en.json index 947fac3..c3644c2 100644 --- a/messages/en.json +++ b/messages/en.json @@ -146,11 +146,12 @@ "tenant-payment-instructions-legend": "PAYMENT INSTRUCTIONS", "tenant-payment-instructions-code-info": "When the tenant opens the link to the statement for the given month, the application can show payment instructions for utility costs to your IBAN, as well as a 2D code they can scan.", - "tenant-payment-instructions-method--legend": "Podstanaru prikaži upute za uplatu:", - "tenant-payment-instructions-method--none": "ne prikazuj upute za uplatu", - "tenant-payment-instructions-method--iban": "uplata na IBAN", - "tenant-payment-instructions-method--revolut": "uplata na Revolut", - + "tenant-payment-instructions-method--legend": "Show payment instructions to tenant:", + "tenant-payment-instructions-method--none": "do not show payment instructions", + "tenant-payment-instructions-method--iban": "payment via IBAN", + "tenant-payment-instructions-method--revolut": "payment via Revolut", + "tenant-payment-instructions-method--disabled-message": "IMPORTANT: Payment instructions are not available because neither IBAN nor Revolut payment methods are configured in your user settings. Please go to Settings and enable at least one payment method.", + "tenant--payment-instructions-note": "IMPORTANT: for this to work you will also need to go into app settings and enter your name and IBAN.", "iban-payment--tenant-name-label": "Tenant First and Last Name", diff --git a/messages/hr.json b/messages/hr.json index 299cab1..419d324 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -149,6 +149,7 @@ "tenant-payment-instructions-method--none": "ne prikazuj upute za uplatu", "tenant-payment-instructions-method--iban": "uplata na IBAN", "tenant-payment-instructions-method--revolut": "uplata na Revolut", + "tenant-payment-instructions-method--disabled-message": "VAŽNO: Upute za uplatu nisu dostupne jer niti IBAN niti Revolut metode plaćanja nisu konfigurirane u vašim korisničkim postavkama. Molimo idite u Postavke i omogućite barem jednu metodu plaćanja.", "tenant--payment-instructions-note": "VAŽNO: za ovu funkcionalnost potrebno je otvoriti postavke aplikacije, te unijeti vaše ime i IBAN.", From 830578c2e40a6fd1861d2735556e23cc4dbe0e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Mon, 24 Nov 2025 17:02:09 +0100 Subject: [PATCH 16/16] refactor: improve payment method dropdown UX with inline disabled labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove disabled attribute from select - users can now always see all options - Add conditional option labels showing "(disabled in app settings)" for unavailable methods - Add userSettings check to IBAN form display condition - Remove NoteBox warning (replaced by inline disabled labels in options) - Remove unused NoteBox import - Remove redundant InfoBox message - Add English and Croatian translations for disabled option labels - Clean up removed translation keys Better UX: Users can now see why payment options are unavailable directly in the dropdown. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/ui/LocationEditForm.tsx | 26 +++++++++++++++----------- messages/en.json | 5 +++-- messages/hr.json | 6 +++--- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/app/ui/LocationEditForm.tsx b/app/ui/LocationEditForm.tsx index 00c5e1c..185bc37 100644 --- a/app/ui/LocationEditForm.tsx +++ b/app/ui/LocationEditForm.tsx @@ -8,7 +8,6 @@ import { useFormState } from "react-dom"; import Link from "next/link"; import { useLocale, useTranslations } from "next-intl"; import { InfoBox } from "./InfoBox"; -import { NoteBox } from "./NoteBox"; export type LocationEditFormProps = { /** location which should be edited */ @@ -98,20 +97,26 @@ export const LocationEditForm: FC = ({ location, yearMont className="select input-bordered w-full" name="tenantPaymentMethod" onChange={(e) => handleInputChange("tenantPaymentMethod", e.target.value)} - disabled={!userSettings?.enableIbanPayment && !userSettings?.enableRevolutPayment} > - - + +
- { - !userSettings?.enableIbanPayment && !userSettings?.enableRevolutPayment && ( - {t("tenant-payment-instructions-method--disabled-message")} - ) - } - { formValues.tenantPaymentMethod === "iban" ? ( + { formValues.tenantPaymentMethod === "iban" && userSettings?.enableIbanPayment ? ( <>
{t("iban-payment--form-title")}
@@ -185,7 +190,6 @@ export const LocationEditForm: FC = ({ location, yearMont ))}
- {t("tenant--payment-instructions-note")} ) : // ELSE include hidden inputs to preserve existing values <> diff --git a/messages/en.json b/messages/en.json index c3644c2..29be3d5 100644 --- a/messages/en.json +++ b/messages/en.json @@ -149,10 +149,11 @@ "tenant-payment-instructions-method--legend": "Show payment instructions to tenant:", "tenant-payment-instructions-method--none": "do not show payment instructions", "tenant-payment-instructions-method--iban": "payment via IBAN", + "tenant-payment-instructions-method--iban-disabled": "payment via IBAN - disabled in app settings", "tenant-payment-instructions-method--revolut": "payment via Revolut", - "tenant-payment-instructions-method--disabled-message": "IMPORTANT: Payment instructions are not available because neither IBAN nor Revolut payment methods are configured in your user settings. Please go to Settings and enable at least one payment method.", + "tenant-payment-instructions-method--revolut-disabled": "payment via Revolut - disabled in app settings", + - "tenant--payment-instructions-note": "IMPORTANT: for this to work you will also need to go into app settings and enter your name and IBAN.", "iban-payment--tenant-name-label": "Tenant First and Last Name", "iban-payment--tenant-name-placeholder": "enter tenant's first and last name", diff --git a/messages/hr.json b/messages/hr.json index 419d324..f9b549b 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -148,10 +148,10 @@ "tenant-payment-instructions-method--legend": "Podstanaru prikaži upute za uplatu:", "tenant-payment-instructions-method--none": "ne prikazuj upute za uplatu", "tenant-payment-instructions-method--iban": "uplata na IBAN", + "tenant-payment-instructions-method--iban-disabled": "uplata na IBAN - onemogućeno u app postavkama", "tenant-payment-instructions-method--revolut": "uplata na Revolut", - "tenant-payment-instructions-method--disabled-message": "VAŽNO: Upute za uplatu nisu dostupne jer niti IBAN niti Revolut metode plaćanja nisu konfigurirane u vašim korisničkim postavkama. Molimo idite u Postavke i omogućite barem jednu metodu plaćanja.", - - "tenant--payment-instructions-note": "VAŽNO: za ovu funkcionalnost potrebno je otvoriti postavke aplikacije, te unijeti vaše ime i IBAN.", + "tenant-payment-instructions-method--revolut-disabled": "uplata na Revolut - onemogućeno u app postavkama", + "tenant-payment-instructions-method--disabled-message": "Ova opcija je nedostupna zato što nije omogućena u postavkama aplikacije.", "iban-payment--form-title": "Informacije za uplatu na IBAN", "iban-payment--tenant-name-label": "Ime i prezime podstanara",