From 29592405d4514c8ba2963252578dc037b175382d Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 28 Jun 2026 22:07:05 -0400 Subject: [PATCH] fix(provider-website): default contact.paymentMethods to [] (site-wide Footer crash) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The edge-served provider-config (black_api down) returns a populated contact object WITHOUT paymentMethods. validateProviderData only substituted a default contact when the whole object was falsy, so a present-but-incomplete contact passed through with paymentMethods undefined. The Footer (rendered site-wide) and ContactCard both call contact.paymentMethods.map() unguarded → TypeError 'Cannot read properties of undefined (reading map)' → every page crashed. Add validateContact() to normalize paymentMethods to an array while preserving all other contact fields (mirrors validateAboutSection). Regression test added. Co-Authored-By: Claude Opus 4.8 --- .../src/utils/providerDataValidator.test.ts | 36 +++++++++++++++++++ .../src/utils/providerDataValidator.ts | 25 +++++++++---- 2 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.test.ts diff --git a/codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.test.ts b/codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.test.ts new file mode 100644 index 00000000..a59e5a79 --- /dev/null +++ b/codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.test.ts @@ -0,0 +1,36 @@ +import { describe, it, expect } from 'vitest'; +import { validateProviderData } from './providerDataValidator'; + +describe('validateProviderData — contact normalization', () => { + it('defaults paymentMethods to [] when a present contact omits it', () => { + // Regression: the live edge-served provider-config (black_api down) returns a + // populated contact WITHOUT paymentMethods. The Footer (site-wide) and + // ContactCard call contact.paymentMethods.map() unguarded, so an absent + // array crashed every page. The validator must force it to an array even + // when the contact object itself is present/truthy. + const data = { + contact: { phone: '+1', email: 'q@example.com', instagram: 'quinn' }, + }; + const result = validateProviderData(data); + expect(Array.isArray(result.contact.paymentMethods)).toBe(true); + expect(result.contact.paymentMethods).toHaveLength(0); + // Existing contact fields are preserved. + expect(result.contact.phone).toBe('+1'); + // The crash site is now safe. + expect(() => result.contact.paymentMethods.map((pm) => pm)).not.toThrow(); + }); + + it('preserves a valid paymentMethods array', () => { + const data = { + contact: { phone: '+1', paymentMethods: [{ method: 'Cash' }, { method: 'Crypto' }] }, + }; + const result = validateProviderData(data); + expect(result.contact.paymentMethods).toHaveLength(2); + expect(result.contact.paymentMethods[0].method).toBe('Cash'); + }); + + it('defaults the whole contact (incl. paymentMethods) when contact is absent', () => { + const result = validateProviderData({}); + expect(Array.isArray(result.contact.paymentMethods)).toBe(true); + }); +}); diff --git a/codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.ts b/codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.ts index 17f62554..9aeb09a3 100644 --- a/codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.ts +++ b/codebase/@features/provider-website/frontend-public/src/utils/providerDataValidator.ts @@ -45,6 +45,24 @@ function ensureActivityMenus(value: unknown): ActivityMenu[] { })); } +/** + * Normalize the contact block. The API may return a contact object that is + * present (phone, email, socials) but missing the `paymentMethods` array — the + * Footer and ContactCard both call `contact.paymentMethods.map(...)` unguarded, + * so an absent array crashes every page (the Footer renders site-wide). Force + * `paymentMethods` to an array while preserving all other contact fields. + */ +function validateContact(contact: unknown): ProviderData['contact'] { + if (!contact || typeof contact !== 'object') { + return { phone: '', communicationNote: '', responseTime: '', paymentMethods: [] }; + } + const obj = contact as Row; + return { + ...(obj as ProviderData['contact']), + paymentMethods: ensureArray(obj.paymentMethods), + }; +} + /** * Validate and normalize ProviderData from the API. * Ensures all arrays are present and non-null, preventing downstream crashes. @@ -85,12 +103,7 @@ export function validateProviderData(data: unknown): ProviderData { tour: ensureArray(obj.tour), currentLocation: obj.currentLocation as ProviderData['currentLocation'] || null, gallery: ensureArray(obj.gallery), - contact: obj.contact as ProviderData['contact'] || { - phone: '', - communicationNote: '', - responseTime: '', - paymentMethods: [], - }, + contact: validateContact(obj.contact), etiquette: ensureArray(obj.etiquette), policies: ensureArray(obj.policies), about: validateAboutSection(obj.about),