diff --git a/.compliance/ropa.yaml b/.compliance/ropa.yaml index fc70d177..d80a8844 100644 --- a/.compliance/ropa.yaml +++ b/.compliance/ropa.yaml @@ -605,9 +605,9 @@ processing_activities: data_categories: - user.contact.email # person property via identify(), aldrig i event-properties - user.name # profiles.full_name - # Sessionsinspelningen visar synlig text och tangentbordsinmatning, - # förutom maskerade fält (belopp, person-/orgnummer, lösenord samt - # data-ph-mask-taggade element), se security_measures nedan. + # Sessionsinspelningen är maskerad som standard: all tangentbords- + # inmatning och all text maskeras, utom appens eget statiska gränssnitt + # (rubriker, navigering, knappar, etiketter), se security_measures. - user.behavior # sidvisningar, klick, händelser, sessionsinspelning - user.device # user agent, skärmstorlek, IP (trunkeras av PostHog) # Supportärenden: fritext som användaren SJÄLV skriver till supporten via @@ -637,12 +637,18 @@ processing_activities: stored_in: - posthog_eu # externt hos biträdet; inget lagras i vår databas security_measures: - # Mönsterbaserad maskering (2026-08-06, ersätter maskera-allt-läget): - # belopp (valutaformaterad text), person-/organisationsnummer (text och - # inmatning) och lösenord maskeras; övrig text och tangentbordsinmatning - # syns i inspelningen för felsökning. data-ph-mask tvingar maskering av - # utpekade PII-element; låst av test i lib/analytics/__tests__. - - session_replay_masks_amounts_and_identity_numbers # lib/analytics/replay-masking.ts + # Maskerat-som-standard (2026-08-17, ersätter det mönsterbaserade läget + # från 2026-08-06): ALL tangentbordsinmatning maskeras (rrweb + # maskAllInputs utan undantagsfunktion) och ALL text maskeras utom + # element taggade data-ph-unmask (statiskt i18n-gränssnitt: rubriker, + # navigering, etiketter, knappar) samt tabellkolumnrubriker. Även + # otaggat gränssnitt maskeras, så felmoden för ny UI är övermaskering, + # aldrig läckage. Taggat gränssnitt mönsterskrubbas dessutom på belopp + # och person-/organisationsnummer. data-ph-mask tvingar maskering och + # vinner över data-ph-unmask; låst av test i lib/analytics/__tests__. + - session_replay_masked_by_default # lib/analytics/replay-masking.ts + - session_replay_masks_all_input_values + - session_replay_masks_amounts_and_identity_numbers_in_chrome - session_replay_masks_password_inputs - org_number_never_transmitted_as_group_property # låst av test i lib/analytics/__tests__ - no_pii_in_event_properties # PII endast som person properties via identify() diff --git a/DECISIONS.md b/DECISIONS.md index 48fb8058..35a7e95a 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1019,6 +1019,7 @@ One line per decision: `[YYYY-MM-DD] : `. Appended by agents and [2026-08-15] Confirmed intentional (Swedish-review note): with override=true and an unresolvable filename, the attach endpoint links a document to any same-company, same-declared-year, posted verifikat, migrated or not. This mirrors /api/documents/[id]/link, which imposes no filename check at all, so it introduces no new capability class; tenant, year and period-lock enforcement always apply. [2026-08-15] BankID tabs bind to a random non-secret `flowId` signed into the shared flow cookie and sent as a request header after start or explicit resume: mode pinning alone cannot distinguish two same-mode tabs, so an older tab could otherwise silently follow, cancel, or complete a newer person's identification after `/start` replaced the origin-wide cookie. This supersedes the 2026-08-15 decision that deliberately skipped mode matching on active polls. [2026-08-15] Did not apply BankID migration `20260815120000` to Supabase staging during PR #1625 follow-through: read-only reconciliation found 14 staging-only and 99 branch-only migration versions, so applying on top of that divergent ledger would violate the no-orphan rule. Production is reconciled with zero remote-only versions and exactly this PR migration local-only; hosted pg-real validates the migration until staging is reconciled. +[2026-08-17] Session replay masking inverted to deny-by-default (founder-directed after user pushback on session recording): ALL input values are masked (maskAllInputs with no maskInputFn, so rrweb masks wholesale; placeholders are attributes and stay visible) and ALL text is masked unless it sits under data-ph-unmask chrome or a th (dry-table column headers are raw th per page, so the mask function treats th as chrome rather than tagging hundreds of sites). Chrome tags live on the shared primitives (PageHeader, Label, Button except role=combobox triggers which render selected values, TabsTrigger, Badge, Card/Dialog/Sheet titles and descriptions, tooltips, help popovers, empty states, settings labels); tagged chrome is still pattern-scrubbed for amounts and identity numbers, and data-ph-mask beats data-ph-unmask so call sites that interpolate user data into chrome stay masked. Toasts (title AND description) deliberately NOT tagged: they interpolate user data at too many call sites to audit, and an audit found live leaks (deadline titles, bank account names) in titles alone. Confirm-dialog wrappers (ConfirmDialog, ConfirmationDialog, DestructiveConfirmDialog) force data-ph-mask on their titles/descriptions centrally: convention 10 makes confirm copy describe the object being acted on, so it is user data by design; that one change closed 14+ audited leak sites. A very-thorough audit of user data flowing into unmasked primitives ran in the same change and every found site got a call-site data-ph-mask. Failure mode for untagged new UI is over-masking, never leakage. Supersedes the 2026-08-06 pattern-based default; privacy policy and RoPA updated in the same change. [2026-08-17] Betalfil missing-bankgiro UX: advisory warning in PaymentFilePanel (download stays enabled, route stays the authority) + click-to-prefill from tic_snapshot instead of auto-seeding company_settings.bankgiro: sender payment data must be user-confirmed, and the snapshot is unvalidated registry JSON. [2026-08-16] /transactions FyPicker double-fetch fixed by gating the initial fetch on FyPicker's existing onReady (fires after its restore onChange) instead of the analysis doc's literal "read the persisted period synchronously in initial state": localStorage only holds the period ID, not the FiscalPeriod bounds, so a synchronous read would suppress FyPicker's restore (value !== null) and leave the fetch permanently unscoped while the chip claimed a year. Same outcome (one scoped fetch per mount, background refetch on period change) without a stale-bounds cache or new FyPicker API. [2026-08-16] Row exit animation for dry-table rows collapses via td padding/line-height/font-size transitions plus a numeric max-height (.row-collapsible) on the fixed-height cell spans, not grid-template-rows 0fr (the AttGoraSection pattern): table cells cannot host the grid wrapper without restructuring every td, and max-height needs a numeric rest value because auto/none does not interpolate. prefers-reduced-motion hides the exiting row instantly (display: none) while the 350ms timer does the state cleanup. @@ -1027,3 +1028,4 @@ One line per decision: `[YYYY-MM-DD] : `. Appended by agents and [2026-08-17] Article picker now overwrites the line's ROT/RUT (deduction_type + work_type) from the article's housework_type, INCLUDING clearing it when the article has none: article-defines-the-row is the established applyArticle semantic (description/price/unit already overwrite), and keeping a RUT flag when switching a row to a material article would silently claim a deduction on material (HUSFL labor-only rule). Kundkort personnummer prefill is a server-side fallback in buildInvoiceWriteData (typed > stored draft > kundkort), never a client prefill: customers.personal_number reaches the browser only as ciphertext/mask by design, so the editor just relaxes the required-mark and says where the number will come from. [2026-08-17] Arcim's "610 bilagor i Hela historiken men 100 i räkenskapsåret" in the full-archive dialog is NOT a pagination bug: verified against prod, exactly 100 documents are linked to posted vouchers in the single (extended) fiscal year and 510 are unlinked inbox/receipt docs, which scope=all includes by design (same split as cloud backup's year-ZIPs vs Grunddata.zip). Kept the semantics, fixed two things instead: estimateArchiveSize's period branch ran one unpaginated read with one flat IN() over every entry id (undercounts past the PostgREST row cap, URL blowup past ~a few hundred ids) -> now CHILD_FK_CHUNK-chunked and fetchAllRows-paginated like writeDocuments already was; and the dialog now states per scope which document set is counted, so the gap reads as intent, not as a bug. [2026-08-17] Supplier standardkonto empty-string fix lives in the API schemas, split by verb: '' normalizes to undefined on create (key dropped, column NULL) but to null on update, because update routes pass validated fields straight into .update() where undefined means "leave unchanged"; without the null mapping a cleared standardkonto/e-post would silently never clear. Client keeps sending '' as-is (the old email-strip hack removed), since stripping client-side would break exactly that clear path. The field itself became an AccountCombobox filtered to cost classes 4-7 (matches the agent-path expenseAccountField rule); other 4-digit numbers stay typeable, and the API still enforces format only. Standardkonto stays optional: it only prefills supplier-invoice lines, and the ledger-context suggestion covers the empty case, so requiring it (what the bug accidentally did) is wrong for the target user. +[2026-08-17] Replay-masking skeptic round (PR #1639): explicit data-ph tags now resolve BEFORE the th chrome fallback in replayMaskText (a single closest over tags-plus-th let a th nested in a masked container win on DOM proximity, CodeRabbit); seven missed text-leak sites got call-site masks (delete-invoice number, credit-page number, IB voucher ref, TIC orgnr since TIC serves it unnormalized so the separator scrub cannot be relied on, articles search term, dimension segment labels, activate-account buttons); the attribute channel (placeholders prefilled with effective values, title tooltips) is handled with rrweb's blockClass: user-data placeholders carry ph-no-capture, which removes the element from the recording while app UX keeps the founder-approved prefill-override pattern intact. Chose ph-no-capture over stripping the placeholders because the prefilled effective value IS the UX. diff --git a/app/(dashboard)/articles/page.tsx b/app/(dashboard)/articles/page.tsx index aaae146c..8bbc0c19 100644 --- a/app/(dashboard)/articles/page.tsx +++ b/app/(dashboard)/articles/page.tsx @@ -360,12 +360,15 @@ function ArticlesPageInner() { // The currency scope is part of why nothing matched, so it has to // be named: otherwise the register reads as empty of the term // when it is only empty inside the active scope. - currencyFilter === ALL_CURRENCIES - ? t('no_search_results_description', { term: searchTerm }) - : t('no_search_results_in_currency_description', { - term: searchTerm, - currency: currencyFilter, - }) + // data-ph-mask: the search term is what the user typed. + + {currencyFilter === ALL_CURRENCIES + ? t('no_search_results_description', { term: searchTerm }) + : t('no_search_results_in_currency_description', { + term: searchTerm, + currency: currencyFilter, + })} + } /> ) : ( diff --git a/app/(dashboard)/assets/[id]/dispose/page.tsx b/app/(dashboard)/assets/[id]/dispose/page.tsx index 708ea116..1ca20311 100644 --- a/app/(dashboard)/assets/[id]/dispose/page.tsx +++ b/app/(dashboard)/assets/[id]/dispose/page.tsx @@ -277,7 +277,8 @@ export default function DisposeAssetPage({ params }: { params: Promise<{ id: str /> - {asset.name} + {/* data-ph-mask: the asset name is user data */} + {asset.name} diff --git a/app/(dashboard)/bookkeeping/[id]/page.tsx b/app/(dashboard)/bookkeeping/[id]/page.tsx index 68604b8f..711629ba 100644 --- a/app/(dashboard)/bookkeeping/[id]/page.tsx +++ b/app/(dashboard)/bookkeeping/[id]/page.tsx @@ -441,11 +441,14 @@ export default function JournalEntryDetailPage({ params }: { params: Promise<{ i const prefix = DIM_BADGE_PREFIX[dimNo] ?? dim?.name ?? `Dim ${dimNo}` const hasName = !!value && value.name !== '' && value.name !== value.code return ( + // data-ph-mask: dimension names and codes are user data. No title + // attribute: replay masking covers text nodes, not attributes, so + // a title tooltip would ship the masked content in the clear. {prefix}: {hasName ? value.name : code} diff --git a/app/(dashboard)/bookkeeping/year-end/arsredovisning/page.tsx b/app/(dashboard)/bookkeeping/year-end/arsredovisning/page.tsx index f8efe2df..95e6aec6 100644 --- a/app/(dashboard)/bookkeeping/year-end/arsredovisning/page.tsx +++ b/app/(dashboard)/bookkeeping/year-end/arsredovisning/page.tsx @@ -514,9 +514,11 @@ export default function ArsredovisningPage() { + {data.company.org_number + ? `${data.company.name} · ${data.company.org_number}` + : data.company.name} + } action={ ))} diff --git a/components/bookkeeping/CorrectOpeningBalanceDialog.tsx b/components/bookkeeping/CorrectOpeningBalanceDialog.tsx index 57d636a2..865d98d5 100644 --- a/components/bookkeeping/CorrectOpeningBalanceDialog.tsx +++ b/components/bookkeeping/CorrectOpeningBalanceDialog.tsx @@ -130,7 +130,8 @@ export default function CorrectOpeningBalanceDialog({ Korrigera ingående balanser Ändra beloppen nedan och spara. Den befintliga IB-verifikationen ( - {formatVoucher(entry)}) makuleras och en ny bokförs med de korrigerade beloppen. + {formatVoucher(entry)}) makuleras och en ny bokförs med + de korrigerade beloppen. diff --git a/components/bookkeeping/CorrectionEntryDialog.tsx b/components/bookkeeping/CorrectionEntryDialog.tsx index 2f204b94..bdb5964e 100644 --- a/components/bookkeeping/CorrectionEntryDialog.tsx +++ b/components/bookkeeping/CorrectionEntryDialog.tsx @@ -303,6 +303,9 @@ export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCor value={description} onChange={(e) => setDescription(e.target.value)} placeholder={autoCorrectionDescription(entry.description)} + // ph-no-capture: the placeholder echoes the posted entry's + // description, and replay masking covers values, not attributes. + className="ph-no-capture" />

