From bff44e57579e0f0249c26723330affcdba263eb7 Mon Sep 17 00:00:00 2001 From: Jakob Wennberg Date: Sat, 5 Sep 2026 14:53:13 +0200 Subject: [PATCH] =?UTF-8?q?fix(parties):=20one=20"fr=C3=A5n=20SCB"=20note?= =?UTF-8?q?=20per=20contact=20section=20(#2316)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(parties): one "från SCB" note per contact section, not one under every field The founder read four tags in a row. Kontaktuppgifter now ends with one sentence naming the fields the register gave: "E-post, Telefon, adress och Momsnr från SCB." Nothing else changes; the fill rule and the equality test behind it are the same. Co-Authored-By: Claude Fable 5.1 * fix(parties): the customer page's registry note tolerates the loading state Co-Authored-By: Claude Fable 5.1 * fix(parties): the supplier page's registry note tolerates the loading state Co-Authored-By: Claude Fable 5.1 --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 --- app/(dashboard)/customers/[id]/page.tsx | 22 +++++++++++++--------- app/(dashboard)/suppliers/[id]/page.tsx | 23 +++++++++++++---------- lib/parties/registry-summary.ts | 6 ++++++ messages/en.json | 3 +++ messages/sv.json | 3 +++ 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/app/(dashboard)/customers/[id]/page.tsx b/app/(dashboard)/customers/[id]/page.tsx index b09b6f02..064633a1 100644 --- a/app/(dashboard)/customers/[id]/page.tsx +++ b/app/(dashboard)/customers/[id]/page.tsx @@ -28,7 +28,7 @@ import type { Customer, CustomerType, CreateCustomerInput } from '@/types' import { DetailPageSkeleton } from '@/components/common/DetailPageSkeleton' import { PartyFactsSection } from '@/components/parties/PartyFactsSection' import { usePartyDossier } from '@/components/parties/use-party-dossier' -import { fromRegistry, addressRowsFromRegistry } from '@/lib/parties/registry-summary' +import { fromRegistry, addressRowsFromRegistry, listSv } from '@/lib/parties/registry-summary' const CUSTOMER_TYPE_KEY: Record = { individual: 'type_individual', @@ -68,7 +68,17 @@ export default function CustomerDetailPage({ const partyId = customer && customer.customer_type !== 'individual' ? ((customer as { party_id?: string | null }).party_id ?? null) : null const party = usePartyDossier(partyId) const registryAddress = party.registry?.contact.address ? addressRowsFromRegistry(party.registry.contact.address) : null - const scbNote = (isFromRegistry: boolean) => (isFromRegistry ? {tParties('facts_from_registry')} : null) + // Which contact fields carry what the register said: one note for the + // section, not a tag under every row. + const registryFields = [ + fromRegistry(customer?.email, party.registry?.contact.email) ? tParties('fact_email') : null, + fromRegistry(customer?.phone, party.registry?.contact.phone) ? tParties('fact_phone') : null, + !!registryAddress && fromRegistry(customer?.address_line1, registryAddress.address_line1) && fromRegistry(customer?.city, registryAddress.city) ? tParties('facts_address_short') : null, + fromRegistry(customer?.vat_number, party.registry?.vat_number) ? tParties('fact_vat') : null, + ].filter((x): x is string => !!x) + const registryNote = registryFields.length ? ( +

{tParties('facts_contact_from_registry', { fields: listSv(registryFields, tParties('facts_list_and')) })}

+ ) : null const [isLoading, setIsLoading] = useState(true) const [isEditOpen, setIsEditOpen] = useState(false) const [isUpdating, setIsUpdating] = useState(false) @@ -271,11 +281,9 @@ export default function CustomerDetailPage({ ) : ( )} - {scbNote(fromRegistry(customer.email, party.registry?.contact.email))} {customer.phone || } - {scbNote(fromRegistry(customer.phone, party.registry?.contact.phone))} {customer.address_line1 || customer.city ? ( @@ -285,17 +293,13 @@ export default function CustomerDetailPage({ {(customer.postal_code || customer.city) && (

{[customer.postal_code, customer.city].filter(Boolean).join(' ')}

)} - {scbNote( - !!registryAddress && - fromRegistry(customer.address_line1, registryAddress.address_line1) && - fromRegistry(customer.city, registryAddress.city), - )} {customer.country &&

{getCountryName(customer.country, errorLocale)}

} ) : ( )}
+ {registryNote} {partyId && party.dossier ? ( diff --git a/app/(dashboard)/suppliers/[id]/page.tsx b/app/(dashboard)/suppliers/[id]/page.tsx index 84eda383..95351c61 100644 --- a/app/(dashboard)/suppliers/[id]/page.tsx +++ b/app/(dashboard)/suppliers/[id]/page.tsx @@ -21,7 +21,7 @@ import type { Supplier, SupplierType, CreateSupplierInput, SupplierInvoice } fro import { DetailPageSkeleton } from '@/components/common/DetailPageSkeleton' import { PartyFactsSection } from '@/components/parties/PartyFactsSection' import { usePartyDossier } from '@/components/parties/use-party-dossier' -import { fromRegistry, addressRowsFromRegistry } from '@/lib/parties/registry-summary' +import { fromRegistry, addressRowsFromRegistry, listSv } from '@/lib/parties/registry-summary' import { formatOrgNumber } from '@/lib/utils' // Supplier invoices carry their own currency; "kr" is only correct for SEK. @@ -51,7 +51,17 @@ export default function SupplierDetailPage() { const partyId = (supplier as { party_id?: string | null } | null)?.party_id ?? null const party = usePartyDossier(partyId) const registryAddress = party.registry?.contact.address ? addressRowsFromRegistry(party.registry.contact.address) : null - const scbNote = (isFromRegistry: boolean) => (isFromRegistry ? {tParties('facts_from_registry')} : null) + // Which contact fields carry what the register said: one note for the + // section, not a tag under every row. + const registryFields = [ + fromRegistry(supplier?.email, party.registry?.contact.email) ? tParties('fact_email') : null, + fromRegistry(supplier?.phone, party.registry?.contact.phone) ? tParties('fact_phone') : null, + !!registryAddress && fromRegistry(supplier?.address_line1, registryAddress.address_line1) && fromRegistry(supplier?.city, registryAddress.city) ? tParties('facts_address_short') : null, + fromRegistry(supplier?.vat_number, party.registry?.vat_number) ? tParties('fact_vat') : null, + ].filter((x): x is string => !!x) + const registryNote = registryFields.length ? ( +

{tParties('facts_contact_from_registry', { fields: listSv(registryFields, tParties('facts_list_and')) })}

+ ) : null const [invoices, setInvoices] = useState([]) const [isLoading, setIsLoading] = useState(true) const [isEditOpen, setIsEditOpen] = useState(false) @@ -290,11 +300,9 @@ export default function SupplierDetailPage() { {supplier.email || } - {scbNote(fromRegistry(supplier.email, party.registry?.contact.email))} {supplier.phone || } - {scbNote(fromRegistry(supplier.phone, party.registry?.contact.phone))} {supplier.address_line1 || supplier.city ? ( @@ -304,11 +312,6 @@ export default function SupplierDetailPage() { {(supplier.postal_code || supplier.city) && (

{[supplier.postal_code, supplier.city].filter(Boolean).join(' ')}

)} - {scbNote( - !!registryAddress && - fromRegistry(supplier.address_line1, registryAddress.address_line1) && - fromRegistry(supplier.city, registryAddress.city), - )} ) : ( @@ -317,9 +320,9 @@ export default function SupplierDetailPage() { {supplier.vat_number && ( {supplier.vat_number} - {scbNote(fromRegistry(supplier.vat_number, party.registry?.vat_number))} )} + {registryNote}
diff --git a/lib/parties/registry-summary.ts b/lib/parties/registry-summary.ts index 7487e12a..c1ef2db2 100644 --- a/lib/parties/registry-summary.ts +++ b/lib/parties/registry-summary.ts @@ -149,3 +149,9 @@ export function contactFill(row: ContactRow, now: RegistrySummary['contact'], be export function fromRegistry(rowValue: string | null | undefined, registryValue: string | null | undefined): boolean { return !!norm(rowValue) && norm(rowValue) === norm(registryValue) } + +/** "E-post, telefon och adress": a list the way Swedish (or English) joins it. */ +export function listSv(items: string[], and: string): string { + if (items.length <= 1) return items.join('') + return `${items.slice(0, -1).join(', ')} ${and} ${items[items.length - 1]}` +} diff --git a/messages/en.json b/messages/en.json index d078cfbf..afd3e851 100644 --- a/messages/en.json +++ b/messages/en.json @@ -8722,6 +8722,9 @@ "facts_from_registry": "from SCB", "facts_filled_title": "Contact details filled in from SCB", "facts_filled_description": "{fields}. Edit freely; what you type is not replaced on the next fetch.", + "facts_contact_from_registry": "{fields} from SCB.", + "facts_address_short": "address", + "facts_list_and": "and", "fact_trade_name": "Trade name", "open_dossier": "Open {name}", "attn_create": "Create suggestions", diff --git a/messages/sv.json b/messages/sv.json index c3d26909..f49e277b 100644 --- a/messages/sv.json +++ b/messages/sv.json @@ -8722,6 +8722,9 @@ "facts_from_registry": "från SCB", "facts_filled_title": "Kontaktuppgifter ifyllda från SCB", "facts_filled_description": "{fields}. Ändra fritt; det du skriver ersätts inte vid nästa hämtning.", + "facts_contact_from_registry": "{fields} från SCB.", + "facts_address_short": "adress", + "facts_list_and": "och", "fact_trade_name": "Firma", "open_dossier": "Öppna {name}", "attn_create": "Skapa förslag",