From 0145a2030d4bc0d4166a11b3b5751dd397e14126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Dere=C5=BEi=C4=87?= Date: Thu, 18 Dec 2025 17:45:27 +0100 Subject: [PATCH] feat: add multi-bill-edit page for batch bill status updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add getLocationsByMonth server action with aggregation pipeline to calculate hasAttachment - Add updateMonth server action for bulk bill status updates with path revalidation - Create multi-bill-edit page at /home/multi-bill-edit/[year]/[month] - Implement MultiBillEdit component with toggle functionality for all bills - Add BillToggleBadge component integration for consistent bill display - Add "set all as paid/unpaid" toggle button for batch operations - Implement server-side redirect with success message after save - Add Suspense boundary with loading skeleton - Update translations for multi-bill-edit feature (Croatian and English) - Ensure data freshness with unstable_noStore and revalidatePath 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../[year]/[month]/BillToggleBadge.tsx | 24 +++ .../[year]/[month]/MultiBillEdit.tsx | 162 ++++++++++++++++++ .../[year]/[month]/MultiBillEditButton.tsx | 26 +++ .../[year]/[month]/MultiBillEditPage.tsx | 16 ++ .../[year]/[month]/not-found.tsx | 6 + .../multi-bill-edit/[year]/[month]/page.tsx | 21 +++ app/lib/actions/monthActions.ts | 138 ++++++++++++++- app/ui/MonthLocationList.tsx | 8 + messages/en.json | 33 ++-- messages/hr.json | 33 ++-- 10 files changed, 442 insertions(+), 25 deletions(-) create mode 100644 app/[locale]/home/multi-bill-edit/[year]/[month]/BillToggleBadge.tsx create mode 100644 app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEdit.tsx create mode 100644 app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditButton.tsx create mode 100644 app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditPage.tsx create mode 100644 app/[locale]/home/multi-bill-edit/[year]/[month]/not-found.tsx create mode 100644 app/[locale]/home/multi-bill-edit/[year]/[month]/page.tsx diff --git a/app/[locale]/home/multi-bill-edit/[year]/[month]/BillToggleBadge.tsx b/app/[locale]/home/multi-bill-edit/[year]/[month]/BillToggleBadge.tsx new file mode 100644 index 0000000..887b269 --- /dev/null +++ b/app/[locale]/home/multi-bill-edit/[year]/[month]/BillToggleBadge.tsx @@ -0,0 +1,24 @@ +import { FC } from "react" +import { Bill } from "@/app/lib/db-types" +import { TicketIcon } from "@heroicons/react/24/outline" + +export interface BillBadgeProps { + locationId: string, + bill: Pick, + onClick?: () => void +}; + +export const BillToggleBadge:FC = ({ bill: { name, paid, hasAttachment, proofOfPayment }, onClick}) => { + + const className = `badge badge-lg ${paid?"badge-success":" badge-outline"} ${ !paid && hasAttachment ? "btn-outline btn-success" : "" } cursor-pointer`; + + return ( +
+ {name} + { + proofOfPayment?.uploadedAt ? + : null + } +
+ ); +} \ No newline at end of file diff --git a/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEdit.tsx b/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEdit.tsx new file mode 100644 index 0000000..cf483d5 --- /dev/null +++ b/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEdit.tsx @@ -0,0 +1,162 @@ +'use client'; + +import { FC, useState } from "react"; +import { BillingLocation, YearMonth } from "../../../../../lib/db-types"; +import { formatYearMonth } from "../../../../../lib/format"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { updateMonth } from "../../../../../lib/actions/monthActions"; +import { toast, ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import { BillToggleBadge } from "./BillToggleBadge"; + +export interface MultiBillEditProps { + locations: BillingLocation[]; + year: number; + month: number; +} + +interface BillState { + locationId: string; + billId: string; + paid: boolean; +} + +export const MultiBillEdit: FC = ({ locations, year, month }) => { + const t = useTranslations("multi-bill-edit"); + const router = useRouter(); + + // Initialize bill states from locations + const initialBillStates: BillState[] = locations.flatMap(location => + location.bills.map(bill => ({ + locationId: location._id, + billId: bill._id, + paid: bill.paid, + })) + ); + + const [billStates, setBillStates] = useState(initialBillStates); + const [isSaving, setIsSaving] = useState(false); + const [allPaidMode, setAllPaidMode] = useState(false); + + // Toggle individual bill paid status + const handleBillToggle = (locationId: string, billId: string) => { + setBillStates(prevStates => + prevStates.map(state => + state.locationId === locationId && state.billId === billId + ? { ...state, paid: !state.paid } + : state + ) + ); + }; + + // Toggle all bills paid status + const handleSetAllAsPayed = () => { + const newPaidState = !allPaidMode; + setAllPaidMode(newPaidState); + setBillStates(prevStates => + prevStates.map(state => ({ ...state, paid: newPaidState })) + ); + }; + + // Save changes to database + const handleSave = async () => { + setIsSaving(true); + try { + const updates = billStates.map(state => ({ + locationId: state.locationId, + billId: state.billId, + paid: state.paid, + })); + + await updateMonth({ year, month }, updates); + + } catch (error) { + console.error('Error saving bill updates:', error); + toast.error(t("save-error-message"), { theme: "dark" }); + setIsSaving(false); + } + }; + + // Cancel and return to home page + const handleCancel = () => { + router.push(`/home?year=${year}`); + }; + + // Get bill state for a specific bill + const getBillState = (locationId: string, billId: string): boolean => { + const state = billStates.find( + s => s.locationId === locationId && s.billId === billId + ); + return state?.paid ?? false; + }; + + const yearMonth: YearMonth = { year, month }; + + return ( +
+

{formatYearMonth(yearMonth)}

+
+ +
+
+ {locations.map(location => ( +
+
+

+ {formatYearMonth(yearMonth)} {location.name} +

+
+ {location.bills.length > 0 ? ( +
+ {location.bills.map(bill => { + const isPaid = getBillState(location._id, bill._id); + return ( + handleBillToggle(location._id, bill._id)} + /> + ); + })} +
+ ) : ( +

{t("no-bills-message")}

+ )} +
+
+
+ ))} + {/* Action buttons */} +
+ + +
+
+ +
+ ); +}; diff --git a/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditButton.tsx b/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditButton.tsx new file mode 100644 index 0000000..de8f248 --- /dev/null +++ b/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditButton.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { InboxStackIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline'; +import { useTranslations } from 'next-intl'; +import { YearMonth } from '../../../../../lib/db-types'; +import Link from 'next/link'; + +export interface MultiBillEditButtonProps { + yearMonth: YearMonth; +} + +export const MultiBillEditButton: React.FC = ({ yearMonth }) => { + + const t = useTranslations("home-page.multi-bill-edit-button"); + + return ( +
+ + + + {t("tooltip")} + + +
+ ); +}; \ No newline at end of file diff --git a/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditPage.tsx b/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditPage.tsx new file mode 100644 index 0000000..8218b0a --- /dev/null +++ b/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditPage.tsx @@ -0,0 +1,16 @@ +import { notFound } from 'next/navigation'; +import { MultiBillEdit } from '@/app/[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEdit'; +import { getLocationsByMonth } from '@/app/lib/actions/monthActions'; + +export default async function MultiBillEditPage({ year, month }: { year: number; month: number }) { + + const locations = await getLocationsByMonth({ year, month }); + + if (!locations || locations.length === 0) { + return(notFound()); + } + + const result = ; + + return (result); +} diff --git a/app/[locale]/home/multi-bill-edit/[year]/[month]/not-found.tsx b/app/[locale]/home/multi-bill-edit/[year]/[month]/not-found.tsx new file mode 100644 index 0000000..794ae95 --- /dev/null +++ b/app/[locale]/home/multi-bill-edit/[year]/[month]/not-found.tsx @@ -0,0 +1,6 @@ +import { NotFoundPage } from '@/app/ui/NotFoundPage'; + +const MultiBillEditNotFound = () => +; + +export default MultiBillEditNotFound; \ No newline at end of file diff --git a/app/[locale]/home/multi-bill-edit/[year]/[month]/page.tsx b/app/[locale]/home/multi-bill-edit/[year]/[month]/page.tsx new file mode 100644 index 0000000..de3a4f0 --- /dev/null +++ b/app/[locale]/home/multi-bill-edit/[year]/[month]/page.tsx @@ -0,0 +1,21 @@ +import { Suspense } from 'react'; +import MultiBillEditPage from './MultiBillEditPage'; +import { Main } from '@/app/ui/Main'; + +const MultiBillEditSkeleton = () => ( +
+ +

Loading...

+
+); + +export default async function Page({ params }: { params: { year: string; month: string } }) { + + return ( +
+ }> + + +
+ ); +} diff --git a/app/lib/actions/monthActions.ts b/app/lib/actions/monthActions.ts index 2b71f8d..d71def5 100644 --- a/app/lib/actions/monthActions.ts +++ b/app/lib/actions/monthActions.ts @@ -5,7 +5,9 @@ import { ObjectId } from 'mongodb'; import { Bill, BillingLocation, YearMonth } from '../db-types'; import { AuthenticatedUser } from '../types/next-auth'; import { withUser } from '../auth'; -import { unstable_noStore as noStore } from 'next/cache'; +import { unstable_noStore as noStore, unstable_noStore, revalidatePath } from 'next/cache'; +import { getLocale } from 'next-intl/server'; +import { gotoHomeWithMessage } from './navigationActions'; /** * Server-side action which adds a new month to the database @@ -82,3 +84,137 @@ export const fetchAvailableYears = withUser(async (user:AuthenticatedUser) => { return(sortedYears); }) + +/** + * Fetches all locations for a specific month for the authenticated user + * Only projects essential fields needed for the multi-bill-edit page + * @param yearMonth - The year and month to fetch + * @returns Array of locations with minimal bill data + */ +export const getLocationsByMonth = withUser(async (user: AuthenticatedUser, yearMonth: YearMonth) => { + + unstable_noStore(); + + const { id: userId } = user; + const dbClient = await getDbClient(); + + // Use aggregation pipeline to calculate hasAttachment field + const locations = await dbClient.collection("lokacije") + .aggregate([ + { + $match: { + userId, + yearMonth: { + year: yearMonth.year, + month: yearMonth.month, + } + } + }, + { + $addFields: { + _id: { $toString: "$_id" }, + bills: { + $map: { + input: "$bills", + as: "bill", + in: { + _id: { $toString: "$$bill._id" }, + name: "$$bill.name", + paid: "$$bill.paid", + hasAttachment: { $ne: ["$$bill.attachment", null] }, + proofOfPayment: "$$bill.proofOfPayment", + }, + }, + } + } + }, + { + $project: { + "_id": 1, + "name": 1, + "yearMonth.year": 1, + "yearMonth.month": 1, + "bills._id": 1, + "bills.name": 1, + "bills.paid": 1, + "bills.hasAttachment": 1, + "bills.proofOfPayment.uploadedAt": 1, + } + }, + { + $sort: { + name: 1, + }, + }, + ]) + .toArray(); + + return locations as Array; +}); + +/** + * Updates the paid status of bills for locations in a specific month + * @param yearMonth - The year and month to update + * @param updates - Array of updates with locationId, billId, and paid status + * @returns Success status + */ +export const updateMonth = withUser(async ( + user: AuthenticatedUser, + yearMonth: YearMonth, + updates: Array<{ locationId: string; billId: string; paid: boolean }> +) => { + unstable_noStore(); + + const { id: userId } = user; + const dbClient = await getDbClient(); + + // Group updates by location to minimize database operations + const updatesByLocation = updates.reduce((acc, update) => { + if (!acc[update.locationId]) { + acc[update.locationId] = []; + } + acc[update.locationId].push(update); + return acc; + }, {} as Record); + + // Perform bulk updates + const updatePromises = Object.entries(updatesByLocation).map( + async ([locationId, locationUpdates]) => { + // For each bill update in this location + const billUpdatePromises = locationUpdates.map(({ billId, paid }) => + dbClient.collection("lokacije").updateOne( + { + _id: locationId, + userId, // Ensure the location belongs to the authenticated user + yearMonth: { + year: yearMonth.year, + month: yearMonth.month, + }, + 'bills._id': billId, + }, + { + $set: { + 'bills.$.paid': paid, + }, + } + ) + ); + + return Promise.all(billUpdatePromises); + } + ); + + await Promise.all(updatePromises); + + // Revalidate the home page and multi-edit page to show fresh data + revalidatePath('/home'); + revalidatePath(`/home/multi-bill-edit/${yearMonth.year}/${yearMonth.month}`); + + // Redirect to home page with year and month parameters, including success message + if (yearMonth) { + const locale = await getLocale(); + await gotoHomeWithMessage(locale, 'bill-multi-edit-saved', yearMonth); + } + + return { success: true }; +}); diff --git a/app/ui/MonthLocationList.tsx b/app/ui/MonthLocationList.tsx index ea2131e..10ff494 100644 --- a/app/ui/MonthLocationList.tsx +++ b/app/ui/MonthLocationList.tsx @@ -12,6 +12,7 @@ import { useRouter, useSearchParams } from "next/navigation"; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import { useTranslations } from "next-intl"; +import { MultiBillEditButton } from "../[locale]/home/multi-bill-edit/[year]/[month]/MultiBillEditButton"; const getNextYearMonth = (yearMonth:YearMonth) => { const {year, month} = yearMonth; @@ -84,6 +85,12 @@ export const MonthLocationList:React.FC = ({ params.delete('locationDeleted'); messageShown = true; } + + if (search.get('bill-multi-edit-saved') === 'true') { + toast.success(t("bill-multi-edit-save-success-message"), { theme: "dark" }); + params.delete('bill-multi-edit-saved'); + messageShown = true; + } }, [search, router, t]); if(!availableYears || !months) { @@ -128,6 +135,7 @@ export const MonthLocationList:React.FC = ({
+
) diff --git a/messages/en.json b/messages/en.json index fd95348..d38caeb 100644 --- a/messages/en.json +++ b/messages/en.json @@ -58,6 +58,9 @@ "add-month-button": { "tooltip": "Add next mont" }, + "multi-bill-edit-button": { + "tooltip": "Multi Bills Edit" + }, "location-card": { "edit-card-tooltip": "Edit realestate", "add-bill-button-tooltip": "Add a new bill", @@ -103,7 +106,8 @@ "bill-saved-message": "Bill saved successfully", "bill-deleted-message": "Bill deleted successfully", "location-saved-message": "Location saved successfully", - "location-deleted-message": "Location deleted successfully" + "location-deleted-message": "Location deleted successfully", + "bill-multi-edit-save-success-message": "Changes saved successfully" }, "bill-delete-form": { "text": "Please confirm deletion of bill \"{bill_name}\" at \"{location_name}\".", @@ -153,7 +157,6 @@ "location-name-legend": "Realestate name", "location-name-placeholder": "enter realestate name", "notes-placeholder": "notes", - "proof-of-payment-attachment-type--legend": "Proof of Payment", "proof-of-payment-attachment-type--info": "Here you can choose how the tenant can provide proof of payment for utilities. Select the option that best matches the payment arrangement you have agreed upon.", "proof-of-payment-attachment-type--option--label": "Tenant provides ...", @@ -164,24 +167,20 @@ "proof-of-payment-attachment-type--option--combined--hint": "💡 with the selected option you might also want to activate payment instructions - see above", "proof-of-payment-attachment-type--option--per-bill": "✂️ separate proof of payment for each bill", "proof-of-payment-attachment-type--option--per-bill--tooltip": "The selected option is useful if the tenant pays utilities directly to individual service providers", - "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": "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--revolut-disabled": "payment via Revolut - disabled in app settings", - "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", @@ -222,12 +221,10 @@ }, "user-settings-form": { "title": "User settings", - "iban-payment-instructions--legend": "Payment to Your IBAN", "iban-payment-instructions--intro-title": "What does this option do?", "iban-payment-instructions--intro-message": "By activating this option, the monthly statement sent to the tenant will contain payment details and a 2D barcode allowing a direct payment to your bank account.", "iban-payment-instructions--toggle-label": "enable IBAN payment instructions", - "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", @@ -237,22 +234,17 @@ "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", - - "revolut-form-title": "Payment Information for Revolut", "revolut-payment-instructions--legend": "Payment to Your Revolut Profile", "revolut-payment-instructions--intro-title": "What does this option do?", "revolut-payment-instructions--intro-message": "By activating this option, the monthly statement sent to the tenant will contain a link allowing a direct payment to your Revolut account.", "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').", "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.", - "general-settings-legend": "General Settings", "currency-label": "Currency", "save-button": "Save", @@ -271,5 +263,20 @@ }, "info-box": { "default-title": "What is this option for?" + }, + "multi-bill-edit": { + "title": "Multi Bill Edit", + "loading-message": "Loading...", + "error-title": "Error", + "no-locations-title": "No Locations", + "no-locations-message": "No locations found for the selected month", + "no-bills-message": "No bills", + "set-all-as-paid-button": "Mark all as paid", + "set-all-as-unpaid-button": "Mark all as unpaid", + "save-button": "Save", + "saving-button": "Saving...", + "cancel-button": "Cancel", + "back-to-home-button": "Back to Home", + "save-error-message": "Error saving changes" } } \ No newline at end of file diff --git a/messages/hr.json b/messages/hr.json index 583889d..bc542b3 100644 --- a/messages/hr.json +++ b/messages/hr.json @@ -58,6 +58,9 @@ "add-month-button": { "tooltip": "Dodaj idući mjesec" }, + "multi-bill-edit-button": { + "tooltip": "Izmjena više računa" + }, "location-card": { "edit-card-tooltip": "Izmjeni nekretninu", "add-bill-button-tooltip": "Dodaj novi račun", @@ -103,7 +106,10 @@ "bill-saved-message": "Račun uspješno spremljen", "bill-deleted-message": "Račun uspješno obrisan", "location-saved-message": "Nekretnina uspješno spremljena", - "location-deleted-message": "Nekretnina uspješno obrisana" + "location-deleted-message": "Nekretnina uspješno obrisana", + "bill-multi-edit-save-success-message": "Promjene uspješno spremljene", + "bill-multi-edit-save-error-message": "Greška pri spremanju promjena", + "bill-multi-edit-load-error-message": "Greška pri učitavanju podataka" }, "bill-delete-form": { "text": "Molim potvrdi brisanje računa \"{bill_name}\" koji pripada nekretnini \"{location_name}\".", @@ -152,7 +158,6 @@ "location-name-legend": "Realestate name", "location-name-placeholder": "unesite naziv nekretnine", "notes-placeholder": "bilješke", - "proof-of-payment-attachment-type--legend": "Potvrda o uplati", "proof-of-payment-attachment-type--info": "Ovdje možete odabrati na koji način na koji podstanar može priložiti potvrdu o uplati režija. Izaberite način koji najbolje odgovara načinu na koji ste dogovorili plaćanje režija.", "proof-of-payment-attachment-type--option--label": "Podstanar prilaže ...", @@ -163,10 +168,8 @@ "proof-of-payment-attachment-type--option--combined--hint": "💡 za odabranu opciju dobro je uključiti i prikaz uputa za uplatu - vidi gore", "proof-of-payment-attachment-type--option--per-bill": "✂️ zasebna potvrda za svaki račun", "proof-of-payment-attachment-type--option--per-bill--tooltip": "Odabrana opcija je korisna ako podstanar plaća režije izravno pojedinačnim davateljima usluga", - "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 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", @@ -174,7 +177,6 @@ "tenant-payment-instructions-method--revolut": "🅡 uplata na Revolut", "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", "iban-payment--tenant-name-placeholder": "unesite ime i prezime podstanara", @@ -182,7 +184,6 @@ "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", @@ -223,11 +224,9 @@ }, "user-settings-form": { "title": "Korisničke postavke", - "iban-payment-instructions--legend": "Uplata na vaš IBAN", "iban-payment-instructions--intro-message": "Aktiviranjem ove opcije, mjesečni obračun poslan podstanaru sadržavati će podatke za uplatu i 2D barkod putem kojeg će podstanar moći izvršiti izravnu uplatu sredstava na bankovni račun.", "iban-payment-instructions--toggle-label": "omoguć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", @@ -237,18 +236,15 @@ "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, mjesečni obračun poslan podstanaru sadržavati će link putem kojeg će podstanar moći izvršiti izravnu uplatu sredstava na vaš Revolut račun.", "revolut-payment-instructions--toggle-label": "omogući Revolut uplatu", - "revolut-form-title": "Info 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').", "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", "save-button": "Spremi", @@ -268,5 +264,20 @@ }, "info-box": { "default-title": "Čemu služi ova opcija?" + }, + "multi-bill-edit": { + "title": "Masovna izmjena računa", + "loading-message": "Učitavanje...", + "error-title": "Greška", + "no-locations-title": "Nema lokacija", + "no-locations-message": "Nisu pronađene lokacije za odabrani mjesec", + "no-bills-message": "Nema računa", + "set-all-as-paid-button": "Označi sve kao plaćeno", + "set-all-as-unpaid-button": "Označi sve kao neplaćeno", + "save-button": "Spremi", + "saving-button": "Spremanje...", + "cancel-button": "Odustani", + "back-to-home-button": "Povratak na početnu", + "save-error-message": "Greška pri spremanju promjena" } } \ No newline at end of file