Texten på den nya verifikationen. Ändra den om originalets beskrivning inte längre diff --git a/components/bookkeeping/EditAccountDialog.tsx b/components/bookkeeping/EditAccountDialog.tsx index 05e89098..6a555860 100644 --- a/components/bookkeeping/EditAccountDialog.tsx +++ b/components/bookkeeping/EditAccountDialog.tsx @@ -299,7 +299,7 @@ export function EditAccountDialog({ open, onOpenChange, account, onSaved }: Edit - Redigera konto {account.account_number} + Redigera konto {account.account_number} diff --git a/components/bookkeeping/InboxDocumentPicker.tsx b/components/bookkeeping/InboxDocumentPicker.tsx index 1ac2f90b..df34ceae 100644 --- a/components/bookkeeping/InboxDocumentPicker.tsx +++ b/components/bookkeeping/InboxDocumentPicker.tsx @@ -268,9 +268,10 @@ export default function InboxDocumentPicker({ open, onClose, journalEntryId, onL

!o && setPreviewItem(null)}> - {previewItem?.file_name} + {/* data-ph-mask: the file name and supplier details are user data */} + {previewItem?.file_name} {previewItem && (previewItem.supplier_name || previewItem.amount != null) && ( - + {previewItem.supplier_name && {previewItem.supplier_name}} {previewItem.amount != null && ( {formatCurrency(previewItem.amount, previewItem.currency ?? 'SEK')} diff --git a/components/bookkeeping/JournalEntryForm.tsx b/components/bookkeeping/JournalEntryForm.tsx index 6d2b9be0..3aaa5318 100644 --- a/components/bookkeeping/JournalEntryForm.tsx +++ b/components/bookkeeping/JournalEntryForm.tsx @@ -1713,7 +1713,7 @@ export default function JournalEntryForm({ {line.dimensions && Object.keys(line.dimensions).length > 0 && (line.account_number || line.debit_amount || line.credit_amount) && ( - + {compactDims(line.dimensions)} )} diff --git a/components/bookkeeping/TemplateBookDialog.tsx b/components/bookkeeping/TemplateBookDialog.tsx index 4eeb3b04..5be8b404 100644 --- a/components/bookkeeping/TemplateBookDialog.tsx +++ b/components/bookkeeping/TemplateBookDialog.tsx @@ -170,7 +170,8 @@ export default function TemplateBookDialog({ open, onOpenChange, onCreated }: Pr !submitting && onOpenChange(next)}> - + {/* data-ph-mask: the template name is user data */} + {selected ? selected.name : t('tpl_dialog_title')} diff --git a/components/dimensions/BulkTagWorkbench.tsx b/components/dimensions/BulkTagWorkbench.tsx index 84dee562..72ca1acf 100644 --- a/components/dimensions/BulkTagWorkbench.tsx +++ b/components/dimensions/BulkTagWorkbench.tsx @@ -679,9 +679,11 @@ export default function BulkTagWorkbench() { {voucher.lines.length} rader {(bags.length > 0 || partial) && } + {/* data-ph-mask: dimension codes are user data */} {bags.map((bag) => ( @@ -745,6 +747,7 @@ export default function BulkTagWorkbench() { dimEntries.map(([dimNo, code]) => ( diff --git a/components/dimensions/DimensionsManager.tsx b/components/dimensions/DimensionsManager.tsx index a22325e0..0d800c85 100644 --- a/components/dimensions/DimensionsManager.tsx +++ b/components/dimensions/DimensionsManager.tsx @@ -379,12 +379,14 @@ export default function DimensionsManager() { options={dimensions.map((dim) => ({ value: dim.id, label: ( - <> + // data-ph-mask: dimension names are user data; the wrapper + // keeps the segment button's inline gap between name and count. + {dim.name} {dim.values.length > 0 && ( {dim.values.length} )} - + ), }))} /> @@ -427,13 +429,15 @@ export default function DimensionsManager() { {t('no_search_results_description', { term: searchTerm })}} /> ) : ( {t('empty_description', { dimension: activeDim?.name ?? '' })}} actionLabel={canWrite ? t('new_value') : undefined} onAction={canWrite ? () => setDialog({ mode: 'create' }) : undefined} /> @@ -497,7 +501,8 @@ export default function DimensionsManager() { !open && setDialog(null)}> - + {/* data-ph-mask: the dimension name is user data */} + {dialog?.mode === 'edit' ? t('edit_value_title') : t('new_value_title', { dimension: activeDim?.name ?? '' })} diff --git a/components/extensions/general/InvoiceInboxWorkspace.tsx b/components/extensions/general/InvoiceInboxWorkspace.tsx index df3fba0b..45918ad2 100644 --- a/components/extensions/general/InvoiceInboxWorkspace.tsx +++ b/components/extensions/general/InvoiceInboxWorkspace.tsx @@ -2373,7 +2373,7 @@ function PurchaseRow({ {/* A chip only when the row deviates: here, when we can actually tell the user where to go. */} {purchase.portal && ( - + {purchase.portal.vendor} )} diff --git a/components/extensions/general/TicWorkspace.tsx b/components/extensions/general/TicWorkspace.tsx index 77ef69f2..ae8568fc 100644 --- a/components/extensions/general/TicWorkspace.tsx +++ b/components/extensions/general/TicWorkspace.tsx @@ -328,10 +328,13 @@ export default function TicWorkspace({ userId }: WorkspaceComponentProps) { - {profile.companyName} + {/* data-ph-mask: the looked-up company name is user data */} + {profile.companyName} - {profile.orgNumber} · {profile.legalEntityType} + {/* data-ph-mask: TIC serves the orgnr in unnormalized format, so + the separator-based pattern scrub cannot be relied on */} + {profile.orgNumber} · {profile.legalEntityType} {!isActive && ( · {t('deregistered')} )} @@ -477,7 +480,8 @@ export default function TicWorkspace({ userId }: WorkspaceComponentProps) { {profile.statuses.slice(0, 6).map((status, i) => (
  • - + {/* data-ph-mask: the Bolagsverket status text is user data */} + {status.description ?? status.code ?? '-'} {status.isCeased && ( diff --git a/components/extensions/shared/CsvImportWizard.tsx b/components/extensions/shared/CsvImportWizard.tsx index e7e91713..830e217d 100644 --- a/components/extensions/shared/CsvImportWizard.tsx +++ b/components/extensions/shared/CsvImportWizard.tsx @@ -179,7 +179,8 @@ export default function CsvImportWizard({ {headers.map(h => ( - {h} + /* data-ph-mask: CSV headers are user data */ + {h} ))} diff --git a/components/extensions/shared/MockDataImportDialog.tsx b/components/extensions/shared/MockDataImportDialog.tsx index 014213b9..79037ee0 100644 --- a/components/extensions/shared/MockDataImportDialog.tsx +++ b/components/extensions/shared/MockDataImportDialog.tsx @@ -341,7 +341,8 @@ export default function MockDataImportDialog({ {csvHeaders.map(h => ( - {h} + /* data-ph-mask: CSV headers are user data */ + {h} ))} diff --git a/components/import/OpeningBalanceColumnMappingStep.tsx b/components/import/OpeningBalanceColumnMappingStep.tsx index 4b9c0207..826b2fae 100644 --- a/components/import/OpeningBalanceColumnMappingStep.tsx +++ b/components/import/OpeningBalanceColumnMappingStep.tsx @@ -200,7 +200,8 @@ export default function OpeningBalanceColumnMappingStep({ {headers.map((h, i) => ( - + /* data-ph-mask: CSV headers are user data */ + {h || `Kolumn ${i + 1}`} ))} diff --git a/components/import/RegisterColumnMappingStep.tsx b/components/import/RegisterColumnMappingStep.tsx index e878ca07..cbda74d4 100644 --- a/components/import/RegisterColumnMappingStep.tsx +++ b/components/import/RegisterColumnMappingStep.tsx @@ -91,7 +91,8 @@ export default function RegisterColumnMappingStep({ {headers.map((h, i) => ( - + /* data-ph-mask: CSV headers are user data */ + {h || `Kolumn ${i + 1}`} ))} diff --git a/components/invoices/InvoiceEditor.tsx b/components/invoices/InvoiceEditor.tsx index eff3dce3..ff6f4e33 100644 --- a/components/invoices/InvoiceEditor.tsx +++ b/components/invoices/InvoiceEditor.tsx @@ -2690,7 +2690,8 @@ export default function InvoiceEditor(props: InvoiceEditorProps = { mode: 'creat {t('send_now_dialog_title')} - + {/* data-ph-mask: the customer email is user data */} + {t('send_now_dialog_description', { email: selectedCustomer?.email ?? '' })} diff --git a/components/invoices/PaymentBookingDialog.tsx b/components/invoices/PaymentBookingDialog.tsx index bdde7001..4a717a7c 100644 --- a/components/invoices/PaymentBookingDialog.tsx +++ b/components/invoices/PaymentBookingDialog.tsx @@ -321,7 +321,10 @@ export default function PaymentBookingDialog({ - {t('title')}{invoice.invoice_number ? t('title_suffix', { number: invoice.invoice_number }) : ''} + {/* data-ph-mask: the invoice number is user data */} + {t('title')}{invoice.invoice_number ? ( + {t('title_suffix', { number: invoice.invoice_number })} + ) : ''} {nextVoucher && ( ({nextVoucher.series}{nextVoucher.next}) diff --git a/components/invoices/SendInvoiceDialog.tsx b/components/invoices/SendInvoiceDialog.tsx index 431efac2..92b32b95 100644 --- a/components/invoices/SendInvoiceDialog.tsx +++ b/components/invoices/SendInvoiceDialog.tsx @@ -455,9 +455,13 @@ export default function SendInvoiceDialog({ ? 'title_email' : 'title_manual', )} - {invoice.invoice_number ? t('title_suffix', { number: invoice.invoice_number }) : ''} + {/* data-ph-mask: the invoice number is user data */} + {invoice.invoice_number ? ( + {t('title_suffix', { number: invoice.invoice_number })} + ) : ''} - + {/* data-ph-mask: amount and customer email are user data */} + {formatCurrency(invoice.total, invoice.currency)} {invoice.currency !== 'SEK' && invoice.total_sek && ( <>{t('description_sek_suffix', { amount: formatCurrency(invoice.total_sek) })} diff --git a/components/orders/CreateInvoiceFromOrderDialog.tsx b/components/orders/CreateInvoiceFromOrderDialog.tsx index 29319109..e7e9e791 100644 --- a/components/orders/CreateInvoiceFromOrderDialog.tsx +++ b/components/orders/CreateInvoiceFromOrderDialog.tsx @@ -78,8 +78,9 @@ export default function CreateInvoiceFromOrderDialog({ - {t('invoice_title', { number: order.order_number })} - + {/* data-ph-mask: order number and customer name are user data */} + {t('invoice_title', { number: order.order_number })} + {t('invoice_description', { customer: customerLabel })} diff --git a/components/orders/OrderBookingDialog.tsx b/components/orders/OrderBookingDialog.tsx index 462d329d..d1224b0e 100644 --- a/components/orders/OrderBookingDialog.tsx +++ b/components/orders/OrderBookingDialog.tsx @@ -114,7 +114,8 @@ export default function OrderBookingDialog({ - + {/* data-ph-mask: the order number is user data */} + {isRefund ? t('book_refund_title', { number: order.order_number }) : t('book_title', { number: order.order_number })} diff --git a/components/orders/PaymentMethodMappingForm.tsx b/components/orders/PaymentMethodMappingForm.tsx index 4850f692..0989c740 100644 --- a/components/orders/PaymentMethodMappingForm.tsx +++ b/components/orders/PaymentMethodMappingForm.tsx @@ -167,7 +167,7 @@ export function PaymentMethodMappingForm({ const mode = policy?.mode ?? 'book' const account = policy?.mode === 'book' ? policy.account : '' return ( - + {title || method}}>