From fe54f6e64ac4696370f9cb04e9ced4eeb237df26 Mon Sep 17 00:00:00 2001 From: Gerhard Scheikl Date: Fri, 15 May 2026 16:20:55 +0200 Subject: [PATCH] feat(invoice): make the phone number on the PDF a clickable tel: link Wraps the phone in a @react-pdf 'Link' with src='tel:+\u2026' just like the existing email (mailto:) and website (https:) entries in the contact footer. Display string keeps the human-readable formatting (spaces, parens) while the underlying URL is normalized per RFC 3966 (digits plus an optional leading '+'). PDF readers on macOS, iOS, Android and most desktop Linux setups launch the system dialer or a VoIP app on click; readers without a 'tel' handler fall back to selecting the number. --- app/services/invoice/pdf/InvoiceDocument.tsx | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app/services/invoice/pdf/InvoiceDocument.tsx b/app/services/invoice/pdf/InvoiceDocument.tsx index 205da10..b803339 100644 --- a/app/services/invoice/pdf/InvoiceDocument.tsx +++ b/app/services/invoice/pdf/InvoiceDocument.tsx @@ -588,7 +588,11 @@ function Footer({ issuer, language }: { issuer: IssuerData; language: InvoiceLan {t.contactHeading} - {issuer.phone ? {t.phoneLabel}: {issuer.phone} : null} + {issuer.phone ? ( + + {t.phoneLabel}: {issuer.phone} + + ) : null} {issuer.email ? ( {t.emailLabel}: {issuer.email} @@ -644,6 +648,21 @@ function normaliseWebUrl(url: string): string { return `https://${trimmed.replace(/^\/\//, "")}`; } +/** + * Build a `tel:` URL from a free-form phone string. RFC 3966 allows only + * digits and a leading `+`, so we strip everything else (spaces, parens, + * dashes, slashes, dots, internal letters). The display string above the + * link keeps the human-readable formatting. + */ +function toTelUrl(phone: string): string { + const cleaned = phone.replace(/[^\d+]/g, ""); + // Keep only a single leading '+' if present. + const normalized = cleaned.startsWith("+") + ? "+" + cleaned.slice(1).replace(/\+/g, "") + : cleaned.replace(/\+/g, ""); + return `tel:${normalized}`; +} + /** * Turn a Shopify payment-gateway machine name (e.g. `shopify_payments`, * `manual`, `bogus`) or a built-in manual-payment template name (e.g.