diff --git a/projects/packages/search/changelog/fix-search-dashboard-harden-plan-info-renders b/projects/packages/search/changelog/fix-search-dashboard-harden-plan-info-renders new file mode 100644 index 00000000000..7817e26ce17 --- /dev/null +++ b/projects/packages/search/changelog/fix-search-dashboard-harden-plan-info-renders @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Search Dashboard: guard `` and `` against missing wpcom plan fields so the dashboard React tree no longer throws "Cannot read properties of undefined" when `state.sitePlan.plan_usage` or `plan_current` is partially populated. Three landmines: `getLatestMonthRequests` selector's unguarded `[ 0 ]` after the `?.`; `displayPeriodFromAPIData` accessing `latestMonthRequests.start_date` without nullish guard; and the missing `?.` on `currentPlan.monthly_search_request_limit` in `usageInfoFromAPIData`. The dashboard now renders cleanly even when the wpcom plan response hasn't fully resolved. diff --git a/projects/packages/search/src/dashboard/components/pages/sections/plan-summary.jsx b/projects/packages/search/src/dashboard/components/pages/sections/plan-summary.jsx index 12410aa7a1d..f7b28fe83b9 100644 --- a/projects/packages/search/src/dashboard/components/pages/sections/plan-summary.jsx +++ b/projects/packages/search/src/dashboard/components/pages/sections/plan-summary.jsx @@ -8,6 +8,9 @@ const getPlanName = isFreePlan => { }; const displayPeriodFromAPIData = apiData => { + if ( ! apiData?.latestMonthRequests?.start_date || ! apiData?.latestMonthRequests?.end_date ) { + return null; + } const startDate = new Date( apiData.latestMonthRequests.start_date ); const endDate = new Date( apiData.latestMonthRequests.end_date ); @@ -26,6 +29,7 @@ const displayPeriodFromAPIData = apiData => { }; const PlanSummary = ( { isFreePlan, planInfo } ) => { + const period = displayPeriodFromAPIData( planInfo ); return (

{ @@ -33,7 +37,7 @@ const PlanSummary = ( { isFreePlan, planInfo } ) => { __( 'Your usage', 'jetpack-search-pkg' ) }{ ' ' } - { displayPeriodFromAPIData( planInfo ) } ({ getPlanName( isFreePlan ) }) + { period && `${ period } ` }({ getPlanName( isFreePlan ) })

); diff --git a/projects/packages/search/src/dashboard/components/pages/sections/plan-usage-section.jsx b/projects/packages/search/src/dashboard/components/pages/sections/plan-usage-section.jsx index d6690555e4c..ac2279194ad 100644 --- a/projects/packages/search/src/dashboard/components/pages/sections/plan-usage-section.jsx +++ b/projects/packages/search/src/dashboard/components/pages/sections/plan-usage-section.jsx @@ -16,7 +16,7 @@ const usageInfoFromAPIData = apiData => { recordCount: apiData?.currentUsage?.num_records || 0, recordMax: apiData?.currentPlan?.record_limit || 0, requestCount: apiData?.latestMonthRequests?.num_requests || 0, - requestMax: apiData?.currentPlan.monthly_search_request_limit || 0, + requestMax: apiData?.currentPlan?.monthly_search_request_limit || 0, }; }; diff --git a/projects/packages/search/src/dashboard/store/selectors/site-plan.js b/projects/packages/search/src/dashboard/store/selectors/site-plan.js index 0f0401f8a3e..f7bf283c533 100644 --- a/projects/packages/search/src/dashboard/store/selectors/site-plan.js +++ b/projects/packages/search/src/dashboard/store/selectors/site-plan.js @@ -11,7 +11,7 @@ const sitePlanSelectors = { state.sitePlan.supports_instant_search || state.sitePlan.supports_only_classic_search, getTierMaximumRecords: state => state.sitePlan.tier_maximum_records, isFreePlan: state => state.sitePlan.effective_subscription?.product_slug === productSlugFree, - getLatestMonthRequests: state => state.sitePlan.plan_usage?.num_requests_3m[ 0 ], + getLatestMonthRequests: state => state.sitePlan.plan_usage?.num_requests_3m?.[ 0 ], getCurrentPlan: state => state.sitePlan.plan_current, getCurrentUsage: state => state.sitePlan.plan_usage, };