diff --git a/resources/js/admin/mobile/PackageShopPage.tsx b/resources/js/admin/mobile/PackageShopPage.tsx index c966e4c..9a7fcb8 100644 --- a/resources/js/admin/mobile/PackageShopPage.tsx +++ b/resources/js/admin/mobile/PackageShopPage.tsx @@ -11,7 +11,12 @@ import { MobileCard, CTAButton, PillBadge, SkeletonCard } from './components/Pri import { useAdminTheme } from './theme'; import { getPackages, Package, getTenantPackagesOverview, TenantPackageSummary } from '../api'; import { useQuery } from '@tanstack/react-query'; -import { buildPackageComparisonRows, classifyPackageChange, selectRecommendedPackageId } from './lib/packageShop'; +import { + buildPackageComparisonRows, + classifyPackageChange, + getEnabledPackageFeatures, + selectRecommendedPackageId, +} from './lib/packageShop'; import { usePackageCheckout } from './hooks/usePackageCheckout'; export default function MobilePackageShopPage() { @@ -229,13 +234,12 @@ function PackageShopCard({ ) : null} {/* Render specific feature if it was requested */} - {Object.entries(pkg.features || {}) - .filter(([key, val]) => val === true && (!pkg.max_photos || key !== 'photos')) - .slice(0, 3) - .map(([key]) => ( - - )) - } + {getEnabledPackageFeatures(pkg) + .filter((key) => !pkg.max_photos || key !== 'photos') + .slice(0, 3) + .map((key) => ( + + ))} { const active = { @@ -65,3 +70,9 @@ describe('buildPackageComparisonRows', () => { ]); }); }); + +describe('getEnabledPackageFeatures', () => { + it('accepts array payloads', () => { + expect(getEnabledPackageFeatures({ features: ['custom_branding', ''] } as any)).toEqual(['custom_branding']); + }); +}); diff --git a/resources/js/admin/mobile/lib/packageShop.ts b/resources/js/admin/mobile/lib/packageShop.ts index fe5e643..84aaabc 100644 --- a/resources/js/admin/mobile/lib/packageShop.ts +++ b/resources/js/admin/mobile/lib/packageShop.ts @@ -17,12 +17,30 @@ export type PackageComparisonRow = featureKey: string; }; -function collectFeatures(pkg: Package | null): Set { +function normalizePackageFeatures(pkg: Package | null): string[] { if (!pkg?.features) { - return new Set(); + return []; } - return new Set(Object.entries(pkg.features).filter(([, enabled]) => enabled).map(([key]) => key)); + if (Array.isArray(pkg.features)) { + return pkg.features.filter((feature): feature is string => typeof feature === 'string' && feature.trim().length > 0); + } + + if (typeof pkg.features === 'object') { + return Object.entries(pkg.features) + .filter(([, enabled]) => enabled) + .map(([key]) => key); + } + + return []; +} + +export function getEnabledPackageFeatures(pkg: Package): string[] { + return normalizePackageFeatures(pkg); +} + +function collectFeatures(pkg: Package | null): Set { + return new Set(normalizePackageFeatures(pkg)); } function compareLimit(candidate: number | null, active: number | null): number { @@ -88,7 +106,7 @@ export function selectRecommendedPackageId( return null; } - const candidates = packages.filter((pkg) => pkg.features?.[feature]); + const candidates = packages.filter((pkg) => normalizePackageFeatures(pkg).includes(feature)); if (candidates.length === 0) { return null; } @@ -109,8 +127,8 @@ export function buildPackageComparisonRows(packages: Package[]): PackageComparis const featureKeys = new Set(); packages.forEach((pkg) => { - Object.entries(pkg.features ?? {}).forEach(([key, enabled]) => { - if (enabled && key !== 'photos') { + normalizePackageFeatures(pkg).forEach((key) => { + if (key !== 'photos') { featureKeys.add(key); } });