From 747e8cb3b29fb205d950bd86a5afdd8afa3ec3c6 Mon Sep 17 00:00:00 2001 From: Charles Pizzato <311327716+modernitconsultants@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:12:48 +1000 Subject: [PATCH 1/3] fix(dashboard): render money in the property currency, not hardcoded USD --- .../dashboard/src/context/PropertyContext.tsx | 11 +++ apps/dashboard/src/lib/money.ts | 72 +++++++++++++++++++ apps/dashboard/src/lib/property-types.ts | 2 + apps/dashboard/src/pages/Accounting.tsx | 6 +- apps/dashboard/src/pages/Channels.tsx | 13 ++-- apps/dashboard/src/pages/Commercial.tsx | 6 +- apps/dashboard/src/pages/Dashboard.tsx | 17 ++--- apps/dashboard/src/pages/HouseAccounts.tsx | 6 +- apps/dashboard/src/pages/NightAudit.tsx | 11 +-- apps/dashboard/src/pages/RatePlans.tsx | 11 +-- apps/dashboard/src/pages/Reports.tsx | 13 ++-- apps/dashboard/src/pages/Reservations.tsx | 9 +-- 12 files changed, 132 insertions(+), 45 deletions(-) create mode 100644 apps/dashboard/src/lib/money.ts diff --git a/apps/dashboard/src/context/PropertyContext.tsx b/apps/dashboard/src/context/PropertyContext.tsx index aecfcde8..983d5a00 100644 --- a/apps/dashboard/src/context/PropertyContext.tsx +++ b/apps/dashboard/src/context/PropertyContext.tsx @@ -2,6 +2,7 @@ import { createContext, useContext, useState, useEffect, type ReactNode } from ' import { useSearchParams } from 'react-router-dom'; import { api, setPropertyId as setApiPropertyId } from '../lib/api'; import { joinPropertyRoom, leavePropertyRoom } from '../lib/socket'; +import { DEFAULT_CURRENCY } from '../lib/money'; import { PORTFOLIO_MODE_ID, type PropertySummary, @@ -10,6 +11,8 @@ import { interface PropertyContextValue { propertyId: string | null; + /** Active property's ISO 4217 code; falls back to USD before properties load. */ + currencyCode: string; setPropertyId: (id: string) => void; isPortfolioMode: boolean; properties: PropertySummary[]; @@ -20,6 +23,7 @@ interface PropertyContextValue { const PropertyContext = createContext({ propertyId: null, + currencyCode: DEFAULT_CURRENCY, setPropertyId: () => {}, isPortfolioMode: false, properties: [], @@ -39,6 +43,12 @@ export function PropertyProvider({ children }: { children: ReactNode }) { const [propertiesError, setPropertiesError] = useState(null); const isPortfolioMode = propertyId === PORTFOLIO_MODE_ID; + // Portfolio mode spans properties that may not share a currency, so it has no + // single answer — fall back rather than assert one property's code over others. + const currencyCode = + (!isPortfolioMode && + properties.find((p) => p.id === propertyId)?.currencyCode) || + DEFAULT_CURRENCY; function setPropertyId(id: string) { setPropertyIdState(id); @@ -91,6 +101,7 @@ export function PropertyProvider({ children }: { children: ReactNode }) { = { function AccountingHome() { const { t } = useTranslation(); - const { propertyId } = useProperty(); + const { propertyId, currencyCode } = useProperty(); const queryClient = useQueryClient(); const today = format(new Date(), 'yyyy-MM-dd'); const [depositOpen, setDepositOpen] = useState(false); @@ -149,7 +149,7 @@ function AccountingHome() { return api.post('/v1/deposits', { propertyId, amount: moneyString(depositAmount), - currencyCode: 'USD', + currencyCode, }); }, onSuccess: () => { @@ -215,7 +215,7 @@ function AccountingHome() { propertyId, name: ledgerName, paymentTermsDays: ledgerTerms || undefined, - currencyCode: 'USD', + currencyCode, }); }, onSuccess: () => { diff --git a/apps/dashboard/src/pages/Channels.tsx b/apps/dashboard/src/pages/Channels.tsx index 5360f242..66807b94 100644 --- a/apps/dashboard/src/pages/Channels.tsx +++ b/apps/dashboard/src/pages/Channels.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useMemo } from 'react'; +import { formatMoney } from '../lib/money'; import { Routes, Route, useNavigate, useParams } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Radio, Plus, ChevronLeft, RefreshCw, Zap, Image as ImageIcon, Trash2 } from 'lucide-react'; @@ -95,10 +96,6 @@ function contentPushErrorMessages(results: unknown[]): string[] { return messages; } -function formatMoney(amount: number): string { - return `$${Number(amount).toFixed(2)}`; -} - // ---- Connection List ---- function ConnectionList() { const { t } = useTranslation(); @@ -540,7 +537,7 @@ function ConnectionDetail() { // ---- Rate Parity ---- function RateParity() { const { t } = useTranslation(); - const { propertyId } = useProperty(); + const { propertyId, currencyCode } = useProperty(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { toast } = useToast(); @@ -663,7 +660,7 @@ function RateParity() { {parity.map((row) => ( {row.ratePlanName} - {formatMoney(row.baseAmount)} + {formatMoney(row.baseAmount, currencyCode)} {channelColumns.map((col) => { const ch = row.channels.find((c) => c.channelConnectionId === col.channelConnectionId); if (!ch) { @@ -671,7 +668,7 @@ function RateParity() { } return ( -
{formatMoney(ch.effectiveRate)}
+
{formatMoney(ch.effectiveRate, currencyCode)}
{!ch.isParity && (
- {t('channels.variance')}: {formatMoney(ch.variance)} + {t('channels.variance')}: {formatMoney(ch.variance, currencyCode)}
)} diff --git a/apps/dashboard/src/pages/Commercial.tsx b/apps/dashboard/src/pages/Commercial.tsx index fe87b95e..27c5bf84 100644 --- a/apps/dashboard/src/pages/Commercial.tsx +++ b/apps/dashboard/src/pages/Commercial.tsx @@ -25,7 +25,7 @@ interface CommercialProfile { function CommercialList() { const { t } = useTranslation(); - const { propertyId } = useProperty(); + const { propertyId, currencyCode } = useProperty(); const navigate = useNavigate(); const queryClient = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); @@ -197,7 +197,7 @@ function CommercialList() { function CommercialDetail() { const { t } = useTranslation(); const { id } = useParams<{ id: string }>(); - const { propertyId } = useProperty(); + const { propertyId , currencyCode } = useProperty(); const navigate = useNavigate(); const queryClient = useQueryClient(); const [arOpen, setArOpen] = useState(false); @@ -223,7 +223,7 @@ function CommercialDetail() { propertyId, name: profile!.name, paymentTermsDays: profile?.paymentTermsDays, - currencyCode: 'USD', + currencyCode, groupProfileId: id, description: `Direct bill — ${profile!.name}`, }); diff --git a/apps/dashboard/src/pages/Dashboard.tsx b/apps/dashboard/src/pages/Dashboard.tsx index 82fc8225..d648a99e 100644 --- a/apps/dashboard/src/pages/Dashboard.tsx +++ b/apps/dashboard/src/pages/Dashboard.tsx @@ -1,4 +1,5 @@ import { useEffect, useState, useCallback } from 'react'; +import { formatMoney } from '../lib/money'; import { useQuery } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { @@ -44,7 +45,7 @@ interface ActivityEvent { export default function Dashboard() { const { t, i18n } = useTranslation(); - const { propertyId, setPropertyId, isPortfolioMode, properties } = useProperty(); + const { propertyId, setPropertyId, isPortfolioMode, properties, currencyCode } = useProperty(); const navigate = useNavigate(); const now = new Date(); const dateLocale = getDateLocale(i18n.resolvedLanguage); @@ -163,19 +164,19 @@ export default function Dashboard() { /> @@ -189,7 +190,7 @@ export default function Dashboard() { - [`$${v.toFixed(2)}`, t('dashboard.portfolio.revenue')]} /> + [formatMoney(v, currencyCode), t('dashboard.portfolio.revenue')]} /> @@ -276,7 +277,7 @@ export default function Dashboard() { /> ('accounts'); @@ -64,7 +64,7 @@ function HouseAccountList() { const createMutation = useMutation({ mutationFn: () => { requirePropertyId(propertyId); - return api.post('/v1/house-accounts', { propertyId, name, kind, currencyCode: 'USD' }); + return api.post('/v1/house-accounts', { propertyId, name, kind, currencyCode }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['house-accounts'] }); @@ -80,7 +80,7 @@ function HouseAccountList() { propertyId, name: productName, price: moneyString(productPrice), - currencyCode: 'USD', + currencyCode, category: productCategory || undefined, }); }, diff --git a/apps/dashboard/src/pages/NightAudit.tsx b/apps/dashboard/src/pages/NightAudit.tsx index 20263954..62d79136 100644 --- a/apps/dashboard/src/pages/NightAudit.tsx +++ b/apps/dashboard/src/pages/NightAudit.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { formatMoney } from '../lib/money'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Moon, Play, Brain, AlertTriangle, Info, XCircle } from 'lucide-react'; import { format } from 'date-fns'; @@ -38,9 +39,9 @@ function auditCount(value: string | number | undefined): number { return Number.isNaN(n) ? 0 : n; } -function formatAuditRevenue(a: AuditResult, empty = '$0.00'): string { +function formatAuditRevenue(a: AuditResult, currencyCode: string, empty = '—'): string { const rev = auditRevenue(a); - return rev != null ? `$${rev.toFixed(2)}` : empty; + return rev != null ? formatMoney(rev, currencyCode) : empty; } const SEVERITY_ICONS = { critical: XCircle, warning: AlertTriangle, info: Info }; @@ -88,7 +89,7 @@ function AnomalySection({ propertyId }: { propertyId: string }) { export default function NightAudit() { const { t } = useTranslation(); - const { propertyId } = useProperty(); + const { propertyId, currencyCode } = useProperty(); const queryClient = useQueryClient(); const [auditDate, setAuditDate] = useState(format(new Date(), 'yyyy-MM-dd')); const [lastResult, setLastResult] = useState(null); @@ -153,7 +154,7 @@ export default function NightAudit() {
- +
)} @@ -201,7 +202,7 @@ export default function NightAudit() { {a.completedAt ? format(new Date(a.completedAt), 'HH:mm:ss') : '—'} {auditCount(a.roomChargesPosted)} {auditCount(a.noShowsProcessed)} - {formatAuditRevenue(a, '—')} + {formatAuditRevenue(a, currencyCode)} ))} {audits.length === 0 && ( diff --git a/apps/dashboard/src/pages/RatePlans.tsx b/apps/dashboard/src/pages/RatePlans.tsx index efde20c7..3d7359b4 100644 --- a/apps/dashboard/src/pages/RatePlans.tsx +++ b/apps/dashboard/src/pages/RatePlans.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { formatMoney } from '../lib/money'; import { Routes, Route, useNavigate, useParams } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { BadgeDollarSign, Plus, ChevronLeft, Pencil, Trash2 } from 'lucide-react'; @@ -89,7 +90,7 @@ function buildRestrictionBody(form: RestrictionForm) { // ---- Rate Plan List ---- function RatePlanList() { const { t } = useTranslation(); - const { propertyId } = useProperty(); + const { propertyId, currencyCode } = useProperty(); const navigate = useNavigate(); const queryClient = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); @@ -127,7 +128,7 @@ function RatePlanList() { type, baseAmount: moneyString(baseAmount || '0'), roomTypeId, - currencyCode: 'USD', + currencyCode, }; if (type === 'derived') { payload.parentRatePlanId = parentRatePlanId; @@ -184,7 +185,7 @@ function RatePlanList() { {p.code} {p.roomTypeName ?? '—'} - {p.baseAmount != null ? `$${Number(p.baseAmount).toFixed(2)}` : '—'} + {p.baseAmount != null ? formatMoney(p.baseAmount, currencyCode) : '—'} ))} @@ -483,7 +484,7 @@ function RestrictionsPanel({ ratePlanId }: { ratePlanId: string }) { // ---- Rate Plan Detail ---- function RatePlanDetail() { const { t } = useTranslation(); - const { propertyId } = useProperty(); + const { propertyId , currencyCode } = useProperty(); const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [testDate, setTestDate] = useState(''); @@ -537,7 +538,7 @@ function RatePlanDetail() {

{t('ratePlans.details')}

{t('ratePlans.code')}

{plan.code}

-

{t('ratePlans.baseAmount')}

{plan.baseAmount != null ? `$${Number(plan.baseAmount).toFixed(2)}` : '—'}

+

{t('ratePlans.baseAmount')}

{plan.baseAmount != null ? formatMoney(plan.baseAmount, currencyCode) : '—'}

{t('ratePlans.roomType')}

{plan.roomTypeName ?? '—'}

{t('ratePlans.currency')}

{plan.currency ?? plan.currencyCode ?? 'USD'}

{plan.type === 'derived' && ( diff --git a/apps/dashboard/src/pages/Reports.tsx b/apps/dashboard/src/pages/Reports.tsx index 42b23172..76c1f11a 100644 --- a/apps/dashboard/src/pages/Reports.tsx +++ b/apps/dashboard/src/pages/Reports.tsx @@ -1,4 +1,5 @@ import { useEffect, useMemo, useState } from 'react'; +import { formatMoney } from '../lib/money'; import { useSearchParams } from 'react-router-dom'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { BarChart3, Percent, DollarSign, TrendingUp, Building2, Star } from 'lucide-react'; @@ -26,7 +27,7 @@ const DEMO_FAVORITES_KEY = 'haip.reportFavorites'; export default function Reports() { const { t } = useTranslation(); - const { propertyId, isPortfolioMode, properties } = useProperty(); + const { propertyId, isPortfolioMode, properties, currencyCode } = useProperty(); const queryClient = useQueryClient(); const [searchParams] = useSearchParams(); // Deep links (e.g. Accounting → live trial balance) may preselect report/date. @@ -255,8 +256,8 @@ export default function Reports() { {report === 'financial-summary' && (
- - + +
{isPortfolioMode && Array.isArray(reportData.byProperty) && ( @@ -354,9 +355,9 @@ export default function Reports() { {report === 'daily-revenue' && (
- - - + + +
{payments && Object.keys(payments).length > 0 && (
diff --git a/apps/dashboard/src/pages/Reservations.tsx b/apps/dashboard/src/pages/Reservations.tsx index a0582106..3cfa9587 100644 --- a/apps/dashboard/src/pages/Reservations.tsx +++ b/apps/dashboard/src/pages/Reservations.tsx @@ -1,4 +1,5 @@ import { useState, useMemo } from 'react'; +import { formatMoney } from '../lib/money'; import { Routes, Route, useNavigate } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { @@ -104,7 +105,7 @@ function parseImportRows(text: string): Record[] { // ---- Reservation List ---- function ReservationList() { const { t } = useTranslation(); - const { propertyId } = useProperty(); + const { propertyId, currencyCode } = useProperty(); const navigate = useNavigate(); const queryClient = useQueryClient(); @@ -238,7 +239,7 @@ function ReservationList() { adults: createAdults, children: createChildren, totalAmount, - currencyCode: 'USD', + currencyCode, source: 'direct', }); }, @@ -382,7 +383,7 @@ function ReservationList() { return ; }, size: 120 }, - { accessorKey: 'totalAmount', header: t('reservations.total'), cell: ({ getValue }) => getValue() != null ? `$${Number(getValue()).toFixed(2)}` : '—', size: 100 }, + { accessorKey: 'totalAmount', header: t('reservations.total'), cell: ({ getValue }) => getValue() != null ? formatMoney(getValue() as string | number, currencyCode) : '—', size: 100 }, { accessorKey: 'source', header: t('reservations.source'), cell: ({ getValue }) => (getValue() as string) ?? 'direct', size: 90 }, { id: 'actions', @@ -628,7 +629,7 @@ function ReservationList() { - +
{detailRes.notes && ( From 886aaebf07600777da5ed21db08c00118bfcce52 Mon Sep 17 00:00:00 2001 From: Charles Pizzato <311327716+modernitconsultants@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:27:20 +1000 Subject: [PATCH 2/3] fix(dashboard): finish every money render, and stop threading currency by hand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the review: 63 renders across 15 files, not the 18 my first pass claimed — I had grepped a single pattern, which is the same mistake as the bug itself. The design changed to make that safe. Rather than thread currencyCode into every component that happens to show an amount — including module-level helpers that cannot call a hook — lib/money keeps the active property's currency in a module value that PropertyContext pushes on change. That is the pattern lib/api.ts already uses for propertyId, so it follows the codebase rather than inventing a mechanism. Call sites are now formatMoney(amount), with an explicit second argument only where a record carries its own currency. Nothing needs a hook in scope, which is what made the earlier partial attempts fragile. --- .../front-desk/GuestDetailsModal.tsx | 7 +++--- .../src/components/rates/RatePlanCalendar.tsx | 5 ++-- .../dashboard/src/context/PropertyContext.tsx | 8 ++++++- apps/dashboard/src/lib/money.ts | 23 +++++++++++++++++-- apps/dashboard/src/pages/Accounting.tsx | 13 ++++++----- apps/dashboard/src/pages/Cashier.tsx | 7 +++--- apps/dashboard/src/pages/Commercial.tsx | 3 ++- apps/dashboard/src/pages/Dashboard.tsx | 6 ++--- apps/dashboard/src/pages/Folios.tsx | 17 +++++++------- apps/dashboard/src/pages/FrontDesk.tsx | 5 ++-- apps/dashboard/src/pages/Groups.tsx | 3 ++- apps/dashboard/src/pages/HouseAccounts.tsx | 9 ++++---- apps/dashboard/src/pages/RatePlans.tsx | 2 +- apps/dashboard/src/pages/Reports.tsx | 20 ++++++++-------- apps/dashboard/src/pages/Reservations.tsx | 2 +- 15 files changed, 82 insertions(+), 48 deletions(-) diff --git a/apps/dashboard/src/components/front-desk/GuestDetailsModal.tsx b/apps/dashboard/src/components/front-desk/GuestDetailsModal.tsx index 33228e60..0076110f 100644 --- a/apps/dashboard/src/components/front-desk/GuestDetailsModal.tsx +++ b/apps/dashboard/src/components/front-desk/GuestDetailsModal.tsx @@ -1,4 +1,5 @@ import { Link } from 'react-router-dom'; +import { formatMoney } from '../../lib/money'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { ArrowRightLeft, StickyNote, LogIn, LogOut } from 'lucide-react'; @@ -229,7 +230,7 @@ export default function GuestDetailsModal({ {t('frontDesk.accountSummary')}

- {t('frontDesk.balance')}: ${Number(balance).toFixed(2)} + {t('frontDesk.balance')}: {formatMoney(balance)}

@@ -247,7 +248,7 @@ export default function GuestDetailsModal({
  • {c.description || '—'} - ${Number(c.amount).toFixed(2)} + {formatMoney(c.amount)}
  • ))} @@ -268,7 +269,7 @@ export default function GuestDetailsModal({
  • {p.method || '—'} - ${Number(p.amount).toFixed(2)} + {formatMoney(p.amount)}
  • ))} diff --git a/apps/dashboard/src/components/rates/RatePlanCalendar.tsx b/apps/dashboard/src/components/rates/RatePlanCalendar.tsx index 25664b8c..3e542186 100644 --- a/apps/dashboard/src/components/rates/RatePlanCalendar.tsx +++ b/apps/dashboard/src/components/rates/RatePlanCalendar.tsx @@ -1,4 +1,5 @@ import { useMemo, useState } from 'react'; +import { formatMoney } from '../../lib/money'; import { useQuery } from '@tanstack/react-query'; import { addDays, format } from 'date-fns'; import { api } from '../../lib/api'; @@ -165,8 +166,8 @@ export default function RatePlanCalendar({ ratePlanId, propertyId, baseAmount }: {days.map((day, i) => ( {day.date} - ${day.baseRate.toFixed(2)} - ${day.effectiveRate.toFixed(2)} + {formatMoney(day.baseRate)} + {formatMoney(day.effectiveRate)}
    {day.badges.length === 0 ? ( diff --git a/apps/dashboard/src/context/PropertyContext.tsx b/apps/dashboard/src/context/PropertyContext.tsx index 983d5a00..78fda6f3 100644 --- a/apps/dashboard/src/context/PropertyContext.tsx +++ b/apps/dashboard/src/context/PropertyContext.tsx @@ -2,7 +2,7 @@ import { createContext, useContext, useState, useEffect, type ReactNode } from ' import { useSearchParams } from 'react-router-dom'; import { api, setPropertyId as setApiPropertyId } from '../lib/api'; import { joinPropertyRoom, leavePropertyRoom } from '../lib/socket'; -import { DEFAULT_CURRENCY } from '../lib/money'; +import { DEFAULT_CURRENCY, setActiveCurrency } from '../lib/money'; import { PORTFOLIO_MODE_ID, type PropertySummary, @@ -85,6 +85,12 @@ export function PropertyProvider({ children }: { children: ReactNode }) { // Bootstrap once; propertyId auto-select handled inside the effect. }, []); + // Keep the money formatter's default in step with the active property, the + // same way setApiPropertyId keeps the API client in step above. + useEffect(() => { + setActiveCurrency(currencyCode); + }, [currencyCode]); + useEffect(() => { if (isPortfolioMode) { setApiPropertyId(null); diff --git a/apps/dashboard/src/lib/money.ts b/apps/dashboard/src/lib/money.ts index 52992168..26cae3c8 100644 --- a/apps/dashboard/src/lib/money.ts +++ b/apps/dashboard/src/lib/money.ts @@ -15,6 +15,25 @@ /** Fallback when a record carries no currency and no property is selected. */ export const DEFAULT_CURRENCY = 'USD'; +/** + * The active property's currency, pushed here by PropertyContext. + * + * Same pattern the API client already uses for propertyId (`setPropertyId` in + * lib/api.ts): a module-level value the context keeps current. It means a money + * render does not need the currency threaded into every component that happens + * to display an amount — dozens of call sites across the dashboard, many inside + * helpers that cannot call a hook at all. + */ +let activeCurrency = DEFAULT_CURRENCY; + +export function setActiveCurrency(code?: string | null) { + activeCurrency = (code || DEFAULT_CURRENCY).toUpperCase(); +} + +export function getActiveCurrency() { + return activeCurrency; +} + /** * Format a money value for display. * @@ -31,7 +50,7 @@ export function formatMoney( const value = typeof amount === 'string' ? Number(amount) : amount; if (!Number.isFinite(value)) return '—'; - const code = (currencyCode || DEFAULT_CURRENCY).toUpperCase(); + const code = (currencyCode || activeCurrency).toUpperCase(); try { return new Intl.NumberFormat(locale, { style: 'currency', @@ -56,7 +75,7 @@ export function formatMoneyPlain( const value = typeof amount === 'string' ? Number(amount) : amount; if (!Number.isFinite(value)) return '—'; - const code = (currencyCode || DEFAULT_CURRENCY).toUpperCase(); + const code = (currencyCode || activeCurrency).toUpperCase(); try { const digits = new Intl.NumberFormat(locale, { style: 'currency', diff --git a/apps/dashboard/src/pages/Accounting.tsx b/apps/dashboard/src/pages/Accounting.tsx index aed2216e..3dd158f5 100644 --- a/apps/dashboard/src/pages/Accounting.tsx +++ b/apps/dashboard/src/pages/Accounting.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { formatMoney } from '../lib/money'; import { Routes, Route, Link } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Calculator, Plus, Download, BarChart3, Pencil, Archive } from 'lucide-react'; @@ -340,12 +341,12 @@ function AccountingHome() { {(Object.keys(AGING_LABELS) as (keyof AgingBuckets)[]).map((key) => (
    {AGING_LABELS[key]} - ${Number(report.buckets[key] ?? 0).toFixed(2)} + {formatMoney(report.buckets[key] ?? 0)}
    ))}
    {t('accounting.total')} - ${Number(report.total ?? 0).toFixed(2)} + {formatMoney(report.total ?? 0)}
    ) : ( @@ -396,7 +397,7 @@ function AccountingHome() {
      {deposits.slice(0, 8).map((d) => (
    • - ${Number(d.amount).toFixed(2)} + {formatMoney(d.amount)} {d.status} {d.status === 'held' && ( @@ -554,7 +555,7 @@ function AccountingHome() {
    - setDepositActionOpen(false)} title={t('accounting.depositActions', { amount: Number(selectedDeposit?.amount ?? 0).toFixed(2) })}> + setDepositActionOpen(false)} title={t('accounting.depositActions', { amount: formatMoney(selectedDeposit?.amount ?? 0) })}>
    setApplyFolioId(e.target.value)} placeholder={t('accounting.folioIdPlaceholder')} className="w-full border border-gray-200 rounded-lg px-3 py-2 text-sm font-mono text-xs" /> @@ -596,7 +597,7 @@ function AccountingHome() { {reversible.map((tx) => ( ))} diff --git a/apps/dashboard/src/pages/Cashier.tsx b/apps/dashboard/src/pages/Cashier.tsx index 2ee7372f..09cce7ee 100644 --- a/apps/dashboard/src/pages/Cashier.tsx +++ b/apps/dashboard/src/pages/Cashier.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { formatMoney } from '../lib/money'; import { Routes, Route, useNavigate, useParams } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Banknote, Plus, ChevronLeft, FileText } from 'lucide-react'; @@ -85,7 +86,7 @@ function CashierHome() { {drawers.map((d, i) => ( {d.name} - ${Number(d.startingFloat ?? 0).toFixed(2)} + {formatMoney(d.startingFloat ?? 0)} @@ -222,7 +223,7 @@ function CashierSession() {
    {drawer?.startingFloat != null && (

    - {t('cashier.startingFloat')}: ${Number(drawer.startingFloat).toFixed(2)} + {t('cashier.startingFloat')}: {formatMoney(drawer.startingFloat)}

    )}
    @@ -314,7 +315,7 @@ function SessionReport() { {type.replace('_', ' ')} {row.count} - ${Number(row.total).toFixed(2)} + {formatMoney(row.total)} ))} diff --git a/apps/dashboard/src/pages/Commercial.tsx b/apps/dashboard/src/pages/Commercial.tsx index 27c5bf84..3832f1a8 100644 --- a/apps/dashboard/src/pages/Commercial.tsx +++ b/apps/dashboard/src/pages/Commercial.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { formatMoney } from '../lib/money'; import { Routes, Route, useNavigate, useParams } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Briefcase, Plus, ChevronLeft } from 'lucide-react'; @@ -281,7 +282,7 @@ function CommercialDetail() { {arLedgers.map((l) => (
  • {l.name} - ${Number(l.balance ?? 0).toFixed(2)} + {formatMoney(l.balance ?? 0)}
  • ))} {arLedgers.length === 0 && ( diff --git a/apps/dashboard/src/pages/Dashboard.tsx b/apps/dashboard/src/pages/Dashboard.tsx index d648a99e..96031c14 100644 --- a/apps/dashboard/src/pages/Dashboard.tsx +++ b/apps/dashboard/src/pages/Dashboard.tsx @@ -222,9 +222,9 @@ export default function Dashboard() { {formatOccupancyPercent(row.occupancyRate)} - ${Number(row.adr).toFixed(2)} - ${Number(row.revpar).toFixed(2)} - ${Number(row.totalRevenue).toFixed(2)} + {formatMoney(row.adr)} + {formatMoney(row.revpar)} + {formatMoney(row.totalRevenue)} ))} diff --git a/apps/dashboard/src/pages/Folios.tsx b/apps/dashboard/src/pages/Folios.tsx index d93fc14d..5dae58a0 100644 --- a/apps/dashboard/src/pages/Folios.tsx +++ b/apps/dashboard/src/pages/Folios.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { formatMoney } from '../lib/money'; import { useTranslation } from 'react-i18next'; import { Routes, Route, useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; @@ -132,7 +133,7 @@ function FolioList() { {f.guestName ?? '—'} - ${Number(f.balance ?? 0).toFixed(2)} + {formatMoney(f.balance ?? 0)} ))} {folios.length === 0 && ( @@ -398,7 +399,7 @@ function SplitFolioPanel({ {movableCharges.map((c) => ( ))} @@ -648,7 +649,7 @@ function FolioDetail() {

    {t('folios.balance')}

    -

    ${Number(folio.balance ?? 0).toFixed(2)}

    +

    {formatMoney(folio.balance ?? 0)}

    @@ -679,7 +680,7 @@ function FolioDetail() { {c.serviceDate} {c.description} {c.isLocked && } {t(`folios.chargeTypes.${c.type}`, { defaultValue: c.type })} - ${Number(c.amount).toFixed(2)} + {formatMoney(c.amount)} {!c.isReversal && !reversedIds.has(c.id) && !c.isLocked && folio.status === 'open' && (