@fluid-app/portal-sdk 0.1.457 → 0.1.458

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/{CardWidget-BbddboSI.cjs → CardWidget-8kubp1dh.cjs} +2 -2
  2. package/dist/{CardWidget-Bs0hqVjy.mjs → CardWidget-Dee9DpBH.mjs} +2 -2
  3. package/dist/{CardWidget-Bs0hqVjy.mjs.map → CardWidget-Dee9DpBH.mjs.map} +1 -1
  4. package/dist/{CardWidget-CGbAcMeq.cjs → CardWidget-YGsYcK7i.cjs} +2 -2
  5. package/dist/{CardWidget-CGbAcMeq.cjs.map → CardWidget-YGsYcK7i.cjs.map} +1 -1
  6. package/dist/{ContainerWidget-bbzSrNUJ.mjs → ContainerWidget-BVlY76wy.mjs} +2 -2
  7. package/dist/{ContainerWidget-bbzSrNUJ.mjs.map → ContainerWidget-BVlY76wy.mjs.map} +1 -1
  8. package/dist/{ContainerWidget-CRCAjF4G.cjs → ContainerWidget-Ba_Ti1Qh.cjs} +3 -3
  9. package/dist/{ContainerWidget-BOZYpAH1.cjs → ContainerWidget-DCDrjhgP.cjs} +2 -2
  10. package/dist/{ContainerWidget-BOZYpAH1.cjs.map → ContainerWidget-DCDrjhgP.cjs.map} +1 -1
  11. package/dist/{FluidProvider-KBZgRXjL.mjs → FluidProvider-C5JqeOGi.mjs} +3 -3
  12. package/dist/{FluidProvider-KBZgRXjL.mjs.map → FluidProvider-C5JqeOGi.mjs.map} +1 -1
  13. package/dist/{FluidProvider-Dm5QH6EP.cjs → FluidProvider-LYDiXUkk.cjs} +3 -3
  14. package/dist/{FluidProvider-Dm5QH6EP.cjs.map → FluidProvider-LYDiXUkk.cjs.map} +1 -1
  15. package/dist/{LayoutWidget-nea3CGc6.cjs → LayoutWidget-BKjkjoNO.cjs} +2 -2
  16. package/dist/{LayoutWidget-CS8fOr1e.cjs → LayoutWidget-CqIkNZNa.cjs} +2 -2
  17. package/dist/{LayoutWidget-CS8fOr1e.cjs.map → LayoutWidget-CqIkNZNa.cjs.map} +1 -1
  18. package/dist/{LayoutWidget-ENLd1G__.mjs → LayoutWidget-DkYKWC2P.mjs} +2 -2
  19. package/dist/{LayoutWidget-ENLd1G__.mjs.map → LayoutWidget-DkYKWC2P.mjs.map} +1 -1
  20. package/dist/{MessagingScreen-BrbkMeld.cjs → MessagingScreen-BLZt8lMN.cjs} +2 -2
  21. package/dist/{MessagingScreen-BrbkMeld.cjs.map → MessagingScreen-BLZt8lMN.cjs.map} +1 -1
  22. package/dist/{MessagingScreen-uuydnJEi.cjs → MessagingScreen-CoBvR0p4.cjs} +7 -7
  23. package/dist/{MessagingScreen-CYqzOl6D.mjs → MessagingScreen-CowNfnz0.mjs} +7 -7
  24. package/dist/{MessagingScreen-BtoXnSqJ.mjs → MessagingScreen-kftHOAO7.mjs} +2 -2
  25. package/dist/{MessagingScreen-BtoXnSqJ.mjs.map → MessagingScreen-kftHOAO7.mjs.map} +1 -1
  26. package/dist/{OrdersScreen-UaQcR3IU.cjs → OrdersScreen-B3Aa4ip-.cjs} +7 -7
  27. package/dist/{OrdersScreen-DKQmax51.cjs → OrdersScreen-C-B6_Agy.cjs} +2 -2
  28. package/dist/{OrdersScreen-DKQmax51.cjs.map → OrdersScreen-C-B6_Agy.cjs.map} +1 -1
  29. package/dist/{OrdersScreen-eqJy6_m3.mjs → OrdersScreen-DDQ8QXWo.mjs} +2 -2
  30. package/dist/{OrdersScreen-eqJy6_m3.mjs.map → OrdersScreen-DDQ8QXWo.mjs.map} +1 -1
  31. package/dist/{OrdersScreen-CutgAgyO.mjs → OrdersScreen-DPkq_mS2.mjs} +7 -7
  32. package/dist/{ProfileScreen-B5DcNq7i.cjs → ProfileScreen-CK17yi8m.cjs} +8 -8
  33. package/dist/{ProfileScreen-CealQbpo.mjs → ProfileScreen-DnlCxwh6.mjs} +3 -3
  34. package/dist/{ProfileScreen-CealQbpo.mjs.map → ProfileScreen-DnlCxwh6.mjs.map} +1 -1
  35. package/dist/{ProfileScreen-DXSnyfCO.cjs → ProfileScreen-R4ulzpPu.cjs} +3 -3
  36. package/dist/{ProfileScreen-DXSnyfCO.cjs.map → ProfileScreen-R4ulzpPu.cjs.map} +1 -1
  37. package/dist/{ProfileScreen-C4SiOL-k.mjs → ProfileScreen-ntvqBCpb.mjs} +8 -8
  38. package/dist/{ScreenRenderer-EGk5-w0h.cjs → ScreenRenderer-D0-ymcZV.cjs} +13 -4
  39. package/dist/ScreenRenderer-D0-ymcZV.cjs.map +1 -0
  40. package/dist/{ScreenRenderer-BQz-1cCn.mjs → ScreenRenderer-KJIcMne2.mjs} +13 -4
  41. package/dist/ScreenRenderer-KJIcMne2.mjs.map +1 -0
  42. package/dist/{ShopScreen-DH_TcNMs.cjs → ShopScreen-BtwvHTbN.cjs} +7 -7
  43. package/dist/{ShopScreen-B6cf4gW-.mjs → ShopScreen-CGX6YHVV.mjs} +7 -7
  44. package/dist/{ShopScreen-z2J4fRfs.mjs → ShopScreen-CUoUECrG.mjs} +2 -2
  45. package/dist/{ShopScreen-z2J4fRfs.mjs.map → ShopScreen-CUoUECrG.mjs.map} +1 -1
  46. package/dist/{ShopScreen-CcupxIut.cjs → ShopScreen-DS5-ICA-.cjs} +2 -2
  47. package/dist/{ShopScreen-CcupxIut.cjs.map → ShopScreen-DS5-ICA-.cjs.map} +1 -1
  48. package/dist/{SubscriptionsScreen-CTrOXRIz.cjs → SubscriptionsScreen-D04ldHXL.cjs} +3 -3
  49. package/dist/{SubscriptionsScreen-CTrOXRIz.cjs.map → SubscriptionsScreen-D04ldHXL.cjs.map} +1 -1
  50. package/dist/{SubscriptionsScreen-DyL-7oeE.mjs → SubscriptionsScreen-D0pn4_JR.mjs} +3 -3
  51. package/dist/{SubscriptionsScreen-DyL-7oeE.mjs.map → SubscriptionsScreen-D0pn4_JR.mjs.map} +1 -1
  52. package/dist/{SubscriptionsScreen-DdlwKkJ1.cjs → SubscriptionsScreen-J4R7V7na.cjs} +8 -8
  53. package/dist/{SubscriptionsScreen-CBEULwpU.mjs → SubscriptionsScreen-qD8Mnh2A.mjs} +8 -8
  54. package/dist/{build-widget-registry-Bg8G7GQF.cjs → build-widget-registry-DMiPjMfG.cjs} +7 -7
  55. package/dist/{build-widget-registry-Bg8G7GQF.cjs.map → build-widget-registry-DMiPjMfG.cjs.map} +1 -1
  56. package/dist/{build-widget-registry-fQIvocLa.mjs → build-widget-registry-Dveiky2q.mjs} +7 -7
  57. package/dist/{build-widget-registry-fQIvocLa.mjs.map → build-widget-registry-Dveiky2q.mjs.map} +1 -1
  58. package/dist/index.cjs +68 -25
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.d.cts.map +1 -1
  61. package/dist/index.d.mts.map +1 -1
  62. package/dist/index.mjs +68 -25
  63. package/dist/index.mjs.map +1 -1
  64. package/dist/{use-portal-card-add-3ds-flow-Caupep6C.mjs → use-portal-card-add-3ds-flow--PAh5qUt.mjs} +2 -2
  65. package/dist/{use-portal-card-add-3ds-flow-Caupep6C.mjs.map → use-portal-card-add-3ds-flow--PAh5qUt.mjs.map} +1 -1
  66. package/dist/{use-portal-card-add-3ds-flow-Cl3H-Dp7.cjs → use-portal-card-add-3ds-flow-DzFfku22.cjs} +2 -2
  67. package/dist/{use-portal-card-add-3ds-flow-Cl3H-Dp7.cjs.map → use-portal-card-add-3ds-flow-DzFfku22.cjs.map} +1 -1
  68. package/dist/utils/build-remote-widget-plugin-manifests.cjs +5 -5
  69. package/dist/utils/build-remote-widget-plugin-manifests.mjs +5 -5
  70. package/package.json +8 -8
  71. package/dist/ScreenRenderer-BQz-1cCn.mjs.map +0 -1
  72. package/dist/ScreenRenderer-EGk5-w0h.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["useBottomNavHidden","SidebarProvider","Sidebar","SidebarHeader","SidebarContent","SidebarFooter","SidebarInset","SYSTEM_NAVIGATION_ITEMS","ContactRound","Repeat","Bell","Box","Boxes","Briefcase","Calendar","ShoppingCart","CircleUser","MessageCircle","Compass","Mail","File","FileText","FileVideo","Folder","FolderOpen","Settings","Globe","GraduationCap","Heart","House","Image","ListMusic","Search","MessageSquare","PackageOpen","Star","Store","LayoutGrid","User","UserCog","Users","normalizeSlug","shellEn","useSidebar","useShellTranslation","useNativeCapabilities","useBottomNavHidden","React","Ellipsis","MobileBottomSheet","useShellTranslation","useShellTranslation","Collapsible","SidebarMenuItem","CollapsibleTrigger","SidebarMenuButton","ChevronRight","CollapsibleContent","SidebarMenuSub","normalizeSlug","useShellTranslation","SidebarMenu","SidebarGroupLabel","normalizeSlug","SidebarMenuItem","SidebarMenuButton","useActiveLocale","useLanguagesApi","storeKeys","SUPPORTED_LANGUAGES","useSidebar","useRepUser","useShellTranslation","MobileBottomSheet","ChevronLeft","Check","Smartphone","Globe","ChevronRight","Moon","Sun","LogOut","useSidebar","useShellTranslation","normalizeSlug","IconButton","ChevronLeft","Menu","useActiveLocale","useShellTranslation","useLanguagesApi","storeKeys","SUPPORTED_LANGUAGES","Popover","PopoverTrigger","Button","Globe","PopoverContent","Check","Building2","cn","Skeleton","useStore","useShellTranslation","FallbackCard","Compass","Button","useShellTranslation","alertWidgetPropertySchema","bulletListWidgetPropertySchema","calendarWidgetPropertySchema","cardWidgetPropertySchema","carouselWidgetPropertySchema","chartWidgetPropertySchema","containerWidgetPropertySchema","embedWidgetPropertySchema","imageWidgetPropertySchema","layoutWidgetPropertySchema","linkWidgetPropertySchema","listWidgetPropertySchema","mySiteWidgetPropertySchema","nestedWidgetPropertySchema","pointsWidgetPropertySchema","quickLinksWidgetPropertySchema","quickShareWidgetPropertySchema","recentActivityWidgetPropertySchema","separatorWidgetPropertySchema","shopWidgetPropertySchema","spacerWidgetPropertySchema","tableWidgetPropertySchema","textWidgetPropertySchema","toDoWidgetPropertySchema","videoWidgetPropertySchema","useRegistry","isDefaultWidgetComponent","DataAwareWidget","useShellTranslation","ProfileScreen","OrdersScreen","SubscriptionsScreen","MessagingScreen","ContactsScreen","ShopScreen","CustomersScreen","ShareablesScreen","MySiteScreen","useAccount","deniesRepSurfaces","slugifyName","isRepOnlySlug","Suspense","isSystemNavigationItem","CoreScreenPlaceholder","ShellTranslationContext","shellEn","FallbackCard","TriangleAlert","Button","Component","deleteDatabase","useShellTranslation","Moon","Sun","SidebarMenu","SidebarMenuItem","SidebarMenuButton","LogOut","isRepOnlySlug","useSidebar","useScreenHeaderSlotRefs","PanelLeft","useShellTranslation","useOptionalScreenAreaFullscreenCoordinator","useScreenAreaFullscreenSnapshot","normalizeSlug","useFluidApp","useAccount","getDefaultThemeDefinition","resolveTheme","deniesRepSurfaces","collectNavSlugs","extractSlugFromPathname","matchSlugPrefix","ScreenHeaderProvider","RepUserProvider","AppNavigationProvider","WidgetInteractionProvider","BottomNavVisibilityProvider","ScreenAreaFullscreenProvider","Toaster","Component","FluidProvider","StrictMode","useAppDefinitionApi","useCompanyScopedQueryKey","APP_DATA_QUERY_KEY","fetchRawManifest","transformManifestToRepAppData","useAppDefinitionApi","useCompanyScopedQueryKey","useThemeContext","useAccount","hasMemberPermission","memberTypeSlug","memberTypeName","canAccessRepSurfaces","usePortalTenantClient","deleteDatabase","now","daysFromNow","now","now","hoursAgo","now","daysAgo","z","useShellTranslation","useAccount","deniesRepSurfaces","getSystemNavigationBySection","Popover","PopoverTrigger","Button","LayoutGrid","PopoverContent","useAppNavigation"],"sources":["../src/types/page-template.ts","../src/registries/page-template-registry.ts","../src/core/resolve-pages.ts","../src/providers/PageTemplateProvider.tsx","../../react/src/shell/use-suppress-initial-iframe-focus-scroll.ts","../../react/src/shell/AppShellLayout.tsx","../../react/src/shell/ThemeModeContext.tsx","../../core/src/navigation/default-navigation.ts","../../react/src/components/RepIcon.tsx","../../core/src/navigation/navigation-storage.ts","../../core/src/navigation/navigation-utils.ts","../../core/src/navigation/translate-nav-label.ts","../src/shell/SdkBottomNav.tsx","../src/shell/AppShellLoading.tsx","../src/shell/CollapsibleNavItem.tsx","../src/shell/SdkNavigation.tsx","../src/shell/use-mobile-languages.ts","../src/shell/SdkMobileProfileMenu.tsx","../src/shell/SdkHeader.tsx","../src/shell/LanguageSelector.tsx","../../../company-switcher/ui/src/components/CompanyImage.tsx","../../../company-switcher/ui/src/components/CompanyItem.tsx","../../../company-switcher/ui/src/components/CompanySwitcherSkeleton.tsx","../src/shell/SdkCompanySwitcher.tsx","../src/shell/go-back-or-home.ts","../src/screens/ScreenNotFound.tsx","../src/screens/AccessDeniedScreen.tsx","../src/core/default-property-schema-registry.ts","../src/utils/warn-unknown-widget-props.ts","../src/shell/BuilderScreenView.tsx","../../core/src/member-type-content.ts","../src/shell/system-screen-map.ts","../src/shell/PageRouter.tsx","../src/shell/AppShellErrorBoundary.tsx","../src/shell/native-theme-bridge.ts","../src/utils/reset-fairshare-sdk.ts","../src/hooks/use-logout.ts","../src/shell/SdkLogoutButton.tsx","../src/navigation/filter-nav-items.ts","../../react/src/shell/ScreenHeader.tsx","../src/shell/TranslatedScreenHeader.tsx","../src/shell/AppShell.tsx","../src/config/env.ts","../src/config/defaults.ts","../src/entry/create-portal.tsx","../src/hooks/use-fluid-profile.ts","../src/hooks/use-app-definition.ts","../src/hooks/use-fluid-theme.ts","../src/hooks/use-member-type.ts","../src/hooks/use-company-switch.ts","../src/hooks/demo-data/calendar-events.ts","../src/hooks/use-calendar-events.ts","../src/hooks/demo-data/todos.ts","../src/hooks/use-todos.ts","../src/hooks/hook-types.ts","../src/hooks/demo-data/activities.ts","../src/hooks/use-activities.ts","../src/hooks/demo-data/catchups.ts","../src/hooks/use-catchups.ts","../src/hooks/demo-data/mysite.ts","../src/hooks/use-mysite.ts","../src/hooks/demo-data/conversations.ts","../src/hooks/use-conversations.ts","../src/types/screen-types.ts","../src/hooks/demo-data/contacts.ts","../src/hooks/use-contacts.ts","../../../platform/auth/src/types.ts","../../../platform/auth/src/company-switch.ts","../src/screens/core-page-ids.ts","../src/screens/index.ts","../src/shell/QuickLinksDropdown.tsx","../src/shell/AppLink.tsx","../src/scaffold/manifest.ts"],"sourcesContent":["import type { WidgetSchema } from \"./widget-schema\";\n\n/**\n * Category for organizing page templates in the registry\n */\nexport interface PageCategory {\n /** Unique identifier for the category */\n readonly id: string;\n /** Display label */\n label: string;\n /** Icon identifier (e.g., lucide icon name) */\n icon?: string;\n}\n\n/**\n * A reusable page template that can be shared across multiple navigations\n */\nexport interface PageTemplate {\n /** Unique identifier for the template */\n readonly id: string;\n /** URL-friendly slug */\n slug: string;\n /** Display name */\n name: string;\n /** Description of the template's purpose */\n description?: string;\n /** Category ID for organization */\n category: string;\n /** Tags for filtering and search */\n tags?: readonly string[];\n /** The widget tree that defines the page content */\n component_tree: readonly WidgetSchema[];\n /** Semantic version of the template */\n version: string;\n /** Whether this is a core feature that cannot be removed */\n isCore?: boolean;\n /** Default prop values that can be customized */\n defaultProps?: Readonly<Record<string, unknown>>;\n /** Thumbnail image URL for UI display */\n thumbnail?: string;\n}\n\n/**\n * Reference to a shared page template within a navigation\n */\nexport interface PageReference {\n /** ID of the page template being referenced */\n page_template_id: string;\n /** Screen ID to assign to this page in the navigation */\n screen_id: number;\n /** Optional prop overrides (only prop values, not widget structure) */\n overrides?: readonly PageOverride[];\n}\n\n/**\n * Override for a specific widget's props within a page template\n */\nexport interface PageOverride {\n /** ID of the widget to override (must match WidgetSchema.id in the template) */\n readonly widget_id: string;\n /** Props to override (merged with original props) */\n props: Readonly<Record<string, unknown>>;\n}\n\n/**\n * Built-in page category IDs\n */\nexport const PAGE_CATEGORIES = {\n CORE: \"core\",\n COMMERCE: \"commerce\",\n COMMUNICATION: \"communication\",\n DATA: \"data\",\n CUSTOM: \"custom\",\n} as const;\n\nexport type PageCategoryId =\n (typeof PAGE_CATEGORIES)[keyof typeof PAGE_CATEGORIES];\n","import type {\n PageTemplate,\n PageCategory,\n PageCategoryId,\n} from \"../types/page-template\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\n\n/**\n * Registry for managing reusable page templates.\n *\n * The registry provides a central store for page templates that can be\n * shared across multiple navigations. Core pages (like Messaging, Contacts)\n * are pre-registered and cannot be removed.\n *\n * @example\n * ```ts\n * // Register a custom page template\n * PageTemplateRegistry.register({\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Custom Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Hello' } }],\n * });\n *\n * // Get a template by ID\n * const template = PageTemplateRegistry.get('custom-dashboard');\n *\n * // List all templates in a category\n * const corePages = PageTemplateRegistry.getByCategory('core');\n * ```\n */\nclass PageTemplateRegistryImpl {\n private templates = new Map<string, PageTemplate>();\n private categories: PageCategory[] = [];\n\n constructor() {\n // Initialize default categories\n this.categories = [\n { id: PAGE_CATEGORIES.CORE, label: \"Core Features\", icon: \"star\" },\n {\n id: PAGE_CATEGORIES.COMMERCE,\n label: \"Commerce\",\n icon: \"shopping-cart\",\n },\n {\n id: PAGE_CATEGORIES.COMMUNICATION,\n label: \"Communication\",\n icon: \"message-circle\",\n },\n {\n id: PAGE_CATEGORIES.DATA,\n label: \"Data & Analytics\",\n icon: \"bar-chart\",\n },\n { id: PAGE_CATEGORIES.CUSTOM, label: \"Custom\", icon: \"puzzle\" },\n ];\n }\n\n /**\n * Register a new page template.\n * @throws Error if a template with the same ID already exists\n */\n register(template: PageTemplate): void {\n if (this.templates.has(template.id)) {\n throw new Error(\n `Page template with ID \"${template.id}\" is already registered`,\n );\n }\n this.templates.set(template.id, template);\n }\n\n /**\n * Unregister a page template by ID.\n * Core templates cannot be unregistered.\n * @returns true if the template was removed, false if it didn't exist or is a core template\n */\n unregister(id: string): boolean {\n const template = this.templates.get(id);\n if (!template) {\n return false;\n }\n if (template.isCore) {\n console.warn(\n `Cannot unregister core page template \"${id}\". Core templates are required.`,\n );\n return false;\n }\n return this.templates.delete(id);\n }\n\n /**\n * Get a page template by ID.\n */\n get(id: string): PageTemplate | undefined {\n return this.templates.get(id);\n }\n\n /**\n * Get all page templates in a specific category.\n */\n getByCategory(category: PageCategoryId | string): PageTemplate[] {\n return Array.from(this.templates.values()).filter(\n (t) => t.category === category,\n );\n }\n\n /**\n * List all registered page templates.\n */\n listAll(): PageTemplate[] {\n return Array.from(this.templates.values());\n }\n\n /**\n * List all core page templates (isCore: true).\n */\n listCore(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => t.isCore);\n }\n\n /**\n * List all non-core page templates.\n */\n listOptional(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => !t.isCore);\n }\n\n /**\n * List all registered categories.\n */\n listCategories(): PageCategory[] {\n return [...this.categories];\n }\n\n /**\n * Add a custom category.\n */\n addCategory(category: PageCategory): void {\n if (this.categories.some((c) => c.id === category.id)) {\n console.warn(`Category with ID \"${category.id}\" already exists`);\n return;\n }\n this.categories.push(category);\n }\n\n /**\n * Check if a template exists by ID.\n */\n has(id: string): boolean {\n return this.templates.has(id);\n }\n\n /**\n * Get the count of registered templates.\n */\n get size(): number {\n return this.templates.size;\n }\n\n /**\n * Clear all non-core templates.\n * Useful for testing or resetting the registry.\n */\n clearNonCore(): void {\n for (const [id, template] of this.templates) {\n if (!template.isCore) {\n this.templates.delete(id);\n }\n }\n }\n}\n\n/**\n * Global page template registry singleton.\n *\n * This registry is automatically populated with core page templates\n * (Messaging, Contacts) when the SDK is imported.\n */\nexport const PageTemplateRegistry: PageTemplateRegistryImpl =\n new PageTemplateRegistryImpl();\n","import type { Navigation, ScreenDefinition, WidgetSchema } from \"../types\";\nimport type {\n PageReference,\n PageOverride,\n PageTemplate,\n} from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\n\n/**\n * Apply prop overrides to widgets in a component tree.\n * Recursively walks widget.props.children so overrides targeting\n * nested widgets (inside LayoutWidget, ContainerWidget, etc.) are applied.\n *\n * @param componentTree - The original component tree from the page template\n * @param overrides - Array of widget-specific prop overrides\n * @returns A new component tree with overrides applied\n */\nfunction applyOverrides(\n componentTree: readonly WidgetSchema[],\n overrides: readonly PageOverride[],\n): WidgetSchema[] {\n if (!overrides.length) {\n // Return a mutable copy to satisfy ScreenDefinition.component_tree type\n return [...componentTree];\n }\n\n // Build a map of widget_id -> override for O(1) lookup (built once, shared across recursion)\n const overrideMap = new Map(overrides.map((o) => [o.widget_id, o.props]));\n\n return componentTree.map((widget) =>\n applyWidgetOverrides(widget, overrideMap),\n );\n}\n\n/**\n * Recursively apply overrides to a widget and its children.\n * Only clones widgets that actually change (have an override or contain children that might).\n */\nfunction applyWidgetOverrides(\n widget: WidgetSchema,\n overrideMap: ReadonlyMap<string, Record<string, unknown>>,\n): WidgetSchema {\n const override = widget.id ? overrideMap.get(widget.id) : undefined;\n const children = widget.props.children;\n\n // Recurse into children if they exist (LayoutWidget, ContainerWidget)\n const hasChildren = Array.isArray(children) && children.length > 0;\n\n if (!override && !hasChildren) {\n return widget;\n }\n\n const newProps = override\n ? { ...widget.props, ...override }\n : { ...widget.props };\n\n if (hasChildren) {\n newProps.children = (children as readonly WidgetSchema[]).map((child) =>\n applyWidgetOverrides(child, overrideMap),\n );\n }\n\n return { ...widget, props: newProps };\n}\n\n/**\n * Resolve a page reference to a screen definition.\n *\n * @param ref - The page reference from navigation\n * @returns A ScreenDefinition or undefined if the template doesn't exist\n */\nfunction resolvePageReference(\n ref: Readonly<PageReference>,\n): ScreenDefinition | undefined {\n const template = PageTemplateRegistry.get(ref.page_template_id);\n if (!template) {\n console.warn(\n `Page template \"${ref.page_template_id}\" not found in registry`,\n );\n return undefined;\n }\n\n // Apply any overrides to the component tree\n // Spread to create mutable array for ScreenDefinition.component_tree\n const componentTree = ref.overrides\n ? applyOverrides(template.component_tree, ref.overrides)\n : [...template.component_tree];\n\n return {\n id: ref.screen_id,\n slug: template.slug,\n name: template.name,\n component_tree: componentTree,\n };\n}\n\n/**\n * Resolve all page references and local screens into a unified screen list.\n *\n * This function merges:\n * 1. Screen definitions from page_refs (shared templates)\n * 2. Local screen definitions (for backwards compatibility and custom screens)\n *\n * When a screen_id appears in both page_refs and screens, the local screen\n * takes precedence (allows local overrides of template pages).\n *\n * @param navigation - The navigation configuration\n * @returns A unified array of ScreenDefinition objects\n *\n * @example\n * ```ts\n * const navigation: Navigation = {\n * definition_id: 1,\n * id: 1,\n * name: \"Main Nav\",\n * navigation_items: [...],\n * screens: [\n * // Local custom screen\n * { id: 1, slug: \"home\", name: \"Home\", component_tree: [...] }\n * ],\n * page_refs: [\n * // Reference to shared messaging template\n * { page_template_id: \"core-messaging\", screen_id: 2 }\n * ],\n * };\n *\n * const allScreens = resolveNavigationPages(navigation);\n * // Returns: [home screen, messaging screen from template]\n * ```\n */\nexport function resolveNavigationPages(\n navigation: Readonly<Navigation>,\n): ScreenDefinition[] {\n // Start with local screens (these take precedence)\n const localScreenIds = new Set(navigation.screens.map((s) => s.id));\n const result: ScreenDefinition[] = [...navigation.screens];\n\n // Resolve page references (skip if local screen already exists with same ID)\n if (navigation.page_refs) {\n for (const ref of navigation.page_refs) {\n if (localScreenIds.has(ref.screen_id)) {\n // Local screen takes precedence\n continue;\n }\n\n const screen = resolvePageReference(ref);\n if (screen) {\n result.push(screen);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Get all available page templates for use in navigation.\n *\n * @returns Array of page templates from the registry\n */\nexport function getAvailablePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listAll();\n}\n\n/**\n * Get core page templates that are required for basic functionality.\n *\n * @returns Array of core page templates\n */\nexport function getCorePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listCore();\n}\n\n/**\n * Get optional page templates that can be added to navigation.\n *\n * @returns Array of optional (non-core) page templates\n */\nexport function getOptionalPageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listOptional();\n}\n\n/**\n * Check if a navigation has all required core pages.\n *\n * @param navigation - The navigation to check\n * @returns Object with validation result and missing page IDs\n */\nexport function validateNavigationPages(navigation: Readonly<Navigation>): {\n readonly valid: boolean;\n readonly missingCorePages: readonly string[];\n} {\n const corePages = PageTemplateRegistry.listCore();\n const referencedTemplateIds = new Set(\n navigation.page_refs?.map((ref) => ref.page_template_id) ?? [],\n );\n\n // Check which core pages are referenced\n const missingCorePages = corePages\n .filter((page) => !referencedTemplateIds.has(page.id))\n .map((page) => page.id);\n\n return {\n valid: missingCorePages.length === 0,\n missingCorePages,\n };\n}\n","import React, {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport type { PageTemplate } from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { resolveNavigationPages } from \"../core/resolve-pages\";\nimport type { Navigation, ScreenDefinition } from \"../types\";\n\n/** Stable empty array for the default `templates` prop (avoids new reference each render) */\nconst EMPTY_TEMPLATES: readonly PageTemplate[] = [];\n\n/**\n * Context value for page template resolution.\n * All properties are readonly since context values should not be mutated by consumers.\n */\ninterface PageTemplateContextValue {\n /**\n * Resolve a navigation's page_refs and screens into a unified screen list\n */\n readonly resolvePages: (navigation: Navigation) => ScreenDefinition[];\n /**\n * Get all available page templates\n */\n readonly listTemplates: () => PageTemplate[];\n /**\n * Get a specific template by ID\n */\n readonly getTemplate: (id: string) => PageTemplate | undefined;\n /**\n * Check if a template exists\n */\n readonly hasTemplate: (id: string) => boolean;\n}\n\nconst PageTemplateContext = createContext<PageTemplateContextValue | null>(\n null,\n);\n\n/**\n * Props for PageTemplateProvider\n */\ninterface PageTemplateProviderProps {\n children: React.ReactNode;\n /**\n * Additional custom page templates to register.\n * These are registered when the provider mounts and unregistered when it unmounts.\n */\n templates?: readonly PageTemplate[];\n}\n\n/**\n * Provider for page template resolution.\n *\n * This provider:\n * 1. Registers any custom templates passed via props\n * 2. Provides methods for resolving navigation pages\n * 3. Cleans up custom templates on unmount\n *\n * @example\n * ```tsx\n * // With custom templates\n * const customTemplates: PageTemplate[] = [\n * {\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Custom Dashboard' } }],\n * },\n * ];\n *\n * <PageTemplateProvider templates={customTemplates}>\n * <App />\n * </PageTemplateProvider>\n *\n * // Without custom templates (uses only core templates)\n * <PageTemplateProvider>\n * <App />\n * </PageTemplateProvider>\n * ```\n */\nexport function PageTemplateProvider({\n children,\n templates = EMPTY_TEMPLATES,\n}: PageTemplateProviderProps): React.JSX.Element {\n // Track which templates we registered so we can clean them up\n const registeredIds = useRef<string[]>([]);\n\n // Derive a stable primitive key from template IDs\n // (rerender-dependencies rule: use primitive dependencies in effects)\n const templateKey = templates.map((t) => t.id).join(\",\");\n\n // Register custom templates when template IDs change\n useEffect(() => {\n const registered: string[] = [];\n\n for (const template of templates) {\n if (!PageTemplateRegistry.has(template.id)) {\n try {\n PageTemplateRegistry.register(template);\n registered.push(template.id);\n } catch (error) {\n console.warn(\n `Failed to register page template \"${template.id}\":`,\n error,\n );\n }\n }\n }\n\n registeredIds.current = registered;\n\n // Cleanup when template IDs change or on unmount\n return () => {\n for (const id of registeredIds.current) {\n PageTemplateRegistry.unregister(id);\n }\n registeredIds.current = [];\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [templateKey]);\n\n // Memoize context value to prevent unnecessary re-renders\n // Use `satisfies` to validate shape at compile time while preserving inference\n const contextValue = useMemo(\n () =>\n ({\n resolvePages: resolveNavigationPages,\n listTemplates: () => PageTemplateRegistry.listAll(),\n getTemplate: (id: string) => PageTemplateRegistry.get(id),\n hasTemplate: (id: string) => PageTemplateRegistry.has(id),\n }) satisfies PageTemplateContextValue,\n [],\n );\n\n return (\n <PageTemplateContext.Provider value={contextValue}>\n {children}\n </PageTemplateContext.Provider>\n );\n}\n\n/**\n * Hook to access page template functionality.\n *\n * @throws Error if used outside of PageTemplateProvider\n *\n * @example\n * ```tsx\n * function NavigationRenderer({ navigation }: { navigation: Navigation }) {\n * const { resolvePages } = usePageTemplates();\n * const screens = resolvePages(navigation);\n *\n * return (\n * <div>\n * {screens.map((screen) => (\n * <Screen key={screen.id} definition={screen} />\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePageTemplates(): PageTemplateContextValue {\n const context = useContext(PageTemplateContext);\n if (!context) {\n throw new Error(\n \"usePageTemplates must be used within a PageTemplateProvider\",\n );\n }\n return context;\n}\n\n/**\n * Hook to resolve navigation pages directly.\n * Convenience wrapper around usePageTemplates().resolvePages.\n *\n * @param navigation - The navigation to resolve\n * @returns Array of resolved screen definitions\n */\nexport function useResolvedPages(navigation: Navigation): ScreenDefinition[] {\n const { resolvePages } = usePageTemplates();\n return useMemo(() => resolvePages(navigation), [resolvePages, navigation]);\n}\n","import { useEffect, type RefObject } from \"react\";\n\n/**\n * Iframes embed opaque third-party content that can call `.focus()` on its\n * internal elements during load — video players, calendar pickers, form\n * inputs, search boxes. The browser then scrolls the nearest scrolling\n * ancestor to bring the focused iframe into view, yanking the viewport to a\n * widget the user didn't ask for.\n *\n * Suppress that scroll on the given scroll container until the user has\n * explicitly interacted with the page; afterwards, focus-scrolls are\n * treated as user-initiated and left alone. The container's `scrollTop` at\n * effect setup is captured and restored, so any pre-effect scroll position\n * (e.g. from future hash-anchor restoration) is preserved.\n */\nexport function useSuppressInitialIframeFocusScroll(\n ref: RefObject<HTMLElement | null>,\n): void {\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n const initialScrollTop = el.scrollTop;\n let userInteracted = false;\n\n const handleFocusIn = (event: FocusEvent) => {\n if (userInteracted) return;\n if (!(event.target instanceof HTMLIFrameElement)) return;\n requestAnimationFrame(() => {\n if (!userInteracted) el.scrollTop = initialScrollTop;\n });\n };\n\n const markInteracted = () => {\n userInteracted = true;\n };\n\n el.addEventListener(\"focusin\", handleFocusIn);\n window.addEventListener(\"pointerdown\", markInteracted, { once: true });\n window.addEventListener(\"keydown\", markInteracted, { once: true });\n window.addEventListener(\"wheel\", markInteracted, {\n once: true,\n passive: true,\n });\n\n return () => {\n el.removeEventListener(\"focusin\", handleFocusIn);\n window.removeEventListener(\"pointerdown\", markInteracted);\n window.removeEventListener(\"keydown\", markInteracted);\n window.removeEventListener(\"wheel\", markInteracted);\n };\n }, [ref]);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useLayoutEffect, useRef } from \"react\";\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarInset,\n SidebarProvider,\n} from \"./sidebar\";\nimport { useSuppressInitialIframeFocusScroll } from \"./use-suppress-initial-iframe-focus-scroll\";\nimport { useBottomNavHidden } from \"./BottomNavVisibilityContext\";\n\nexport interface AppShellLayoutProps {\n /** Navigation content rendered inside the sidebar's scrollable area */\n sidebarContent: React.ReactNode;\n /** Header content rendered above the main content area */\n headerContent: React.ReactNode;\n /** Main page content */\n children: React.ReactNode;\n /** Optional slot at the top of the sidebar (e.g. logo, search) */\n sidebarHeader?: React.ReactNode;\n /** Optional slot at the bottom of the sidebar (e.g. user info) */\n sidebarFooter?: React.ReactNode;\n /** Content rendered after SidebarInset (e.g. MobileBottomNav) */\n afterContent?: React.ReactNode;\n /** Enable bottom nav mode (hides sidebar on mobile, adds bottom padding) */\n useBottomNav?: boolean;\n}\n\n/**\n * Pure visual frame that replicates the RepApp layout:\n * - 13rem collapsible sidebar\n * - 52px header area\n * - rounded-xl bg-background shadow-lg content area with bg-muted gutters\n *\n * This component handles zero business logic — it simply provides the visual shell.\n */\nexport function AppShellLayout({\n sidebarContent,\n headerContent,\n children,\n sidebarHeader,\n sidebarFooter,\n afterContent,\n useBottomNav = false,\n}: AppShellLayoutProps): React.JSX.Element {\n const screenContentRef = useRef<HTMLDivElement>(null);\n const bottomNavHidden = useBottomNavHidden();\n\n // Reserve space for the fixed bottom nav on mobile. When a drill-down surface\n // hides the nav (e.g. an open conversation), drop the padding entirely so the\n // now full-screen surface owns its bottom edge — including the safe-area\n // inset (e.g. the messaging composer already pads for the home indicator).\n // Padding it here too would double the inset. Animated to match the nav's\n // slide, so content and chrome move together.\n const mobileBottomPadding = useBottomNav\n ? bottomNavHidden\n ? \"max-md:pb-0\"\n : \"max-md:pb-[calc(3.5rem+env(safe-area-inset-bottom))]\"\n : \"\";\n\n // Publish the screen content slot's measured pixel height as\n // `--portal-fullscreen-embed-height` so descendants (e.g. EmbedWidget in\n // inline + Full Screen mode) can anchor against the visible portal screen\n // area instead of the browser viewport. Mirrors the admin builder's\n // ScreenContentSlot in `packages/portal/builder/src/components/core/RepApp.tsx`.\n useLayoutEffect(() => {\n const el = screenContentRef.current;\n if (!el) return;\n const update = () => {\n el.style.setProperty(\n \"--portal-fullscreen-embed-height\",\n `${el.clientHeight}px`,\n );\n };\n update();\n const obs = new ResizeObserver(update);\n obs.observe(el);\n return () => obs.disconnect();\n }, []);\n\n useSuppressInitialIframeFocusScroll(screenContentRef);\n\n return (\n <SidebarProvider useBottomNav={useBottomNav}>\n <div className=\"bg-muted relative flex max-h-dvh w-full overflow-hidden\">\n {/* Navigation Sidebar */}\n <Sidebar collapsible=\"icon\">\n {sidebarHeader && <SidebarHeader>{sidebarHeader}</SidebarHeader>}\n <SidebarContent className=\"p-4\">{sidebarContent}</SidebarContent>\n {sidebarFooter && <SidebarFooter>{sidebarFooter}</SidebarFooter>}\n </Sidebar>\n\n {/* Main Content Area */}\n <SidebarInset className=\"flex flex-1 flex-col overflow-hidden\">\n {/* Header */}\n {headerContent}\n\n {/* Screen Content */}\n <div\n className={`bg-muted min-h-0 flex-1 transition-[padding] duration-300 ease-out motion-reduce:transition-none md:pt-4 md:pr-4 md:pb-4 md:pl-2 ${mobileBottomPadding}`}\n >\n <div\n ref={screenContentRef}\n data-fluid-portal-screen-area=\"\"\n tabIndex={-1}\n className=\"ring-foreground/5 scrollbar-none bg-background text-foreground h-full overflow-auto rounded-xl shadow-sm ring-1 max-md:rounded-b-none max-md:shadow-none max-md:ring-0\"\n >\n {children}\n </div>\n </div>\n </SidebarInset>\n\n {/* After content (e.g. MobileBottomNav) */}\n {afterContent}\n </div>\n </SidebarProvider>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport {\n createContext,\n type ReactNode,\n use,\n useCallback,\n useMemo,\n useSyncExternalStore,\n} from \"react\";\n\nexport type ThemeMode = \"auto\" | \"light\" | \"dark\";\n\nexport type DisplayMode = \"light\" | \"dark\";\n\nexport interface ThemeModeContextValue {\n mode: ThemeMode;\n displayMode: DisplayMode;\n setMode: (mode: ThemeMode) => void;\n autoModeEnabled: boolean;\n cycleMode: () => void;\n dataAttribute: string | undefined;\n}\n\nconst ThemeModeContext = createContext<ThemeModeContextValue | null>(null);\n\nconst darkMediaQuery =\n typeof window !== \"undefined\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n\nfunction subscribeToPrefersColorScheme(callback: () => void) {\n darkMediaQuery?.addEventListener(\"change\", callback);\n return () => darkMediaQuery?.removeEventListener(\"change\", callback);\n}\n\nfunction getSystemPrefersDark(): boolean {\n return darkMediaQuery?.matches ?? false;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\ninterface ThemeModeProviderProps {\n children: ReactNode;\n mode: ThemeMode;\n onModeChange: (mode: ThemeMode) => void;\n /** When false, auto mode is skipped in the cycle (light↔dark only). Default true. */\n autoModeEnabled?: boolean;\n}\n\nexport function ThemeModeProvider({\n children,\n mode,\n onModeChange,\n autoModeEnabled = true,\n}: ThemeModeProviderProps): React.JSX.Element {\n const systemPrefersDark = useSyncExternalStore(\n subscribeToPrefersColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\n const systemMode: DisplayMode = systemPrefersDark ? \"dark\" : \"light\";\n\n const displayMode: DisplayMode = mode === \"auto\" ? systemMode : mode;\n\n const cycleMode = useCallback(() => {\n if (autoModeEnabled) {\n // Toggle displayed mode. If target matches system preference, use \"auto\".\n const target: DisplayMode = displayMode === \"light\" ? \"dark\" : \"light\";\n onModeChange(target === systemMode ? \"auto\" : target);\n } else {\n // light ↔ dark\n onModeChange(mode === \"light\" ? \"dark\" : \"light\");\n }\n }, [mode, displayMode, systemMode, onModeChange, autoModeEnabled]);\n\n const dataAttribute = getThemeModeAttribute(mode);\n\n const value = useMemo(\n () => ({\n mode,\n displayMode,\n setMode: onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n }),\n [\n mode,\n displayMode,\n onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n ],\n );\n\n return (\n <ThemeModeContext.Provider value={value}>\n {children}\n </ThemeModeContext.Provider>\n );\n}\n\n/** Access the current theme mode, setter, and cycle helper. Must be used within a `ThemeModeProvider`. */\nexport function useThemeMode(): ThemeModeContextValue {\n const ctx = use(ThemeModeContext);\n if (!ctx) {\n throw new Error(\"useThemeMode must be used within a ThemeModeProvider\");\n }\n return ctx;\n}\n\n/** Maps a ThemeMode to the value for `data-theme-mode`. Returns undefined for \"auto\". */\nexport function getThemeModeAttribute(mode: ThemeMode): string | undefined {\n return mode === \"auto\" ? undefined : mode;\n}\n","import {\n SYSTEM_NAVIGATION_ITEMS,\n type SystemNavigationSlug,\n} from \"./system-navigation-items\";\nimport type { NavigationItem } from \"../types/navigation\";\n\n/**\n * Build a NavigationItem from a system navigation slug.\n * Icons and labels come from SYSTEM_NAVIGATION_ITEMS (single source of truth).\n */\nfunction systemNavItem(slug: SystemNavigationSlug): NavigationItem {\n const item = SYSTEM_NAVIGATION_ITEMS[slug];\n return {\n slug: item.slug,\n label: item.label,\n icon: item.icon,\n children: [],\n };\n}\n\nfunction sectionHeader(label: string): NavigationItem {\n return { label, children: [] };\n}\n\n/**\n * Returns the default navigation items for a portal app when no\n * API data is available. All icons/labels derive from\n * SYSTEM_NAVIGATION_ITEMS so they can never drift.\n */\nexport function getDefaultNavigation(): NavigationItem[] {\n return [\n sectionHeader(\"We Commerce\"),\n systemNavItem(\"shop\"),\n systemNavItem(\"messages\"),\n systemNavItem(\"contacts\"),\n systemNavItem(\"profile\"),\n systemNavItem(\"orders\"),\n systemNavItem(\"subscriptions\"),\n sectionHeader(\"Marketing Assets\"),\n systemNavItem(\"share/products\"),\n ];\n}\n","import type React from \"react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n Bell,\n Box,\n Boxes,\n Briefcase,\n Calendar,\n CircleUser,\n Compass,\n ContactRound,\n File,\n FileText,\n FileVideo,\n Folder,\n FolderOpen,\n Globe,\n GraduationCap,\n Heart,\n House,\n Image,\n LayoutGrid,\n ListMusic,\n Mail,\n MessageCircle,\n MessageSquare,\n PackageOpen,\n Repeat,\n Search,\n Settings,\n ShoppingCart,\n Star,\n Store,\n User,\n UserCog,\n Users,\n} from \"lucide-react\";\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n \"address-card\": ContactRound,\n \"arrows-repeat\": Repeat,\n bell: Bell,\n box: Box,\n \"boxes-stacked\": Boxes,\n briefcase: Briefcase,\n calendar: Calendar,\n \"cart-shopping\": ShoppingCart,\n \"circle-user\": CircleUser,\n comment: MessageCircle,\n compass: Compass,\n envelope: Mail,\n file: File,\n \"file-lines\": FileText,\n \"file-video\": FileVideo,\n folder: Folder,\n \"folder-open\": FolderOpen,\n gear: Settings,\n globe: Globe,\n \"graduation-cap\": GraduationCap,\n heart: Heart,\n house: House,\n image: Image,\n \"list-music\": ListMusic,\n \"magnifying-glass\": Search,\n message: MessageSquare,\n \"package-open\": PackageOpen,\n star: Star,\n store: Store,\n \"table-cells-large\": LayoutGrid,\n user: User,\n \"user-gear\": UserCog,\n users: Users,\n};\n\ninterface RepIconProps {\n name: string;\n className?: string;\n}\n\nexport function RepIcon({ name, className }: RepIconProps): React.JSX.Element {\n const Icon = ICON_MAP[name] ?? File;\n return <Icon className={className} />;\n}\n","/**\n * Utility functions for managing navigation state in localStorage\n */\n\nconst STORAGE_PREFIX = \"portal-app-last-tab\";\n\n/**\n * Gets the localStorage key for a specific app and top-level section\n */\nfunction getStorageKey(appDefinitionId: number, topLevelSlug: string): string {\n return `${STORAGE_PREFIX}-${appDefinitionId}-${topLevelSlug}`;\n}\n\n/**\n * Retrieves the last visited tab slug for a given section\n */\nexport function getLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): string | null {\n if (typeof window === \"undefined\") return null;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n return localStorage.getItem(key);\n } catch (error) {\n console.warn(\"Failed to read from localStorage:\", error);\n return null;\n }\n}\n\n/**\n * Stores the last visited tab slug for a given section\n */\nexport function setLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n tabSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.setItem(key, tabSlug);\n } catch (error) {\n console.warn(\"Failed to write to localStorage:\", error);\n }\n}\n\n/**\n * Clears the last visited tab for a given section\n */\nexport function clearLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.removeItem(key);\n } catch (error) {\n console.warn(\"Failed to remove from localStorage:\", error);\n }\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\nimport { getLastVisitedTab } from \"./navigation-storage\";\n\n/**\n * True when `normalizedRoute` is the section's slug or a descendant of it, so a\n * tab stays selected while drilling into its sub-routes (e.g. `shop` stays\n * active on a product detail at `shop/123`). The trailing \"/\" keeps the match\n * on a path boundary so `shop` does not match an unrelated `shopping` route.\n */\nfunction routeMatchesSection(\n normalizedRoute: string,\n slug: string | undefined,\n): boolean {\n const normalizedSlug = normalizeSlug(slug);\n if (!normalizedSlug) return false;\n return (\n normalizedRoute === normalizedSlug ||\n normalizedRoute.startsWith(`${normalizedSlug}/`)\n );\n}\n\n/**\n * Helper function to determine if the current route is within a top-level section\n */\nexport function isInSection(\n topLevelItem: NavigationItem,\n currentRoute: string,\n): boolean {\n const normalizedCurrentRoute = normalizeSlug(currentRoute);\n\n if (topLevelItem.children && topLevelItem.children.length > 0) {\n return topLevelItem.children.some((child: NavigationItem) =>\n routeMatchesSection(normalizedCurrentRoute, child.slug),\n );\n }\n\n return routeMatchesSection(normalizedCurrentRoute, topLevelItem.slug);\n}\n\n/**\n * Get the target slug when clicking a top-level navigation item\n */\nexport function getNavigationTarget(\n item: NavigationItem,\n appDefinitionId: number,\n): string | null {\n // If it has children, it's always a section parent — navigate to a child\n if (item.children && item.children.length > 0) {\n // Try to get the last visited tab from localStorage (only if parent has a slug for the key)\n if (item.slug) {\n const lastVisitedSlug = getLastVisitedTab(appDefinitionId, item.slug);\n\n // If we have a last visited tab and it exists in the children, use it\n if (lastVisitedSlug) {\n const normalizedLastVisited = normalizeSlug(lastVisitedSlug);\n const childExists = item.children.some((child: NavigationItem) => {\n const normalizedChildSlug = normalizeSlug(child.slug);\n return normalizedChildSlug === normalizedLastVisited;\n });\n if (childExists) {\n return normalizedLastVisited;\n }\n }\n }\n\n // Default to the first child's slug\n const firstChild = item.children[0];\n if (firstChild?.slug) {\n return normalizeSlug(firstChild.slug);\n }\n\n return null;\n }\n\n // If it's a leaf node with a screen, return its slug directly (normalized)\n if (item.screen_id && item.slug) {\n return normalizeSlug(item.slug);\n }\n\n // If it's a leaf node with just a slug (system nav items like account, messages, etc.)\n if (item.slug) {\n return normalizeSlug(item.slug);\n }\n\n return null;\n}\n","import type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport type { ShellDict } from \"../shell-translation-dictionary\";\nimport { shellEn } from \"../shell-translation-dictionary\";\n\n/**\n * Derive a shell translation key from a navigation slug.\n *\n * Converts slugs like \"app-download\" → \"nav_label_app_download\"\n * and \"share/media\" → \"nav_label_share_media\".\n */\nfunction slugToTranslationKey(slug: string): string {\n return `nav_label_${slug.replace(/-/g, \"_\").replace(/\\//g, \"_\")}`;\n}\n\n/**\n * Derive a shell translation key from a section header label.\n *\n * Converts labels like \"We Commerce\" → \"nav_section_we_commerce\"\n * and \"Marketing Assets\" → \"nav_section_marketing_assets\".\n */\nfunction sectionToTranslationKey(label: string): string {\n return `nav_section_${label.toLowerCase().replace(/\\s+/g, \"_\")}`;\n}\n\n/**\n * Check whether a builder has customized the label by comparing it against the\n * English default. If the fallback label differs from the English dictionary\n * value, the builder intentionally renamed the nav item and we should honour\n * their custom label rather than overriding it with a translation.\n */\nfunction isCustomLabel(key: string, fallbackLabel: string): boolean {\n const englishDefault = (shellEn as Record<string, string>)[key];\n return englishDefault !== undefined && fallbackLabel !== englishDefault;\n}\n\n/**\n * Translate a navigation item label using the shell translation dictionary.\n *\n * Looks up `nav_label_<normalized_slug>` in the dictionary. Falls back to the\n * raw `label` when:\n * - the slug is absent or has no matching translation key\n * - the builder has customised the label (differs from the English default)\n */\nexport function translateNavLabel(\n api: TranslationApi<ShellDict>,\n slug: string | undefined,\n fallbackLabel: string,\n): string {\n if (!slug) return fallbackLabel;\n const t = api.t as (key: string) => string;\n const slugKey = slugToTranslationKey(slug);\n\n if (!isCustomLabel(slugKey, fallbackLabel)) {\n const translated = t(slugKey);\n if (translated !== slugKey) return translated;\n }\n\n // Slug didn't match (e.g. account has slug \"profile\" but label \"Account\").\n // Try a key derived from the label itself before falling back to raw English.\n const labelKey = `nav_label_${fallbackLabel.toLowerCase().replace(/[-\\s]+/g, \"_\")}`;\n if (labelKey !== slugKey) {\n const translated = t(labelKey);\n if (translated !== labelKey) return translated;\n }\n\n return fallbackLabel;\n}\n\n/**\n * Translate a sidebar section header label using the shell translation dictionary.\n *\n * Looks up `nav_section_<normalized_label>` in the dictionary. Falls back to the\n * raw `label` when no matching translation key exists or when the builder has\n * customised the section name.\n */\nexport function translateSectionLabel(\n api: TranslationApi<ShellDict>,\n label: string,\n): string {\n const key = sectionToTranslationKey(label);\n // If the builder renamed the section, honour their custom label\n if (isCustomLabel(key, label)) return label;\n const translated = (api.t as (key: string) => string)(key);\n return translated === key ? label : translated;\n}\n","import * as React from \"react\";\nimport { Ellipsis } from \"lucide-react\";\nimport { MobileBottomSheet } from \"@fluid-app/ui-components/components/MobileBottomSheet\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n isInSection,\n getNavigationTarget,\n} from \"@fluid-app/portal-core/navigation/utils\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { useNativeCapabilities } from \"@fluid-app/portal-core/native-capabilities-api-context\";\nimport { useBottomNavHidden } from \"@fluid-app/portal-react/shell/BottomNavVisibilityContext\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport type { NavigationItem } from \"../types/navigation\";\n\ninterface SdkBottomNavProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n appDefinitionId?: number;\n maxVisibleItems?: number;\n}\n\nexport function SdkBottomNav({\n navItems,\n currentSlug,\n onNavigate,\n appDefinitionId = 0,\n maxVisibleItems = 6,\n}: SdkBottomNavProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const shellApi = useShellTranslation();\n const { t } = shellApi;\n const nativeCapabilities = useNativeCapabilities();\n // Drill-down surfaces (e.g. an open conversation) hide the nav for a native\n // full-screen feel. Kept mounted so it can slide/fade rather than pop out.\n const hidden = useBottomNavHidden();\n const [moreOpen, setMoreOpen] = React.useState(false);\n\n if (!isMobile || !useBottomNav) return null;\n\n const navigableItems = navItems\n .filter((item) => !item.parent_id)\n .filter((item) => item.slug || (item.children && item.children.length > 0));\n\n const hasOverflow = navigableItems.length > maxVisibleItems;\n const visibleItems = hasOverflow\n ? navigableItems.slice(0, maxVisibleItems - 1)\n : navigableItems;\n const overflowItems = hasOverflow\n ? navigableItems.slice(maxVisibleItems - 1)\n : [];\n\n // The \"More\" tab counts as active when any overflow item is in section.\n const overflowIsActive = overflowItems.some((item) =>\n isInSection(item, currentSlug),\n );\n\n const handleItemClick = (item: NavigationItem) => {\n const target = getNavigationTarget(item, appDefinitionId);\n if (target) {\n nativeCapabilities.haptic(\"light\");\n onNavigate(target);\n }\n setMoreOpen(false);\n };\n\n const handleMoreClick = () => {\n nativeCapabilities.haptic(\"light\");\n setMoreOpen(true);\n };\n\n return (\n <>\n <nav\n data-fluid-bottom-nav=\"\"\n data-hidden={hidden ? \"\" : undefined}\n aria-hidden={hidden || undefined}\n inert={hidden}\n className={`border-border bg-sidebar fixed right-0 bottom-0 left-0 z-40 flex h-[calc(3.5rem+env(safe-area-inset-bottom))] items-start justify-around border-t pb-[env(safe-area-inset-bottom)] transition-[transform,opacity] duration-300 ease-out motion-reduce:transition-none ${\n hidden\n ? \"pointer-events-none translate-y-full opacity-0\"\n : \"translate-y-0 opacity-100\"\n }`}\n >\n {visibleItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n aria-current={isActive ? \"page\" : undefined}\n className={`flex h-14 flex-1 touch-manipulation flex-col items-center justify-center gap-0.5 pt-2 pb-1 text-[10px] font-medium transition-[color,transform] duration-150 active:scale-95 ${\n isActive ? \"text-primary\" : \"text-muted-foreground\"\n }`}\n >\n <RepIcon name={item.icon || \"file\"} className=\"size-5\" />\n <span className=\"max-w-[72px] truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n </button>\n );\n })}\n\n {hasOverflow && (\n <button\n type=\"button\"\n onClick={handleMoreClick}\n aria-current={overflowIsActive ? \"page\" : undefined}\n className={`flex h-14 flex-1 touch-manipulation flex-col items-center justify-center gap-0.5 pt-2 pb-1 text-[10px] font-medium transition-[color,transform] duration-150 active:scale-95 ${\n overflowIsActive ? \"text-primary\" : \"text-muted-foreground\"\n }`}\n >\n <Ellipsis className=\"size-5\" />\n <span>{t(\"nav_more\")}</span>\n </button>\n )}\n </nav>\n\n <MobileBottomSheet\n open={moreOpen}\n onOpenChange={setMoreOpen}\n title={t(\"nav_more\")}\n surface=\"sidebar\"\n >\n {overflowItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors ${\n isActive\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent\"\n }`}\n >\n <RepIcon name={item.icon || \"file\"} className=\"size-5\" />\n <span>{translateNavLabel(shellApi, item.slug, item.label)}</span>\n </button>\n );\n })}\n </MobileBottomSheet>\n </>\n );\n}\n","import { useEffect } from \"react\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\nconst SPIN_STYLE_ID = \"fluid-app-shell-loading-spin\";\n\n/**\n * Inject the spin keyframes style once into the document head.\n */\nfunction ensureSpinStyle(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(SPIN_STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = SPIN_STYLE_ID;\n style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;\n document.head.appendChild(style);\n}\n\n/**\n * Full-page loading spinner shown while AppShell is fetching app data\n * for the first time. Uses inline styles (not Tailwind) since the theme\n * hasn't loaded yet.\n */\nexport function AppShellLoading(): React.JSX.Element {\n const { t } = useShellTranslation();\n\n useEffect(() => {\n ensureSpinStyle();\n }, []);\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n }}\n >\n <div\n style={{\n width: \"40px\",\n height: \"40px\",\n border: \"3px solid #e5e7eb\",\n borderTopColor: \"#3b82f6\",\n borderRadius: \"50%\",\n animation: \"spin 1s linear infinite\",\n }}\n />\n <p\n style={{\n marginTop: \"1rem\",\n color: \"#6b7280\",\n fontSize: \"0.875rem\",\n }}\n >\n {t(\"loading\")}\n </p>\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { ChevronRight } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport {\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n Collapsible,\n CollapsibleTrigger,\n CollapsibleContent,\n} from \"@fluid-app/ui-primitives\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport interface CollapsibleNavItemProps {\n item: NavigationItem;\n isActive: boolean;\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function CollapsibleNavItem({\n item,\n isActive,\n currentSlug,\n onNavigate,\n}: CollapsibleNavItemProps): React.JSX.Element {\n const [open, setOpen] = useState(isActive);\n const shellApi = useShellTranslation();\n\n useEffect(() => {\n if (isActive) setOpen(true);\n }, [isActive]);\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n asChild\n className=\"group/collapsible\"\n >\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton>\n <RepIcon name={item.icon || \"file\"} />\n <span className=\"truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n <ChevronRight className=\"ml-auto size-3 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => {\n const childSlug = normalizeSlug(child.slug);\n const childIsActive = normalizeSlug(currentSlug) === childSlug;\n\n return (\n <SidebarMenuItem key={child.slug || child.label}>\n <SidebarMenuButton\n isActive={childIsActive}\n onClick={() => onNavigate(childSlug)}\n >\n <RepIcon name={child.icon || \"file\"} className=\"size-3\" />\n <span>\n {translateNavLabel(shellApi, child.slug, child.label)}\n </span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n );\n}\n","import {\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { isInSection } from \"@fluid-app/portal-core/navigation/utils\";\nimport {\n translateNavLabel,\n translateSectionLabel,\n} from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { CollapsibleNavItem } from \"./CollapsibleNavItem\";\n\nexport interface SdkNavigationProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function SdkNavigation({\n navItems,\n currentSlug,\n onNavigate,\n}: SdkNavigationProps): React.JSX.Element {\n const shellApi = useShellTranslation();\n\n return (\n <SidebarMenu>\n {navItems.map((item, index) => {\n // Skip items without slug AND without label\n if (!item.slug && !item.label) return null;\n\n const isActive = isInSection(item, currentSlug);\n const hasChildren = item.children?.length > 0;\n\n // Items with children — collapsible\n if (hasChildren) {\n return (\n <CollapsibleNavItem\n key={item.slug || item.label}\n item={item}\n isActive={isActive}\n currentSlug={currentSlug}\n onNavigate={onNavigate}\n />\n );\n }\n\n // Items without a slug are section headers\n if (!item.slug) {\n return (\n <SidebarGroupLabel key={item.id ?? `section-${index}`}>\n {translateSectionLabel(shellApi, item.label)}\n </SidebarGroupLabel>\n );\n }\n\n // Leaf item — no children\n const itemSlug = normalizeSlug(item.slug);\n\n return (\n <SidebarMenuItem key={item.id ?? itemSlug}>\n <SidebarMenuButton\n isActive={isActive}\n onClick={() => onNavigate(itemSlug)}\n >\n <RepIcon name={item.icon || \"file\"} />\n <span className=\"truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenu>\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useLanguagesApi } from \"@fluid-app/store-core/languages-api-context\";\nimport { storeKeys } from \"../account/query-keys\";\nimport { SUPPORTED_LANGUAGES } from \"../hooks/use-portal-active-locale\";\n\nexport interface MobileLanguageOption {\n readonly iso: string;\n readonly name: string;\n}\n\nexport interface UseMobileLanguagesResult {\n readonly languages: readonly MobileLanguageOption[];\n readonly locale: string;\n readonly setLocale: (iso: string) => void;\n readonly currentName: string;\n}\n\n/**\n * Resolve the language options to offer in the mobile switcher: the company's\n * enabled languages, falling back to the full supported set if that request\n * hasn't returned, failed, or came back empty (better to over-offer than to\n * strand the user with no way to switch).\n */\nexport function useMobileLanguages(): UseMobileLanguagesResult {\n const { locale, setLocale } = useActiveLocale();\n const languagesApi = useLanguagesApi();\n\n const { data } = useQuery({\n queryKey: storeKeys.languages(),\n queryFn: () => languagesApi.listLanguages({ limit: 100 }),\n staleTime: 5 * 60 * 1000,\n });\n\n const enabledCodes = new Set((data?.languages ?? []).map((l) => l.code));\n const filtered = SUPPORTED_LANGUAGES.filter((l) => enabledCodes.has(l.iso));\n const languages = filtered.length > 0 ? filtered : SUPPORTED_LANGUAGES;\n const current = languages.find((l) => l.iso === locale);\n\n return { languages, locale, setLocale, currentName: current?.name ?? locale };\n}\n","import { useState, useCallback } from \"react\";\nimport {\n Moon,\n Sun,\n LogOut,\n Smartphone,\n Globe,\n Check,\n ChevronRight,\n ChevronLeft,\n} from \"lucide-react\";\nimport { MobileBottomSheet } from \"@fluid-app/ui-components/components/MobileBottomSheet\";\nimport { useMobileLanguages } from \"./use-mobile-languages\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useRepUser } from \"@fluid-app/portal-widgets/contexts\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\nexport interface SdkMobileProfileMenuProps {\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nfunction getInitials(name: string | null | undefined): string {\n if (!name) return \"U\";\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n (parts[0]?.[0] ?? \"\") + (parts[parts.length - 1]?.[0] ?? \"\")\n ).toUpperCase();\n }\n return (name[0] ?? \"U\").toUpperCase();\n}\n\nexport function SdkMobileProfileMenu({\n onNavigate,\n onLogout,\n}: SdkMobileProfileMenuProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const [isOpen, setIsOpen] = useState(false);\n // Which view the sheet shows: the main profile menu or the language list\n // (an inline drill-down within the same sheet — no second sheet).\n const [view, setView] = useState<\"main\" | \"language\">(\"main\");\n const user = useRepUser();\n const themeMode = useThemeMode();\n const languages = useMobileLanguages();\n const { t } = useShellTranslation();\n\n const handleNavigate = useCallback(\n (slug: string) => {\n setIsOpen(false);\n onNavigate(slug);\n },\n [onNavigate],\n );\n\n const handleOpenChange = useCallback((open: boolean) => {\n setIsOpen(open);\n // Always reset to the main view when the sheet closes.\n if (!open) setView(\"main\");\n }, []);\n\n if (!isMobile || !useBottomNav) return null;\n\n const initials = getInitials(user?.name);\n\n return (\n <>\n <button\n type=\"button\"\n onClick={() => setIsOpen(true)}\n className=\"bg-background text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-full p-1\"\n aria-label={t(\"profile_open_menu\")}\n >\n {user?.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? t(\"profile_user_fallback\")}\n className=\"size-8 rounded-full object-cover\"\n />\n ) : (\n <span className=\"flex size-8 items-center justify-center text-xs font-medium\">\n {initials}\n </span>\n )}\n </button>\n\n <MobileBottomSheet\n open={isOpen}\n onOpenChange={handleOpenChange}\n surface=\"sidebar\"\n title={\n view === \"language\" ? (\n <button\n type=\"button\"\n onClick={() => setView(\"main\")}\n className=\"text-foreground -ml-1 flex items-center gap-1\"\n aria-label={t(\"nav_back\")}\n >\n <ChevronLeft className=\"size-5\" />\n <span>{t(\"language_select\")}</span>\n </button>\n ) : (\n t(\"profile_title\")\n )\n }\n >\n {view === \"language\" ? (\n <div className=\"flex flex-col pb-2\">\n {languages.languages.map((lang) => {\n const isActive = lang.iso === languages.locale;\n return (\n <button\n key={lang.iso}\n type=\"button\"\n onClick={() => {\n languages.setLocale(lang.iso);\n setView(\"main\");\n }}\n className=\"hover:bg-sidebar-accent flex w-full items-center justify-between gap-3 rounded-lg px-3 py-3.5 text-left\"\n >\n <span\n className={\n isActive\n ? \"text-foreground text-base font-semibold\"\n : \"text-foreground text-base\"\n }\n >\n {lang.name}\n </span>\n {isActive && (\n <Check className=\"text-primary size-5 shrink-0\" />\n )}\n </button>\n );\n })}\n </div>\n ) : (\n <>\n {user && (\n <div className=\"border-border flex items-center gap-3 border-b px-3 py-4\">\n {user.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? t(\"profile_user_fallback\")}\n className=\"size-10 rounded-full object-cover\"\n />\n ) : (\n <span className=\"bg-muted flex size-10 items-center justify-center rounded-full text-sm font-medium\">\n {initials}\n </span>\n )}\n <div className=\"min-w-0 flex-1\">\n {user.name && (\n <p className=\"text-foreground truncate text-sm font-semibold\">\n {user.name}\n </p>\n )}\n {user.email && (\n <p className=\"text-muted-foreground truncate text-xs\">\n {user.email}\n </p>\n )}\n </div>\n </div>\n )}\n\n <div className=\"border-border border-b py-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"app-download\")}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <Smartphone className=\"size-5\" />\n <span>{t(\"profile_app_download\")}</span>\n </button>\n </div>\n\n <div className=\"space-y-1 py-2\">\n <button\n type=\"button\"\n onClick={() => setView(\"language\")}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <Globe className=\"size-5 shrink-0\" />\n <span>{t(\"language_select\")}</span>\n <span className=\"text-muted-foreground ml-auto flex min-w-0 items-center gap-1\">\n <span className=\"truncate\">{languages.currentName}</span>\n <ChevronRight className=\"size-4 shrink-0\" />\n </span>\n </button>\n\n <button\n type=\"button\"\n onClick={themeMode.cycleMode}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n {themeMode.mode === \"dark\" ? (\n <Moon className=\"size-5\" />\n ) : (\n <Sun className=\"size-5\" />\n )}\n <span>\n {themeMode.mode === \"light\"\n ? t(\"theme_light\")\n : t(\"theme_dark\")}\n </span>\n </button>\n\n {onLogout && (\n <button\n type=\"button\"\n onClick={onLogout}\n className=\"text-destructive hover:bg-sidebar-accent flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <LogOut className=\"size-5\" />\n <span>{t(\"logout\")}</span>\n </button>\n )}\n </div>\n </>\n )}\n </MobileBottomSheet>\n </>\n );\n}\n","import { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { IconButton } from \"@fluid-app/ui-primitives\";\nimport { ChevronLeft, Menu } from \"lucide-react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { SdkMobileProfileMenu } from \"./SdkMobileProfileMenu\";\n\nexport interface SdkHeaderProps {\n mobileTabs?: NavigationItem[];\n currentSlug: string;\n /** Current page title, shown on the left of the mobile top bar. */\n title?: string;\n /**\n * When provided, a native back button is shown at the start of the mobile\n * top bar. Set only on drill-down screens (e.g. a product detail) so tapping\n * it returns to the parent list.\n */\n onBack?: () => void;\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nexport function SdkHeader({\n mobileTabs,\n currentSlug,\n title,\n onBack,\n onNavigate,\n onLogout,\n}: SdkHeaderProps): React.JSX.Element | null {\n const sidebar = useSidebar();\n const shellApi = useShellTranslation();\n const { t } = shellApi;\n\n // Localize the title the same way the desktop header does (translateNavLabel),\n // so nav-item titles aren't left as raw English on mobile. The \"More\" screen\n // isn't a tenant nav item, so it has no derived title — fall back to the\n // localized \"More\" label there.\n const resolvedTitle = title\n ? translateNavLabel(shellApi, currentSlug, title)\n : normalizeSlug(currentSlug) === \"more\"\n ? t(\"nav_more\")\n : undefined;\n\n const isMobileWithBottomNav = sidebar.isMobile && sidebar.useBottomNav;\n const isMobileWithHamburger = sidebar.isMobile && !sidebar.useBottomNav;\n\n // Desktop has nothing to render here (theme toggle moved to sidebar footer).\n // Skip the entire header so we don't reserve a 52px gutter for empty space.\n if (!isMobileWithBottomNav && !isMobileWithHamburger) {\n return null;\n }\n\n return (\n <header className=\"bg-sidebar shrink-0 pt-[env(safe-area-inset-top)]\">\n {isMobileWithBottomNav ? (\n <div className=\"flex h-[52px] items-center gap-2 px-4\">\n {onBack ? (\n <IconButton\n icon={ChevronLeft}\n aria-label={t(\"nav_back\")}\n onClick={onBack}\n className=\"border-border/60 bg-background/70 -ml-1 flex-none rounded-full border shadow-sm backdrop-blur-md transition-transform active:scale-95 [&_svg]:size-5\"\n />\n ) : null}\n {resolvedTitle ? (\n <h1 className=\"text-sidebar-foreground min-w-0 flex-1 truncate text-[17px] leading-tight font-semibold tracking-[-0.01em]\">\n {resolvedTitle}\n </h1>\n ) : null}\n <div className=\"text-muted-foreground ml-auto flex flex-none items-center justify-end gap-2\">\n <SdkMobileProfileMenu onNavigate={onNavigate} onLogout={onLogout} />\n </div>\n </div>\n ) : (\n <div className=\"flex h-[52px] items-center px-6\">\n <button\n type=\"button\"\n onClick={sidebar.toggleSidebar}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label={t(\"nav_toggle_sidebar\")}\n >\n <Menu className=\"size-5\" />\n </button>\n </div>\n )}\n\n {/* Mobile child tabs — shown below header when parent has children */}\n {isMobileWithBottomNav && mobileTabs && mobileTabs.length > 0 && (\n <nav className=\"scrollbar-none flex items-center gap-1 overflow-x-auto px-4\">\n {mobileTabs.map((tab) => {\n const tabSlug = normalizeSlug(tab.slug);\n if (!tabSlug) return null;\n\n const isActive = normalizeSlug(currentSlug) === tabSlug;\n\n return (\n <button\n key={tab.id ?? tabSlug}\n type=\"button\"\n onClick={() => onNavigate(tabSlug)}\n className={`border-b-2 px-4 py-2.5 text-sm font-medium whitespace-nowrap transition-colors ${\n isActive\n ? \"border-primary text-foreground\"\n : \"text-muted-foreground hover:border-border hover:text-foreground border-transparent\"\n }`}\n >\n {translateNavLabel(shellApi, tab.slug, tab.label)}\n </button>\n );\n })}\n </nav>\n )}\n </header>\n );\n}\n","import { useState } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { Globe, Check } from \"lucide-react\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { useLanguagesApi } from \"@fluid-app/store-core/languages-api-context\";\nimport { storeKeys } from \"../account/query-keys\";\nimport { SUPPORTED_LANGUAGES } from \"../hooks/use-portal-active-locale\";\n\nexport function LanguageSelector(): React.JSX.Element {\n const [open, setOpen] = useState(false);\n const { locale, setLocale } = useActiveLocale();\n const { t } = useShellTranslation();\n const languagesApi = useLanguagesApi();\n\n const { data: languagesData } = useQuery({\n queryKey: storeKeys.languages(),\n queryFn: () => languagesApi.listLanguages({ limit: 100 }),\n staleTime: 5 * 60 * 1000,\n });\n\n // Fall back to the full supported list if the company-languages request\n // hasn't returned (loading), failed, or came back empty — better to over-\n // offer than strand the user with no way to switch language.\n const enabledCodes = new Set(\n (languagesData?.languages ?? []).map((l) => l.code),\n );\n const filtered = SUPPORTED_LANGUAGES.filter((l) => enabledCodes.has(l.iso));\n const languagesToRender =\n filtered.length > 0 ? filtered : SUPPORTED_LANGUAGES;\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shrink-0 gap-1.5\"\n aria-label={t(\"language_select\")}\n >\n <Globe className=\"size-3\" />\n <span className=\"text-xs uppercase\">{locale.split(\"_\")[0]}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className=\"bg-popover z-50 w-56 rounded-lg p-1 shadow-lg\"\n >\n {languagesToRender.map((lang) => (\n <Button\n key={lang.iso}\n variant=\"ghost\"\n className=\"h-auto w-full justify-between px-3 py-2 text-left text-sm whitespace-normal\"\n onClick={() => {\n setLocale(lang.iso);\n setOpen(false);\n }}\n >\n <span>{lang.name}</span>\n {lang.iso === locale && <Check className=\"size-4\" />}\n </Button>\n ))}\n </PopoverContent>\n </Popover>\n );\n}\n","import { useState } from \"react\";\nimport { Building2 } from \"lucide-react\";\n\nexport function CompanyImage({\n src,\n alt,\n size,\n}: {\n size: number;\n src: string;\n alt: string;\n}) {\n const [error, setError] = useState(false);\n\n if (!src || error)\n return (\n <Building2\n style={{ height: size, width: size }}\n className=\"text-muted-foreground/25\"\n />\n );\n\n return (\n <img\n className=\"max-h-8 max-w-8 object-contain\"\n alt={alt}\n src={src}\n width={size}\n height={size}\n onError={() => setError(true)}\n />\n );\n}\n","import type { Company } from \"@fluid-app/company-switcher-core\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { CompanyImage } from \"./CompanyImage\";\n\nexport function CompanyItem({\n logo_url,\n icon_url,\n name,\n compact = false,\n}: Omit<Company, \"id\"> & { compact?: boolean }) {\n const resolvedSrc = icon_url || logo_url || \"\";\n return (\n <>\n <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg\">\n <CompanyImage alt={name} src={resolvedSrc} size={32} />\n </div>\n <div\n className={cn(\n \"grid text-left text-sm leading-tight\",\n compact ? \"flex-shrink-0\" : \"flex-1\",\n )}\n >\n <span className=\"truncate font-semibold\">{name}</span>\n </div>\n </>\n );\n}\n","import { Skeleton } from \"@fluid-app/ui-primitives\";\n\nexport function CompanySwitcherSkeleton({ show }: { show: boolean }) {\n if (!show) return null;\n\n return (\n <div className=\"flex items-center gap-4 p-2\">\n <Skeleton className=\"size-5 shrink-0 rounded-sm bg-current\" />\n <Skeleton className=\"h-4 w-36 bg-current\" />\n </div>\n );\n}\n","import { useStore } from \"../hooks/use-store\";\nimport {\n CompanyItem,\n CompanySwitcherSkeleton,\n} from \"@fluid-app/company-switcher-ui\";\n\n/**\n * Static company display for single-tenant portal.\n * Shows logo + name in the sidebar header. No switching — just branding.\n */\nexport function SdkCompanySwitcher(): React.JSX.Element | null {\n const { data: store, isLoading } = useStore();\n\n if (isLoading) {\n return <CompanySwitcherSkeleton show />;\n }\n\n if (!store) {\n return null;\n }\n\n return (\n <div className=\"flex w-full items-center gap-2 rounded-md p-2\">\n <CompanyItem\n logo_url={store.logo_url}\n icon_url={store.icon_url}\n name={store.name || store.subdomain}\n />\n </div>\n );\n}\n","/**\n * Navigate one step back in the browser history if anything is on the stack;\n * otherwise navigate to the SPA root. Used by fallback screens (error\n * boundary, 404) to give the user a way out without risking a navigation\n * outside the SPA when no prior history exists.\n */\nexport function goBackOrHome(): void {\n if (window.history.length > 1) {\n window.history.back();\n } else {\n window.location.assign(\"/\");\n }\n}\n","import { Button } from \"@fluid-app/ui-primitives\";\nimport { Compass } from \"lucide-react\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { goBackOrHome } from \"../shell/go-back-or-home\";\nimport { FallbackCard } from \"./FallbackCard\";\n\n/**\n * Rendered by `PageRouter` when the current slug doesn't match any custom\n * page, system screen, or builder screen. This is a true \"not found\" — the\n * portal isn't configured to render anything for this path.\n */\nexport function ScreenNotFound({\n name,\n}: {\n readonly name?: string;\n}): React.JSX.Element {\n const { t } = useShellTranslation();\n return (\n <FallbackCard\n icon={<Compass />}\n title={t(\"not_found_title\")}\n description={\n name ? t(\"not_found_named\", { name }) : t(\"not_found_generic\")\n }\n actions={\n <Button variant=\"default\" onClick={goBackOrHome}>\n {t(\"error_go_back\")}\n </Button>\n }\n />\n );\n}\n","import { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\n/**\n * Shown when a customer attempts to access a rep-only page via direct URL.\n */\nexport function AccessDeniedScreen(): React.JSX.Element {\n const { t } = useShellTranslation();\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"border-border max-w-sm rounded-lg border border-dashed p-8 text-center\">\n <h2 className=\"text-foreground text-xl font-semibold\">\n {t(\"access_denied_title\")}\n </h2>\n <p className=\"text-muted-foreground mt-2\">\n {t(\"access_denied_description\")}\n </p>\n <p className=\"text-muted-foreground mt-1 text-sm\">\n {t(\"access_denied_detail\")}\n </p>\n </div>\n </div>\n );\n}\n","/**\n * Default property schema registry for SDK apps.\n * Maps widget type names to their editable property schemas from\n * portal-widgets — the same widget set covered by\n * `DEFAULT_SDK_WIDGET_REGISTRY` in `./default-widget-registry`.\n *\n * Used to validate authored widget props at render time (see\n * `warnUnknownWidgetProps` in `../utils/warn-unknown-widget-props`), not for\n * the builder's property-panel UI — that registry\n * (`DEFAULT_PROPERTY_SCHEMAS` in `@fluid-app/portal-builder`) is assembled\n * separately from the same per-widget schema exports and may include\n * builder-only widgets this one doesn't.\n */\n\nimport type { PropertySchemaRegistry } from \"@fluid-app/portal-core/registries\";\nimport {\n alertWidgetPropertySchema,\n bulletListWidgetPropertySchema,\n calendarWidgetPropertySchema,\n cardWidgetPropertySchema,\n carouselWidgetPropertySchema,\n chartWidgetPropertySchema,\n containerWidgetPropertySchema,\n embedWidgetPropertySchema,\n imageWidgetPropertySchema,\n layoutWidgetPropertySchema,\n linkWidgetPropertySchema,\n listWidgetPropertySchema,\n mySiteWidgetPropertySchema,\n nestedWidgetPropertySchema,\n pointsWidgetPropertySchema,\n quickLinksWidgetPropertySchema,\n quickShareWidgetPropertySchema,\n recentActivityWidgetPropertySchema,\n separatorWidgetPropertySchema,\n spacerWidgetPropertySchema,\n tableWidgetPropertySchema,\n textWidgetPropertySchema,\n toDoWidgetPropertySchema,\n videoWidgetPropertySchema,\n} from \"@fluid-app/portal-widgets/widgets\";\n// ShopWidget is imported via its direct subpath — see default-widget-registry.ts\n// for the --isolatedDeclarations rationale; the same barrel-avoidance applies\n// to its property schema.\nimport { shopWidgetPropertySchema } from \"@fluid-app/portal-widgets/widgets/ShopWidget\";\n\nexport const DEFAULT_PROPERTY_SCHEMA_REGISTRY: PropertySchemaRegistry = {\n AlertWidget: alertWidgetPropertySchema,\n BulletListWidget: bulletListWidgetPropertySchema,\n CalendarWidget: calendarWidgetPropertySchema,\n CardWidget: cardWidgetPropertySchema,\n CarouselWidget: carouselWidgetPropertySchema,\n ChartWidget: chartWidgetPropertySchema,\n ContainerWidget: containerWidgetPropertySchema,\n EmbedWidget: embedWidgetPropertySchema,\n ImageWidget: imageWidgetPropertySchema,\n LayoutWidget: layoutWidgetPropertySchema,\n LinkWidget: linkWidgetPropertySchema,\n ListWidget: listWidgetPropertySchema,\n MySiteWidget: mySiteWidgetPropertySchema,\n NestedWidget: nestedWidgetPropertySchema,\n PointsWidget: pointsWidgetPropertySchema,\n QuickLinksWidget: quickLinksWidgetPropertySchema,\n QuickShareWidget: quickShareWidgetPropertySchema,\n RecentActivityWidget: recentActivityWidgetPropertySchema,\n SeparatorWidget: separatorWidgetPropertySchema,\n ShopWidget: shopWidgetPropertySchema,\n SpacerWidget: spacerWidgetPropertySchema,\n TableWidget: tableWidgetPropertySchema,\n TextWidget: textWidgetPropertySchema,\n ToDoWidget: toDoWidgetPropertySchema,\n VideoWidget: videoWidgetPropertySchema,\n};\n","import type {\n PropertyField,\n PropertySchemaRegistry,\n WidgetPropertySchema,\n} from \"@fluid-app/portal-core/registries\";\n\n/**\n * Props every widget instance may legitimately receive that are never part\n * of a widget's own editable property schema:\n * - Standard React/DOM pass-through props (`className`, `style`, `children`,\n * `id`, `key`).\n * - Structural identity props the screen-renderer pipeline injects itself\n * for container widgets so they can support drag/select wiring\n * (`widgetId`, `widgetPath`) — see `ScreenRenderer.tsx` in\n * `@fluid-app/portal-widgets/core` and `WidgetContent` in\n * `@fluid-app/portal-builder/core/widget-rendering`, which both inject\n * these for the same widget nodes rendered here.\n * Never warn about these even though they never appear in a widget's\n * `fields` list.\n */\nexport const ALWAYS_ALLOWED_WIDGET_PROPS: ReadonlySet<string> = new Set([\n \"className\",\n \"style\",\n \"children\",\n \"id\",\n \"key\",\n \"widgetId\",\n \"widgetPath\",\n]);\n\n/** Maximum Levenshtein distance for a \"did you mean\" suggestion to be shown. */\nconst MAX_SUGGESTION_DISTANCE = 3;\n\n/**\n * Common authoring aliases → the canonical prop name to suggest. Levenshtein\n * only catches spelling slips; these are *semantic* misses (the motivating\n * incident authored `content` where the schema says `description` — edit\n * distance 9, far beyond any sane typo threshold). A suggestion is only shown\n * when the canonical name actually exists in the widget's schema.\n */\nconst PROP_NAME_SYNONYMS: Readonly<Record<string, string>> = {\n content: \"description\",\n text: \"description\",\n body: \"description\",\n copy: \"description\",\n message: \"description\",\n heading: \"title\",\n header: \"title\",\n label: \"title\",\n};\n\n/** `${widgetType}::${propName}` pairs already warned about this session. */\nconst warnedPairs = new Set<string>();\n\n// Vite exposes `import.meta.env.DEV`; other bundlers/runtimes (Next.js, Node,\n// Jest/CJS) don't define it at all. The optional chain means a missing/absent\n// `env` can never throw, so this can never fire outside a Vite dev build —\n// mirrors the existing `import.meta.env?.DEV` check in `FluidProvider.tsx`.\n// Written as a direct property access (no intermediate `as` cast) so it\n// compiles to a literal `import.meta.env` substring: the portal-sdk Jest\n// transform text-replaces exactly that substring with `globalThis.__VITE_ENV__`\n// at test time (see `jest-swc-transform.cjs`), which is how tests toggle\n// dev/prod mode. A cast that splits \"meta\" from \".env\" in the compiled output\n// would slip past that replacement and crash Jest on the raw `import.meta`\n// syntax.\nfunction isDevBuild(): boolean {\n return import.meta.env?.DEV === true;\n}\n\n/**\n * Levenshtein edit distance between two strings. Small inline helper (no new\n * dependency) — only ever called with short widget-prop-name strings.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n let previousRow: number[] = Array.from({ length: b.length + 1 }, (_, j) => j);\n\n for (let i = 1; i <= a.length; i++) {\n const currentRow: number[] = [i];\n for (let j = 1; j <= b.length; j++) {\n const substitutionCost = a[i - 1] === b[j - 1] ? 0 : 1;\n // Bounds are guaranteed by the loop ranges; noUncheckedIndexedAccess\n // still types these as possibly-undefined, so fall back defensively.\n const deletion = (previousRow[j] ?? Infinity) + 1;\n const insertion = (currentRow[j - 1] ?? Infinity) + 1;\n const substitution = (previousRow[j - 1] ?? Infinity) + substitutionCost;\n currentRow.push(Math.min(deletion, insertion, substitution));\n }\n previousRow = currentRow;\n }\n\n return previousRow[b.length] ?? Math.max(a.length, b.length);\n}\n\n/**\n * Finds the suggestion for an unknown prop: a known semantic synonym first\n * (see {@link PROP_NAME_SYNONYMS}), then the nearest known key by Levenshtein\n * distance within {@link MAX_SUGGESTION_DISTANCE}.\n */\nfunction findNearestKey(\n propName: string,\n candidates: readonly string[],\n): string | undefined {\n const synonym = PROP_NAME_SYNONYMS[propName.toLowerCase()];\n if (synonym && candidates.includes(synonym)) return synonym;\n\n let nearest: string | undefined;\n let nearestDistance = MAX_SUGGESTION_DISTANCE + 1;\n\n for (const candidate of candidates) {\n const distance = levenshteinDistance(propName, candidate);\n if (distance < nearestDistance) {\n nearestDistance = distance;\n nearest = candidate;\n }\n }\n\n return nearestDistance <= MAX_SUGGESTION_DISTANCE ? nearest : undefined;\n}\n\n/**\n * Collects every prop name a widget's property schema actually recognizes as\n * a `widget.props` key:\n * - `borderRadius` fields are composite — their top-level `key` is a schema\n * grouping identifier, not a real widget prop, so its four corner keys are\n * expanded instead (mirrors `extractPropertyValues` in\n * `@fluid-app/portal-core/registries`).\n * - `dataSource` fields are excluded entirely. Per the same\n * `extractPropertyValues` reference, this field type only exists so the\n * property panel can gate other fields' visibility on whether a data\n * source is configured — its value is read from `widget.dataSource` (the\n * top-level, sibling-of-`props` config), never from `widget.props`.\n * Authoring a literal `props.dataSource` is a real mistake (the config\n * belongs one level up), so its field `key` must not be treated as an\n * accepted prop here.\n */\nfunction collectKnownPropKeys(fields: readonly PropertyField[]): string[] {\n const keys: string[] = [];\n for (const field of fields) {\n if (field.type === \"borderRadius\") {\n keys.push(...Object.values(field.keys));\n } else if (field.type !== \"dataSource\") {\n keys.push(field.key);\n }\n }\n return keys;\n}\n\n/**\n * Merges schema field keys with `dataSourceTargetProps` — props a data\n * source may populate (e.g. ListWidget's `items`) that aren't necessarily\n * declared as editable fields — into one deduped list. Used for both the\n * known/unknown check and what's surfaced to the author (the \"Known props:\"\n * list and suggestion candidates), so a typo of a data-source-only prop\n * still gets a correct suggestion and the valid name is actually listed.\n */\nfunction collectAllKnownKeys(schema: WidgetPropertySchema): string[] {\n const fieldKeys = collectKnownPropKeys(schema.fields);\n const targetProps = schema.dataSourceTargetProps ?? [];\n return Array.from(new Set([...fieldKeys, ...targetProps]));\n}\n\nexport interface WarnUnknownWidgetPropsInput {\n /** The widget's `type` discriminant (e.g. \"TextWidget\"). */\n readonly widgetType: string;\n /** The authored props from the widget's JSON node (`widget.props`). */\n readonly props: Readonly<Record<string, unknown>>;\n /** Registry of built-in property schemas to validate against. */\n readonly registry: PropertySchemaRegistry;\n}\n\n/**\n * Dev-only console warning for widget props not declared in the widget's\n * registered property schema. Guards against the class of bug where an\n * authored screen sets a prop the widget doesn't know about (typo'd name,\n * stale field from a prior schema version) — the prop is silently dropped\n * and the widget renders its placeholder defaults with no signal to the\n * author.\n *\n * No-ops entirely outside a Vite dev build (checked first, before any prop\n * iteration, so there's zero overhead in production bundles) and for widget\n * types with no registered schema (custom/company widgets) — there's nothing\n * to validate against, so we skip silently rather than warn.\n *\n * Warns at most once per (widgetType, propName) pair per session.\n */\nexport function warnUnknownWidgetProps({\n widgetType,\n props,\n registry,\n}: WarnUnknownWidgetPropsInput): void {\n if (!isDevBuild()) return;\n\n const schema = registry[widgetType];\n if (!schema) return;\n\n const knownKeys = collectAllKnownKeys(schema);\n const knownKeySet = new Set(knownKeys);\n\n for (const propName of Object.keys(props)) {\n if (knownKeySet.has(propName)) continue;\n if (ALWAYS_ALLOWED_WIDGET_PROPS.has(propName)) continue;\n\n const pairKey = `${widgetType}::${propName}`;\n if (warnedPairs.has(pairKey)) continue;\n warnedPairs.add(pairKey);\n\n const suggestion = findNearestKey(propName, knownKeys);\n const suggestionText = suggestion ? ` Did you mean \"${suggestion}\"?` : \"\";\n\n console.warn(\n `[portal] ${widgetType} received unknown prop \"${propName}\" — not in its schema.${suggestionText} Known props: ${knownKeys.join(\", \")}.`,\n );\n }\n}\n\n/**\n * Test-only: clears the per-session (widgetType, propName) dedupe set so\n * \"warns once\" behavior can be verified across multiple test cases.\n */\nexport function resetUnknownWidgetPropWarnings(): void {\n warnedPairs.clear();\n}\n","/**\n * BuilderScreenView — renders a builder screen's component_tree with\n * DataAwareWidget support for data-bound widgets.\n *\n * Uses the shared ScreenRenderer from portal-widgets under the hood,\n * but wraps data-bound widgets with DataAwareWidget from portal-core.\n */\n\nimport React, { memo } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { ScreenDefinition } from \"@fluid-app/portal-core/types\";\nimport { DataAwareWidget } from \"@fluid-app/portal-react/data-sources/DataAwareWidget\";\nimport { useRegistry } from \"@fluid-app/portal-widgets/contexts\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { DEFAULT_PROPERTY_SCHEMA_REGISTRY } from \"../core/default-property-schema-registry\";\nimport { isDefaultWidgetComponent } from \"../core/default-widget-registry\";\nimport { warnUnknownWidgetProps } from \"../utils/warn-unknown-widget-props\";\n\ninterface WidgetRendererProps {\n widget: WidgetSchema;\n index: number;\n}\n\n/**\n * Renders a single widget, wrapping with DataAwareWidget if it has a dataSource.\n */\nfunction WidgetRenderer({ widget, index }: WidgetRendererProps) {\n const registry = useRegistry();\n const Component = registry[widget.type];\n\n if (!Component) {\n console.warn(\n `[BuilderScreenView] Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n // Dev-only: warns once per (widgetType, propName) pair when an authored\n // screen sets a prop the widget's schema doesn't declare — the prop is\n // otherwise silently dropped and the widget falls back to its placeholder\n // defaults with no signal to whoever authored the screen. Checked against\n // `widget.props` (the authored JSON), not any data-source-merged props\n // below, since those are only ever populated for keys the schema already\n // declares. No-ops outside dev builds and for widget types with no\n // registered schema (custom/company widgets).\n //\n // `registry` (from context) is the runtime registry apps can extend or\n // override — it isn't necessarily `DEFAULT_SDK_WIDGET_REGISTRY`. Only run\n // the check when `Component` is actually the built-in the schema\n // describes; otherwise an app-overridden built-in (different prop shape)\n // would get false positives, and a custom widget reusing a built-in type\n // name would get validated against the wrong contract.\n if (isDefaultWidgetComponent(widget.type, Component)) {\n warnUnknownWidgetProps({\n widgetType: widget.type,\n props: widget.props,\n registry: DEFAULT_PROPERTY_SCHEMA_REGISTRY,\n });\n }\n\n if (widget.dataSource) {\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <DataAwareWidget widget={widget} Component={Component} />\n </div>\n );\n }\n\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <Component {...widget.props} />\n </div>\n );\n}\n\nexport interface BuilderScreenViewProps {\n /** The screen definition to render */\n screen: ScreenDefinition;\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\n/**\n * Renders a builder screen's component_tree with full data source support.\n * Widgets with `dataSource` config are automatically wrapped with `DataAwareWidget`\n * which fetches data and merges it with static props before rendering.\n */\nfunction BuilderScreenViewImpl({\n screen,\n className,\n}: BuilderScreenViewProps): React.JSX.Element | null {\n const { t } = useShellTranslation();\n const widgets = screen.component_tree;\n\n if (!widgets || widgets.length === 0) {\n return (\n <div className={className ?? \"h-full w-full\"}>\n <div className=\"text-muted-foreground flex h-full items-center justify-center\">\n <p>{t(\"screen_no_content\")}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className={className ?? \"h-full w-full\"}>\n {widgets.map((widget, index) => {\n if (!widget) return null;\n return (\n <WidgetRenderer\n key={widget.id ?? index}\n widget={widget}\n index={index}\n />\n );\n })}\n </div>\n );\n}\n\nexport const BuilderScreenView: React.MemoExoticComponent<\n typeof BuilderScreenViewImpl\n> = memo(BuilderScreenViewImpl);\n","import type { ComponentType } from \"react\";\n\n/**\n * Per-member-type portal content.\n *\n * Lets a portal serve different screens to different member types — e.g.\n * an `affiliate` sees a different dashboard at the `home` slug than a\n * `customer` does — without forking the whole app. Keyed by the real\n * member-type slug (from `member_type_detail.slug`), then by page slug.\n *\n * Resolution order in the router: a per-type page for the current\n * member's slug wins over the global `customPages` entry for the same\n * slug, which in turn wins over the built-in system screen. A member type\n * with no entry falls through to the global content unchanged.\n */\n\n/** A portal page component, receiving the resolved slug and rest params. */\nexport type CustomPageComponent = ComponentType<{\n slug?: string;\n params?: string;\n}>;\n\n/** Page components keyed by navigation slug. */\nexport type CustomPages = Record<string, CustomPageComponent>;\n\n/** Per-member-type page overrides: memberTypeSlug -> (pageSlug -> component). */\nexport type MemberTypeCustomPages = Record<string, CustomPages>;\n\n/**\n * Resolve the page component to render for a given member-type slug and\n * page slug, preferring an exact page match then a base-slug (prefix)\n * match. Returns undefined when the member type has no override for the\n * route, so the caller falls back to global content.\n *\n * Known limitation: `baseSlug` is the caller's navigation-derived base (see\n * PageRouter), so a per-type override registered at a detail *prefix* that is\n * not itself a navigation slug (e.g. `share/product` when the nav only has\n * `share/products`) is only matched on an exact `currentSlug`. Data-driven,\n * prefix-aware matching against the registry's own keys is deferred to the\n * authoring phase; register the exact slug you route to for now.\n */\nexport function resolveMemberTypePage(\n registry: MemberTypeCustomPages | undefined,\n memberTypeSlug: string | null,\n currentSlug: string,\n baseSlug: string,\n): CustomPageComponent | undefined {\n if (!registry || !memberTypeSlug) return undefined;\n\n const pages = registry[memberTypeSlug];\n if (!pages) return undefined;\n\n return (\n pages[currentSlug] ??\n (baseSlug !== currentSlug ? pages[baseSlug] : undefined)\n );\n}\n","import { lazy, type ComponentType } from \"react\";\n\nconst ProfileScreen = lazy(() =>\n import(\"../screens/ProfileScreen\").then((m) => ({\n default: m.ProfileScreen,\n })),\n);\nconst OrdersScreen = lazy(() =>\n import(\"../screens/OrdersScreen\").then((m) => ({\n default: m.OrdersScreen,\n })),\n);\nconst SubscriptionsScreen = lazy(() =>\n import(\"../screens/SubscriptionsScreen\").then((m) => ({\n default: m.SubscriptionsScreen,\n })),\n);\nconst MessagingScreen = lazy(() =>\n import(\"../screens/MessagingScreen\").then((m) => ({\n default: m.MessagingScreen,\n })),\n);\nconst ContactsScreen = lazy(() =>\n import(\"../screens/ContactsScreen\").then((m) => ({\n default: m.ContactsScreen,\n })),\n);\nconst ShopScreen = lazy(() =>\n import(\"../screens/ShopScreen\").then((m) => ({ default: m.ShopScreen })),\n);\nconst CustomersScreen = lazy(() =>\n import(\"../screens/CustomersScreen\").then((m) => ({\n default: m.CustomersScreen,\n })),\n);\nconst ShareablesScreen = lazy(() =>\n import(\"../screens/ShareablesScreen\").then((m) => ({\n default: m.ShareablesScreen,\n })),\n);\nconst MySiteScreen = lazy(() =>\n import(\"../screens/MySiteScreen\").then((m) => ({\n default: m.MySiteScreen,\n })),\n);\nconst AppDownloadScreen = lazy(() =>\n import(\"../screens/AppDownloadScreen\").then((m) => ({\n default: m.AppDownloadScreen,\n })),\n);\n\nexport const SYSTEM_SLUG_SCREEN_MAP: Record<string, ComponentType> = {\n profile: ProfileScreen,\n // Legacy alias: portals that pre-date the /account → /profile rename may\n // still have \"account\" persisted in their nav config.\n account: ProfileScreen,\n orders: OrdersScreen,\n subscriptions: SubscriptionsScreen,\n messages: MessagingScreen,\n contacts: ContactsScreen,\n shop: ShopScreen,\n customers: CustomersScreen,\n \"my-site\": MySiteScreen,\n \"share/products\": ShareablesScreen,\n \"share/product\": ShareablesScreen,\n \"share/media\": ShareablesScreen,\n \"share/playlists\": ShareablesScreen,\n \"share/playlist\": ShareablesScreen,\n \"share/files\": ShareablesScreen,\n \"share/pages\": ShareablesScreen,\n \"share/page\": ShareablesScreen,\n \"app-download\": AppDownloadScreen,\n};\n\n/**\n * System screens that a portal must opt into by adding them to its active\n * navigation. Every other system screen is reachable by default; these two\n * only render when their nav slug is present.\n */\nexport const OPT_IN_SYSTEM_SCREENS: ReadonlySet<ComponentType> = new Set([\n MessagingScreen,\n ContactsScreen,\n]);\n\n/** Pre-sorted keys for greedy prefix matching (longest first). */\nconst SORTED_SCREEN_KEYS = Object.keys(SYSTEM_SLUG_SCREEN_MAP).sort(\n (a, b) => b.length - a.length,\n);\n\n/** Prefix-match a slug against screen map keys, longest match first. */\nexport function findScreenByPrefix(slug: string): ComponentType | undefined {\n for (const key of SORTED_SCREEN_KEYS) {\n if (slug === key || slug.startsWith(key + \"/\")) {\n return SYSTEM_SLUG_SCREEN_MAP[key];\n }\n }\n return undefined;\n}\n","import { Suspense, useMemo, type ComponentType } from \"react\";\nimport {\n isSystemNavigationItem,\n isRepOnlySlug,\n} from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { slugifyName } from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport type { NavigationItem, ScreenDefinition } from \"../types/navigation\";\nimport { CoreScreenPlaceholder } from \"../screens/CoreScreenPlaceholder\";\nimport { ScreenNotFound } from \"../screens/ScreenNotFound\";\nimport { AccessDeniedScreen } from \"../screens/AccessDeniedScreen\";\nimport { BuilderScreenView } from \"./BuilderScreenView\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { deniesRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\nimport {\n resolveMemberTypePage,\n type MemberTypeCustomPages,\n} from \"@fluid-app/portal-core/member-type-content\";\nimport {\n OPT_IN_SYSTEM_SCREENS,\n SYSTEM_SLUG_SCREEN_MAP,\n findScreenByPrefix,\n} from \"./system-screen-map\";\n\n/**\n * Returns true if any nav slug resolves to the given system screen component.\n * Lets detail routes (e.g. `share/product/:id`) render when their listing\n * slug (`share/products`) is in the nav, since both map to the same screen.\n */\nfunction isSystemScreenCoveredByNav(\n target: ComponentType,\n navSlugs: readonly string[],\n): boolean {\n for (const slug of navSlugs) {\n const navScreen = SYSTEM_SLUG_SCREEN_MAP[slug] ?? findScreenByPrefix(slug);\n if (navScreen === target) return true;\n }\n return false;\n}\n\nfunction isRouteCoveredByNav(\n currentSlug: string,\n baseSlug: string,\n navSlugs: readonly string[] | undefined,\n): boolean {\n if (navSlugs === undefined) return true;\n return navSlugs.some(\n (navSlug) =>\n navSlug === baseSlug ||\n currentSlug === navSlug ||\n currentSlug.startsWith(navSlug + \"/\"),\n );\n}\n\nexport interface PageRouterProps {\n currentSlug: string;\n currentNavItem?: NavigationItem | undefined;\n customPages?:\n | Record<string, ComponentType<{ slug?: string; params?: string }>>\n | undefined;\n /** Per-member-type page overrides, resolved by the current member's slug. */\n memberTypeCustomPages?: MemberTypeCustomPages | undefined;\n /** Builder screen definitions (from fluidos API) */\n screens?: ScreenDefinition[] | undefined;\n baseSlug: string;\n restParams: string;\n /**\n * Slugs from the active navigation. When provided, screens listed in\n * `OPT_IN_SYSTEM_SCREENS` only render if they're\n * reachable via the nav — matching the implicit gating builder screens\n * get from being sourced via `appData.screens`. All other system screens\n * bypass this check. Omit to render system screens unconditionally.\n */\n navSlugs?: readonly string[] | undefined;\n}\n\nexport function PageRouter({\n currentSlug,\n currentNavItem,\n customPages,\n memberTypeCustomPages,\n screens,\n baseSlug,\n restParams,\n navSlugs,\n}: PageRouterProps): React.JSX.Element {\n const { data: account } = useAccount();\n // Rep-only routes are gated on the member type's `rep_eligible` permission\n // so custom non-rep types are blocked the same as customers.\n const canRep = !deniesRepSurfaces(account);\n\n // Build a screen lookup map by ID for O(1) access\n const screenById = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n return new Map(screens.map((s) => [s.id, s]));\n }, [screens]);\n\n // Build a screen lookup map by slug for fallback resolution. Keys include\n // both the screen's own `slug` (e.g. `screen-{uuid}` for direct URL access)\n // and `slugifyName(screen.name)` to match nav-item slugs authored in the\n // admin builder via `generateSlugFromName(screen.name)` — without this,\n // the slug fallback never matches because the two formats diverge.\n const screenBySlug = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n const map = new Map<string, ScreenDefinition>();\n for (const s of screens) {\n if (s.slug) map.set(s.slug, s);\n }\n for (const s of screens) {\n if (!s.name) continue;\n const nameSlug = slugifyName(s.name);\n if (nameSlug && !map.has(nameSlug)) map.set(nameSlug, s);\n }\n return map;\n }, [screens]);\n\n // Resolve builder screen: screen_id first, then slug fallback\n const builderScreen = useMemo(() => {\n if (currentNavItem?.screen_id && screenById) {\n const byId = screenById.get(currentNavItem.screen_id);\n if (byId) return byId;\n }\n if (screenBySlug) {\n return (\n screenBySlug.get(currentSlug) ?? screenBySlug.get(baseSlug) ?? undefined\n );\n }\n return undefined;\n }, [\n currentNavItem?.screen_id,\n screenById,\n screenBySlug,\n currentSlug,\n baseSlug,\n ]);\n\n const routeCoveredByNav =\n currentNavItem !== undefined ||\n isRouteCoveredByNav(currentSlug, baseSlug, navSlugs);\n const isRepOnlyRoute = isRepOnlySlug(currentSlug);\n\n // 0. Block customer access to rep-only slugs only when the route is\n // configured in navigation. Hidden rep-only routes should look like normal\n // missing pages instead of revealing that a rep-only screen exists.\n if (!canRep && isRepOnlyRoute && routeCoveredByNav) {\n return <AccessDeniedScreen />;\n }\n\n // 0b. Per-member-type page override — an affiliate/customer/etc. can be\n // served a different screen at the same slug. Wins over the global\n // customPages and system screens below.\n //\n // Keyed on the REAL member type slug (member_type_detail.slug), never\n // the binary fallback: content must not be selected for a member whose\n // true type is still unknown (backfill gap / loading), or a custom type\n // would be served the generic rep/customer bucket's page. Resolves to\n // null in that case and falls through to the global content below.\n //\n // A rep-only route the member cannot access is never served a per-type\n // page — it must fall through to the AccessDenied/404 handling so a\n // per-type override cannot bypass the rep-only gate.\n const memberTypeContentSlug = account?.member_type_detail?.slug ?? null;\n const MemberTypePage =\n canRep || !isRepOnlyRoute\n ? resolveMemberTypePage(\n memberTypeCustomPages,\n memberTypeContentSlug,\n currentSlug,\n baseSlug,\n )\n : undefined;\n if (MemberTypePage) {\n return <MemberTypePage slug={currentSlug} params={restParams} />;\n }\n\n // 1. Custom page match (takes priority over system defaults)\n const ExactCustomPage = customPages?.[currentSlug];\n if (ExactCustomPage) {\n return <ExactCustomPage slug={currentSlug} params={restParams} />;\n }\n\n // 2. Prefix custom page match (base slug with rest params)\n if (baseSlug !== currentSlug) {\n const PrefixCustomPage = customPages?.[baseSlug];\n if (PrefixCustomPage) {\n return <PrefixCustomPage slug={currentSlug} params={restParams} />;\n }\n }\n\n // 3. System navigation item with a dedicated screen component\n // First try exact match on baseSlug, then prefix-match currentSlug\n // against screen map keys (handles detail routes like \"share/product/123\"\n // matching \"share/product\" when the slug isn't a registered nav item).\n const SystemScreen =\n SYSTEM_SLUG_SCREEN_MAP[baseSlug] ?? findScreenByPrefix(currentSlug);\n const systemScreenRequiresNav =\n SystemScreen !== undefined &&\n (OPT_IN_SYSTEM_SCREENS.has(SystemScreen) || isRepOnlyRoute);\n const systemScreenInNav =\n navSlugs === undefined ||\n (SystemScreen !== undefined &&\n (!systemScreenRequiresNav ||\n routeCoveredByNav ||\n isSystemScreenCoveredByNav(SystemScreen, navSlugs)));\n if (SystemScreen && systemScreenInNav) {\n return (\n <Suspense\n fallback={\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"border-primary size-8 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n }\n >\n <SystemScreen />\n </Suspense>\n );\n }\n\n // 4. Recognized system screen gated out by step 3. Treat opt-in and\n // rep-only system screens absent from the active nav as missing pages.\n if (SystemScreen && !systemScreenInNav) {\n return <ScreenNotFound name={currentNavItem?.label ?? currentSlug} />;\n }\n\n // 5. Recognized system slug without an SDK screen mapping yet.\n if (isSystemNavigationItem(baseSlug)) {\n return <CoreScreenPlaceholder name={currentNavItem?.label ?? baseSlug} />;\n }\n\n // 6. Builder screen (by screen_id or slug match)\n if (builderScreen) {\n return <BuilderScreenView screen={builderScreen} />;\n }\n\n // 7. True \"not found\" — slug didn't match any custom/system/builder screen\n return <ScreenNotFound name={currentNavItem?.label ?? currentSlug} />;\n}\n","import { Component, type ErrorInfo, type ReactNode, use } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { TriangleAlert } from \"lucide-react\";\nimport { ShellTranslationContext } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { shellEn } from \"@fluid-app/portal-core/shell-translation-dictionary\";\nimport { FallbackCard } from \"../screens/FallbackCard\";\nimport { goBackOrHome } from \"./go-back-or-home\";\n\nfunction DefaultErrorFallback({\n onReset,\n}: {\n onReset: () => void;\n}): React.JSX.Element {\n const api = use(ShellTranslationContext);\n const t = api ? api.t : (key: keyof typeof shellEn) => shellEn[key];\n return (\n <FallbackCard\n icon={<TriangleAlert />}\n title={t(\"error_title\")}\n description={t(\"error_description\")}\n actions={\n <>\n <Button variant=\"outline\" onClick={onReset}>\n {t(\"error_try_again\")}\n </Button>\n <Button variant=\"default\" onClick={goBackOrHome}>\n {t(\"error_go_back\")}\n </Button>\n </>\n }\n />\n );\n}\n\nexport interface AppShellErrorBoundaryProps {\n children: ReactNode;\n /**\n * Invoked every time the boundary catches an error. Fires again on each\n * subsequent crash if `reset()` is called and the underlying issue persists.\n * When provided, the boundary will not log to `console.error` itself —\n * forward to your reporter (e.g. Sentry) inside this callback.\n *\n * @example\n * ```tsx\n * <AppShellErrorBoundary onError={(err, info) => Sentry.captureException(err, { extra: info })}>\n * ```\n */\n onError?: (error: Error, info: ErrorInfo) => void;\n /** Override the default fallback UI. Receives a reset callback. */\n fallback?: (props: { error: Error; reset: () => void }) => ReactNode;\n}\n\ninterface State {\n error: Error | null;\n}\n\nexport class AppShellErrorBoundary extends Component<\n AppShellErrorBoundaryProps,\n State\n> {\n override state: State = { error: null };\n\n static getDerivedStateFromError(error: Error): State {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: ErrorInfo): void {\n if (this.props.onError) {\n try {\n this.props.onError(error, info);\n } catch (callbackError) {\n // A throwing onError (e.g. misconfigured Sentry transport) must not\n // mask the original crash. Surface both to console as a fallback.\n console.error(\"[portal] onError handler threw:\", callbackError);\n console.error(\"[portal] Original screen crash:\", error, info);\n }\n return;\n }\n console.error(\"[portal] Screen crashed:\", error, info);\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n override render(): ReactNode {\n const { error } = this.state;\n if (!error) return this.props.children;\n\n if (this.props.fallback) {\n return this.props.fallback({ error, reset: this.reset });\n }\n\n return <DefaultErrorFallback onReset={this.reset} />;\n }\n}\n","import type Color from \"colorjs.io\";\nimport type { ResolvedTheme } from \"@fluid-app/portal-core/theme\";\n\nexport const PORTAL_NATIVE_THEME_MESSAGE_TYPE = \"fluid.portal.theme\";\nexport const PORTAL_NATIVE_THEME_MESSAGE_VERSION = 1;\n\nexport type PortalNativeThemeMode = \"light\" | \"dark\";\n\nexport interface PortalNativeThemeMessage {\n readonly type: typeof PORTAL_NATIVE_THEME_MESSAGE_TYPE;\n readonly version: typeof PORTAL_NATIVE_THEME_MESSAGE_VERSION;\n readonly payload: {\n readonly themeId: string;\n readonly mode: PortalNativeThemeMode;\n readonly colors: {\n readonly muted: string;\n };\n };\n}\n\ninterface ReactNativeWebViewBridge {\n postMessage: (message: string) => void;\n}\n\ndeclare global {\n interface Window {\n ReactNativeWebView?: ReactNativeWebViewBridge;\n }\n}\n\nexport function colorToSrgbHex(color: Color): string {\n const srgb = color.to(\"srgb\");\n srgb.alpha = 1;\n return srgb.toString({ format: \"hex\" });\n}\n\nexport function createPortalNativeThemeMessage({\n resolvedTheme,\n mode,\n}: {\n readonly resolvedTheme: ResolvedTheme;\n readonly mode: PortalNativeThemeMode;\n}): PortalNativeThemeMessage {\n return {\n type: PORTAL_NATIVE_THEME_MESSAGE_TYPE,\n version: PORTAL_NATIVE_THEME_MESSAGE_VERSION,\n payload: {\n themeId: resolvedTheme.id,\n mode,\n colors: {\n muted: colorToSrgbHex(resolvedTheme[mode].muted.base),\n },\n },\n };\n}\n\nexport function postPortalNativeThemeMessage(\n message: PortalNativeThemeMessage,\n): void {\n if (typeof window === \"undefined\") return;\n\n window.ReactNativeWebView?.postMessage(JSON.stringify(message));\n}\n","/**\n * The FairShare SDK identity cookies that must be dropped when the portal\n * member changes. `fluid_session` maps to the server-side `session_uuid` (the\n * backend inherits a new cart's customer from the most recent cart in the same\n * session — so a stale session re-binds carts to the previous member), and\n * `fluid_authenticated_jwt` (+ its timestamp) is Bearer-synced onto carts. We\n * deliberately do NOT touch `fluid_visitor`, locale, or `fluid_affiliate`\n * (attribution is meant to persist).\n */\nconst SDK_IDENTITY_COOKIES = [\n \"fluid_session\",\n \"fluid_authenticated_jwt\",\n \"fluid_authenticated_at\",\n];\n\n/** SDK localStorage keys tied to the member's cart/identity. */\nconst SDK_IDENTITY_STORAGE = [\"fs_cart\", \"fs_authenticated_user\"];\n\n/**\n * Resets the FairShare SDK's persisted shopping identity so the next portal\n * member starts a clean session (CURRENT-2180).\n *\n * The SDK owns this state (localStorage + cookies), and its own `reset()` is\n * insufficient: it sets identity cookies WITH a `domain=` attribute (via\n * `setCookieOnApexAndSubdomains`, using the tenant host as the cookie domain)\n * but deletes them WITHOUT one, so the domain-scoped cookies survive — which is\n * exactly `fluid_session` / `fluid_authenticated_jwt`. So we call `reset()` for\n * its localStorage + in-memory teardown, then expire the identity cookies here\n * with the matching domain.\n *\n * Called on portal logout and company switch. No-op-safe when the SDK global\n * isn't present (e.g. a page that never mounted the cart script, or SSR).\n */\nexport function resetFairShareSdk(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n const sdk = (window as unknown as { FairShareSDK?: { reset?: () => void } })\n .FairShareSDK;\n\n if (typeof sdk?.reset === \"function\") {\n try {\n sdk.reset();\n } catch (error) {\n // Best-effort: never let SDK teardown block the logout/switch flow.\n console.error(\"[resetFairShareSdk] SDK reset failed:\", error);\n }\n }\n\n // Expire the identity cookies across every domain they could have been set\n // under. A browser only deletes a cookie when the delete's domain matches how\n // it was written, and these cookies are written under different scopes:\n // `fluid_session` under the tenant host (BFF cookieDomain), but\n // `fluid_authenticated_jwt` can come from a non-BFF login on a broader apex\n // (e.g. `.fluid.app` / `.portal.fluid.app`). So walk up every parent domain\n // of the current host (with and without a leading dot) plus host-only.\n const host = window.location.hostname;\n const labels = host.split(\".\");\n const domainVariants = [\"\"]; // host-only (no domain attribute)\n for (let i = 0; i < labels.length - 1; i++) {\n const domain = labels.slice(i).join(\".\");\n domainVariants.push(`; domain=${domain}`, `; domain=.${domain}`);\n }\n\n for (const name of SDK_IDENTITY_COOKIES) {\n for (const domain of domainVariants) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT${domain}`;\n }\n }\n\n for (const key of SDK_IDENTITY_STORAGE) {\n try {\n window.localStorage.removeItem(key);\n } catch {\n // localStorage may be unavailable in some contexts\n }\n }\n}\n","/**\n * useLogout Hook\n *\n * Provides a complete logout function that:\n * 1. Clears all client-side state (query cache, IndexedDB, sessionStorage)\n * 2. Navigates to GET /logout — Rails clears the session and redirects to the Hub\n */\n\nimport { useCallback } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseLogoutOptions {\n /** URL to redirect to after logout. Triggers a full page navigation. */\n redirectUrl?: string;\n /** Callback invoked after all state is cleared. Ignored when `redirectUrl` is set. */\n onLogout?: () => void;\n}\n\n/**\n * Hook that returns a `logout` function which clears the server session\n * via `DELETE /logout` and all cached/persisted client state.\n *\n * @example\n * ```tsx\n * const logout = useLogout({ redirectUrl: \"/login\" });\n * <button onClick={logout}>Log out</button>\n * ```\n *\n * @example\n * ```tsx\n * const logout = useLogout({\n * onLogout: () => navigate(\"/signed-out\"),\n * });\n * ```\n */\nexport function useLogout({\n redirectUrl,\n onLogout,\n}: UseLogoutOptions = {}): () => Promise<void> {\n const queryClient = useQueryClient();\n\n const logout = useCallback(async () => {\n // 1. Cancel in-flight requests to prevent cache repopulation\n await queryClient.cancelQueries();\n\n // 2. Clear in-memory TanStack Query cache\n queryClient.clear();\n\n // 3. Clear IndexedDB persisted query cache\n try {\n await deleteDatabase();\n } catch (error) {\n // Non-fatal — continue with logout even if IndexedDB cleanup fails\n console.error(\"[useLogout] Failed to clear IndexedDB cache:\", error);\n }\n\n // 4. Clear sessionStorage — intentionally clears all keys because the\n // portal app owns the full page (not embedded as a micro-frontend).\n try {\n sessionStorage.clear();\n } catch {\n // sessionStorage may be unavailable in some contexts\n }\n\n // 5. Reset the FairShare SDK's shopping identity (cart, session, auth\n // cookies). These live in the SDK's own localStorage/cookies, which the\n // steps above don't touch — leaving them lets the next member inherit\n // the previous member's cart/customer (CURRENT-2180).\n resetFairShareSdk();\n\n // 6. Navigate to the server logout endpoint. Rails clears the session\n // cookie and redirects to the Portal Hub company list. A full page\n // navigation lets the browser follow the redirect naturally — no\n // fetch or JSON parsing needed.\n if (redirectUrl) {\n window.location.href = redirectUrl;\n } else if (onLogout) {\n onLogout();\n } else {\n window.location.href = \"/logout\";\n }\n }, [queryClient, redirectUrl, onLogout]);\n\n return logout;\n}\n","import { useCallback, useState } from \"react\";\nimport { LogOut, Moon, Sun } from \"lucide-react\";\nimport {\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\nexport interface SdkLogoutButtonProps {\n onLogout: () => Promise<void>;\n}\n\n/**\n * Sidebar footer slot. Renders Theme toggle + Log out.\n * Filename is historical — kept to avoid touching the AppShell wiring.\n */\nexport function SdkLogoutButton({\n onLogout,\n}: SdkLogoutButtonProps): React.JSX.Element {\n const [isPending, setIsPending] = useState(false);\n const themeMode = useThemeMode();\n const { t } = useShellTranslation();\n\n const handleClick = useCallback(async () => {\n setIsPending(true);\n try {\n await onLogout();\n } catch (error) {\n console.error(\"[SdkLogoutButton] Logout failed:\", error);\n } finally {\n setIsPending(false);\n }\n }, [onLogout]);\n\n const ThemeIcon = themeMode.displayMode === \"dark\" ? Moon : Sun;\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton\n onClick={themeMode.cycleMode}\n aria-label={t(\"theme_switch\", { mode: themeMode.mode })}\n >\n <ThemeIcon className=\"size-4\" />\n <span>{t(\"theme_label\")}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n <SidebarMenuItem>\n <SidebarMenuButton onClick={handleClick} disabled={isPending}>\n <LogOut className=\"size-4\" />\n <span>{isPending ? t(\"logout_pending\") : t(\"logout\")}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n );\n}\n","import type { NavigationItem } from \"@fluid-app/portal-core/types\";\nimport {\n isRepOnlySlug,\n type SystemNavigationItem,\n} from \"@fluid-app/portal-core/navigation/system-navigation-items\";\n\n/**\n * Filter navigation items by removing rep-only slugs.\n * Handles nested children: rep-only children are removed, and parents\n * with no remaining children are also removed.\n */\nexport function filterRepOnlyNavItems(\n items: NavigationItem[],\n): NavigationItem[] {\n const result: NavigationItem[] = [];\n\n for (const item of items) {\n // Leaf item with a rep-only slug — skip\n if (!item.children?.length) {\n if (isRepOnlySlug(item.slug)) continue;\n result.push(item);\n continue;\n }\n\n // Parent with a rep-only slug — skip entire group\n if (isRepOnlySlug(item.slug)) continue;\n\n // Recursively filter children\n const filteredChildren = filterRepOnlyNavItems(item.children);\n\n // Drop parent if all children were removed\n if (filteredChildren.length === 0) continue;\n\n result.push({ ...item, children: filteredChildren });\n }\n\n return result;\n}\n\n/**\n * Filter system navigation sections by removing rep-only items.\n * Sections with no remaining items are dropped entirely.\n */\nexport function filterRepOnlySections(\n sections: Record<string, SystemNavigationItem[]>,\n): Record<string, SystemNavigationItem[]> {\n const filtered: Record<string, SystemNavigationItem[]> = {};\n for (const [section, items] of Object.entries(sections)) {\n const allowed = items.filter((item) => !isRepOnlySlug(item.slug));\n if (allowed.length > 0) filtered[section] = allowed;\n }\n return filtered;\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { PanelLeft } from \"lucide-react\";\nimport { useSidebar } from \"./sidebar\";\nimport { useScreenHeaderSlotRefs } from \"./ScreenHeaderContext\";\n\nexport interface ScreenHeaderProps {\n title?: string;\n trailingContent?: ReactNode;\n override?: ScreenHeaderOverride;\n}\n\nexport interface ScreenHeaderOverride {\n title: string;\n action: ReactNode;\n}\n\nexport function ScreenHeader({\n title,\n trailingContent,\n override,\n}: ScreenHeaderProps): React.JSX.Element | null {\n const { toggleSidebar, isMobile } = useSidebar();\n const { breadcrumbsRef, actionsRef, hasBreadcrumbs } =\n useScreenHeaderSlotRefs();\n\n if (!override && !title && !hasBreadcrumbs) return null;\n\n const showTrailing = !isMobile && trailingContent;\n\n return (\n <div\n data-fluid-screen-header=\"\"\n tabIndex={-1}\n className=\"border-foreground/[0.06] bg-background sticky top-0 z-30 flex flex-row items-center border-b px-4 py-3 max-md:hidden md:px-6\"\n >\n {!isMobile && (\n <>\n <button\n type=\"button\"\n onClick={toggleSidebar}\n className=\"text-muted-foreground hover:text-foreground -ml-1 inline-flex items-center justify-center rounded-md p-1\"\n aria-label=\"Toggle Sidebar\"\n >\n <PanelLeft className=\"size-4\" />\n </button>\n <div className=\"bg-border mx-2 h-4 w-px\" />\n </>\n )}\n <div\n data-fluid-screen-header-normal-content\n hidden={override !== undefined}\n className=\"flex min-w-0 flex-1 items-center\"\n >\n <div\n ref={breadcrumbsRef}\n className=\"min-w-0 flex-1 empty:hidden [&_[data-slot=breadcrumb-list]]:text-base!\"\n />\n {!hasBreadcrumbs && title && (\n <h1 className=\"text-foreground text-base font-semibold\">{title}</h1>\n )}\n <div className=\"ml-auto flex items-center gap-2\">\n <div\n ref={actionsRef}\n className=\"flex items-center gap-2 empty:hidden\"\n />\n {showTrailing}\n </div>\n </div>\n {override && (\n <div\n data-fluid-screen-header-override-content\n className=\"flex min-w-0 flex-1 items-center gap-2\"\n >\n <h1 className=\"text-foreground min-w-0 truncate text-base font-semibold\">\n {override.title}\n </h1>\n <div className=\"ml-auto flex shrink-0 items-center\">\n {override.action}\n </div>\n </div>\n )}\n </div>\n );\n}\n","import { useLayoutEffect, useRef, type ReactNode } from \"react\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport {\n ScreenHeader,\n type ScreenHeaderOverride,\n} from \"@fluid-app/portal-react/shell/ScreenHeader\";\nimport {\n useOptionalScreenAreaFullscreenCoordinator,\n useScreenAreaFullscreenSnapshot,\n} from \"./ScreenAreaFullscreenContext\";\n\nfunction FullscreenExitAction({\n label,\n onExit,\n owner,\n}: {\n readonly label: string;\n readonly onExit: () => void;\n readonly owner: symbol;\n}): React.JSX.Element {\n const buttonRef = useRef<HTMLButtonElement>(null);\n useLayoutEffect(() => buttonRef.current?.focus(), [owner]);\n\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n className=\"fluid-portal-fullscreen-exit\"\n onClick={onExit}\n >\n {label}\n </button>\n );\n}\n\n/** Translates the screen title and swaps in trusted fullscreen header chrome. */\nexport function TranslatedScreenHeader({\n slug,\n title,\n trailingContent,\n}: {\n slug: string | undefined;\n title: string | undefined;\n trailingContent: ReactNode;\n}): React.JSX.Element | null {\n const shellApi = useShellTranslation();\n const fullscreenCoordinator = useOptionalScreenAreaFullscreenCoordinator();\n const fullscreen = useScreenAreaFullscreenSnapshot();\n const translatedTitle = title\n ? translateNavLabel(shellApi, slug, title)\n : undefined;\n const fullscreenOverride: ScreenHeaderOverride | undefined =\n fullscreen && fullscreenCoordinator\n ? {\n title: fullscreen.displayName,\n action: (\n <FullscreenExitAction\n label={shellApi.t(\"fullscreen_exit\")}\n onExit={() => fullscreenCoordinator.exit()}\n owner={fullscreen.owner}\n />\n ),\n }\n : undefined;\n\n return (\n <ScreenHeader\n title={translatedTitle}\n trailingContent={trailingContent}\n override={fullscreenOverride}\n />\n );\n}\n","import {\n useState,\n useEffect,\n useCallback,\n useMemo,\n useRef,\n type ReactNode,\n type ComponentType,\n type ErrorInfo,\n} from \"react\";\nimport { AppShellLayout } from \"@fluid-app/portal-react/shell/AppShellLayout\";\nimport { BottomNavVisibilityProvider } from \"@fluid-app/portal-react/shell/BottomNavVisibilityContext\";\nimport {\n ThemeModeProvider,\n type ThemeMode,\n} from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { getDefaultNavigation } from \"@fluid-app/portal-core/navigation/default-navigation\";\nimport { ScreenHeaderProvider } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { SdkBottomNav } from \"./SdkBottomNav\";\nimport type {\n RepAppData,\n ScreenDefinition,\n} from \"@fluid-app/portal-core/types\";\nimport {\n applyTheme,\n getDefaultThemeDefinition,\n removeAllThemes,\n resolveTheme,\n} from \"@fluid-app/portal-core/theme\";\nimport { useFluidApp } from \"../hooks/use-fluid-app\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { AppShellLoading } from \"./AppShellLoading\";\nimport { SdkNavigation } from \"./SdkNavigation\";\nimport { SdkHeader } from \"./SdkHeader\";\nimport { LanguageSelector } from \"./LanguageSelector\";\nimport { SdkCompanySwitcher } from \"./SdkCompanySwitcher\";\nimport { PageRouter } from \"./PageRouter\";\nimport {\n collectNavSlugs,\n matchSlugPrefix,\n extractSlugFromPathname,\n} from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { AppNavigationProvider } from \"./AppNavigationContext\";\nimport { AppShellErrorBoundary } from \"./AppShellErrorBoundary\";\nimport type { PortalErrorReporter } from \"../errors/portal-error-reporter\";\nimport {\n createPortalNativeThemeMessage,\n postPortalNativeThemeMessage,\n type PortalNativeThemeMode,\n} from \"./native-theme-bridge\";\n\nimport { useLogout } from \"../hooks/use-logout\";\nimport { SdkLogoutButton } from \"./SdkLogoutButton\";\nimport {\n RepUserProvider,\n WidgetInteractionProvider,\n type WidgetInteractionItem,\n} from \"@fluid-app/portal-widgets/contexts\";\nimport { Toaster } from \"@fluid-app/ui-primitives\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { deniesRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\nimport type { MemberTypeCustomPages } from \"@fluid-app/portal-core/member-type-content\";\nimport { filterRepOnlyNavItems } from \"../navigation/filter-nav-items\";\nimport { ScreenAreaFullscreenProvider } from \"./ScreenAreaFullscreenContext\";\nimport { TranslatedScreenHeader } from \"./TranslatedScreenHeader\";\n\nconst THEME_STORAGE_KEY = \"portal-theme-mode\";\n\n// Maps a ShareableResource.shareable_type discriminator to its detail-screen\n// slug segment. The detail route is `share/<slug>/<id>` (see ShareablesScreen).\n// `Playlist` and `Library` both surface under the playlists slug — fetchResource\n// stamps \"Library\" while the metrics adapters stamp \"Playlist\". `EnrollmentPack`\n// is intentionally absent: there's no in-portal detail screen, so clicks open\n// the pack's public canonical_url in a new tab instead — see handleWidgetItemClick.\nconst SHAREABLE_TYPE_TO_SLUG: Record<string, string> = {\n Product: \"products\",\n Medium: \"media\",\n Page: \"pages\",\n Playlist: \"playlists\",\n Library: \"playlists\",\n};\n\nfunction getInitialThemeMode(): ThemeMode {\n if (typeof window === \"undefined\") return \"light\";\n const stored = localStorage.getItem(THEME_STORAGE_KEY);\n if (stored === \"light\" || stored === \"dark\") return stored;\n return \"light\";\n}\n\nfunction findFirstNavigableSlug(items: NavigationItem[]): string {\n for (const item of items) {\n if (item.slug) return normalizeSlug(item.slug);\n for (const child of item.children ?? []) {\n if (child.slug) return normalizeSlug(child.slug);\n }\n }\n return \"\";\n}\n\nfunction findCurrentSection(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n if (\n item.children?.some((child) => normalizeSlug(child.slug) === normalized)\n ) {\n return item;\n }\n }\n return undefined;\n}\n\nfunction findNavItem(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === normalized) return child;\n }\n }\n return undefined;\n}\n\nexport interface AppShellProps {\n /** Pre-fetched app data (skips internal useFluidApp call if provided) */\n appData?: RepAppData;\n /** Override navigation items (otherwise derived from appData/API) */\n navigation?: NavigationItem[];\n /** Custom page components keyed by slug */\n customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /** Per-member-type page overrides, resolved by the current member's slug. */\n memberTypeCustomPages?: MemberTypeCustomPages;\n /** Base path for subpath deployments (e.g. \"/portal\"). Default: \"/\" */\n basePath?: string;\n /** Controlled current slug */\n currentSlug?: string;\n /** Navigation callback */\n onNavigate?: (slug: string) => void;\n /** Custom sidebar header slot */\n sidebarHeader?: ReactNode;\n /** Custom sidebar footer slot. When provided, replaces the default logout button. */\n sidebarFooter?: ReactNode;\n /**\n * Callback invoked after the user logs out and all client state has been cleared.\n * If not provided, a default logout button is still rendered, but no post-logout\n * action is taken (consumers should pair this with `logoutRedirectUrl`\n * or provide their own `sidebarFooter`).\n */\n onLogout?: () => void;\n /** URL to redirect to after logout (e.g. \"/login\"). Takes precedence over `onLogout`. */\n logoutRedirectUrl?: string;\n /** Optional reporter for screen-level crashes. Falls back to console logging when omitted. */\n errorReporter?: PortalErrorReporter;\n /** Render prop or static children for the content area */\n children?:\n | ReactNode\n | ((props: {\n currentSlug: string;\n currentNavItem: NavigationItem | undefined;\n }) => ReactNode);\n}\n\nexport function AppShell({\n appData: appDataProp,\n navigation: navigationProp,\n customPages,\n memberTypeCustomPages,\n basePath = \"/\",\n currentSlug: controlledSlug,\n onNavigate: onNavigateProp,\n sidebarHeader,\n sidebarFooter,\n onLogout,\n logoutRedirectUrl,\n errorReporter,\n children,\n}: AppShellProps): React.JSX.Element {\n // Normalize basePath: ensure leading slash, no trailing slash\n const normalizedBasePath = useMemo(() => {\n const stripped = basePath.replace(/^\\/|\\/$/g, \"\");\n return stripped ? `/${stripped}` : \"/\";\n }, [basePath]);\n\n // Fetch full app data from the rendered-manifest endpoint\n // (disabled when appData is provided). The response carries\n // `definition_id` directly, so no separate definition fetch is needed.\n const { data: fetchedAppData, isLoading } = useFluidApp({\n enabled: !appDataProp,\n });\n const appData = appDataProp ?? fetchedAppData;\n\n const { data: account } = useAccount();\n const repUser = useMemo(\n () => ({\n name:\n account?.first_name && account?.last_name\n ? `${account.first_name} ${account.last_name}`\n : null,\n email: account?.email ?? null,\n imageUrl: account?.avatar_url ?? null,\n publicId: account?.public_id,\n }),\n [\n account?.first_name,\n account?.last_name,\n account?.email,\n account?.avatar_url,\n account?.public_id,\n ],\n );\n\n // Resolve the active theme from app data, falling back to the theme-engine\n // default when the manifest has no themes. This guarantees CSS variables\n // are always emitted so the shell renders with proper styling.\n const activeTheme = useMemo(() => {\n const themes = appData?.profile?.themes;\n const activeId = appData?.profile?.activeThemeId;\n if (themes?.length) {\n const matched = activeId\n ? themes.find((t) => t.id === activeId)\n : undefined;\n const first = themes[0];\n if (matched) return matched;\n if (first) return first;\n }\n return getDefaultThemeDefinition();\n }, [appData?.profile?.themes, appData?.profile?.activeThemeId]);\n\n // Apply theme CSS via the shared theme pipeline\n const resolvedTheme = useMemo(() => resolveTheme(activeTheme), [activeTheme]);\n\n useEffect(() => {\n applyTheme(resolvedTheme);\n return () => {\n removeAllThemes();\n };\n }, [resolvedTheme]);\n\n // Mirror data-theme and data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n root.setAttribute(\"data-theme\", activeTheme.id);\n return () => {\n root.removeAttribute(\"data-theme\");\n };\n }, [activeTheme.id]);\n\n // Resolve navigation items\n const navItems = useMemo(() => {\n if (navigationProp) return navigationProp;\n const profileNav = appData?.profile?.navigation?.navigation_items;\n if (profileNav && profileNav.length > 0) return profileNav;\n return getDefaultNavigation();\n }, [navigationProp, appData?.profile?.navigation?.navigation_items]);\n\n // Resolve mobile navigation items (fall back to desktop nav)\n const mobileNavItems = useMemo(() => {\n const mobileNav = appData?.profile?.mobile_navigation?.navigation_items;\n if (mobileNav && mobileNav.length > 0) return mobileNav;\n return navItems;\n }, [appData?.profile?.mobile_navigation?.navigation_items, navItems]);\n\n // Gate rep-only nav on the member type's `rep_eligible` permission rather\n // than the binary customer/rep split, so company-custom member types are\n // filtered by what they're actually allowed to see.\n const canRep = !deniesRepSurfaces(account);\n\n const filteredNavItems = useMemo(\n () => (canRep ? navItems : filterRepOnlyNavItems(navItems)),\n [navItems, canRep],\n );\n\n const filteredMobileNavItems = useMemo(\n () => (canRep ? mobileNavItems : filterRepOnlyNavItems(mobileNavItems)),\n [mobileNavItems, canRep],\n );\n\n // Resolve builder screen definitions\n const screens: ScreenDefinition[] | undefined = appData?.screens;\n\n // Collect all nav slugs for prefix matching (sorted longest-first)\n const navSlugs = useMemo(\n () => collectNavSlugs(filteredNavItems),\n [filteredNavItems],\n );\n\n // Combined desktop + mobile nav slugs for gating system-screen rendering\n // and prefix-matching; a slug present in either nav counts as \"in nav\".\n // Re-sort longest-first after merging — `collectNavSlugs` orders each list\n // individually, but Set insertion order would otherwise let a shorter\n // desktop prefix (e.g. \"home\") win against a more-specific mobile slug\n // (e.g. \"home/news\") in `matchSlugPrefix`.\n const allNavSlugs = useMemo(() => {\n const mobileSlugs = collectNavSlugs(filteredMobileNavItems);\n return Array.from(new Set([...navSlugs, ...mobileSlugs])).sort(\n (a, b) => b.split(\"/\").length - a.split(\"/\").length,\n );\n }, [navSlugs, filteredMobileNavItems]);\n\n // Theme mode state with localStorage persistence\n const [themeMode, setThemeMode] = useState<ThemeMode>(getInitialThemeMode);\n\n const handleThemeModeChange = useCallback((mode: ThemeMode) => {\n setThemeMode(mode);\n if (typeof window !== \"undefined\") {\n localStorage.setItem(THEME_STORAGE_KEY, mode);\n }\n }, []);\n\n // Mirror data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n const mode = themeMode === \"auto\" ? undefined : themeMode;\n if (mode) {\n root.setAttribute(\"data-theme-mode\", mode);\n } else {\n root.removeAttribute(\"data-theme-mode\");\n }\n return () => {\n root.removeAttribute(\"data-theme-mode\");\n };\n }, [themeMode]);\n\n // Set <meta name=\"theme-color\"> so mobile browser chrome matches the theme\n useEffect(() => {\n let meta = document.querySelector<HTMLMetaElement>(\n 'meta[name=\"theme-color\"]',\n );\n if (!meta) {\n meta = document.createElement(\"meta\");\n meta.name = \"theme-color\";\n document.head.appendChild(meta);\n }\n const metaEl = meta;\n\n const theme = resolvedTheme;\n function update(prefersDark: boolean) {\n const mode: PortalNativeThemeMode =\n themeMode === \"auto\" ? (prefersDark ? \"dark\" : \"light\") : themeMode;\n const nativeThemeMessage = createPortalNativeThemeMessage({\n resolvedTheme: theme,\n mode,\n });\n const mutedColor = nativeThemeMessage.payload.colors.muted;\n metaEl.content = mutedColor;\n document.body.style.backgroundColor = mutedColor;\n postPortalNativeThemeMessage(nativeThemeMessage);\n }\n\n const mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n update(mql.matches);\n\n if (themeMode === \"auto\") {\n const handler = (e: MediaQueryListEvent) => update(e.matches);\n mql.addEventListener(\"change\", handler);\n return () => {\n mql.removeEventListener(\"change\", handler);\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }\n\n return () => {\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }, [resolvedTheme, themeMode]);\n\n // Path-based routing for uncontrolled mode\n const [pathSlug, setPathSlug] = useState<string>(() => {\n if (typeof window === \"undefined\") return \"\";\n return extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n });\n\n const [previousSlug, setPreviousSlug] = useState<string | null>(null);\n\n // Sync initial path when navItems load — redirect to first nav slug if at root.\n // Wait for app data to load before redirecting; otherwise the fallback\n // default navigation can leak its first slug (e.g. \"shop\") into the URL\n // before the tenant's real navigation arrives.\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n if (typeof window === \"undefined\") return;\n if (!navigationProp && isLoading && !appData) return;\n\n const currentPath = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n\n if (!currentPath) {\n const firstSlug = findFirstNavigableSlug(filteredNavItems);\n if (firstSlug) {\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${firstSlug}`\n : `${normalizedBasePath}/${firstSlug}`;\n // replaceState to avoid polluting back-button history\n history.replaceState(null, \"\", targetPath);\n setPathSlug(firstSlug);\n }\n }\n }, [\n filteredNavItems,\n controlledSlug,\n normalizedBasePath,\n isLoading,\n appData,\n navigationProp,\n ]);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n\n const handlePopState = () => {\n const slug = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n setPreviousSlug(null);\n setPathSlug(slug);\n };\n\n window.addEventListener(\"popstate\", handlePopState);\n return () => window.removeEventListener(\"popstate\", handlePopState);\n }, [controlledSlug, normalizedBasePath]);\n\n // Logout hook — always called (rules of hooks) but only wired to UI below\n const logout = useLogout({\n redirectUrl: logoutRedirectUrl,\n onLogout,\n });\n\n const activeSlug = controlledSlug ?? pathSlug;\n\n // Read via ref so handleNavigate stays referentially stable across navigations.\n const activeSlugRef = useRef(activeSlug);\n activeSlugRef.current = activeSlug;\n\n const handleNavigate = useCallback(\n (slug: string) => {\n const current = activeSlugRef.current;\n if (slug !== current) {\n setPreviousSlug(current || null);\n }\n if (onNavigateProp) {\n onNavigateProp(slug);\n return;\n }\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${slug}`\n : `${normalizedBasePath}/${slug}`;\n history.pushState(null, \"\", targetPath);\n setPathSlug(slug);\n },\n [onNavigateProp, normalizedBasePath],\n );\n\n // Route widget item clicks (table rows, nested cards, etc.) to the matching\n // detail screen. Discriminates first on `shareable_type` for content\n // resources (products, media, pages, libraries), then enrollment packs (no\n // in-portal detail screen — open the BFF-provided canonical_url in a new\n // tab, same pattern as shop bundles in shop-app.tsx), then falls back to\n // subscription/order tokens for the BFF-backed normalized shapes.\n const handleWidgetItemClick = useCallback(\n (item: WidgetInteractionItem) => {\n const subscriptionToken = item.subscription_token;\n if (typeof subscriptionToken === \"string\" && subscriptionToken) {\n handleNavigate(`subscriptions/${subscriptionToken}`);\n return;\n }\n\n const shareableType = item.shareable_type;\n if (typeof shareableType === \"string\") {\n if (shareableType === \"EnrollmentPack\") {\n // share_link server-redirects to the same affiliate URL as canonical_url.\n const canonicalUrl = item.canonical_url;\n const shareLink = item.share_link;\n const externalUrl =\n typeof canonicalUrl === \"string\" && canonicalUrl\n ? canonicalUrl\n : typeof shareLink === \"string\" && shareLink\n ? shareLink\n : null;\n if (externalUrl) {\n window.open(externalUrl, \"_blank\", \"noopener,noreferrer\");\n }\n return;\n }\n if (item.id != null) {\n const slug = SHAREABLE_TYPE_TO_SLUG[shareableType];\n if (slug) {\n handleNavigate(`share/${slug}/${String(item.id)}`);\n return;\n }\n }\n }\n\n const orderToken = item.token;\n if (typeof orderToken === \"string\" && orderToken) {\n handleNavigate(`orders/${orderToken}`);\n }\n },\n [handleNavigate],\n );\n\n // Resolve sidebar footer: explicit prop wins, otherwise render default logout button\n const resolvedSidebarFooter = useMemo(\n () =>\n sidebarFooter !== undefined ? (\n sidebarFooter\n ) : (\n <SdkLogoutButton onLogout={logout} />\n ),\n [sidebarFooter, logout],\n );\n\n // Derive base slug and rest params via prefix matching. Use the combined\n // desktop+mobile slug set so a tap on a mobile-only nav item still resolves\n // to a registered prefix instead of falling through to the raw URL slug.\n const slugMatch = useMemo(\n () => matchSlugPrefix(activeSlug, allNavSlugs),\n [activeSlug, allNavSlugs],\n );\n const baseSlug = slugMatch?.matchedSlug ?? normalizeSlug(activeSlug);\n const restParams = slugMatch?.rest ?? \"\";\n\n // Derive mobile sub-tabs for the current section\n const mobileCurrentSection = findCurrentSection(\n filteredMobileNavItems,\n baseSlug,\n );\n const mobileSecondLevelTabs = mobileCurrentSection?.children ?? [];\n const currentNavItem =\n findNavItem(filteredNavItems, baseSlug) ??\n findNavItem(filteredMobileNavItems, baseSlug);\n\n // Resolve screen title from nav label or screen definition name\n const matchingScreen = screens?.find(\n (s) => s.id === currentNavItem?.screen_id,\n );\n const screenTitle =\n currentNavItem?.label || matchingScreen?.name || undefined;\n\n const handleScreenError = useCallback(\n (error: Error, info: ErrorInfo) => {\n errorReporter?.(error, {\n boundary: \"screen\",\n componentStack: info.componentStack,\n pathname:\n typeof window === \"undefined\" ? undefined : window.location.pathname,\n activeSlug,\n baseSlug,\n restParams,\n screenTitle,\n navItemSlug: currentNavItem?.slug,\n screenId: currentNavItem?.screen_id ?? matchingScreen?.id,\n });\n },\n [\n errorReporter,\n activeSlug,\n baseSlug,\n restParams,\n screenTitle,\n currentNavItem?.slug,\n currentNavItem?.screen_id,\n matchingScreen?.id,\n ],\n );\n\n // Determine content to render. Wrap in an error boundary keyed on the\n // active slug so a screen-level crash never takes down the navigation\n // chrome — and navigating to a different slug auto-recovers the boundary.\n const rawContent =\n typeof children === \"function\"\n ? children({ currentSlug: activeSlug, currentNavItem })\n : (children ?? (\n <ScreenHeaderProvider>\n <div className=\"flex h-full flex-col\">\n <TranslatedScreenHeader\n slug={currentNavItem?.slug}\n title={screenTitle}\n trailingContent={<LanguageSelector />}\n />\n <div\n className=\"relative isolate min-h-0 flex-1\"\n data-fluid-portal-screen-body=\"\"\n >\n <PageRouter\n currentSlug={activeSlug}\n currentNavItem={currentNavItem}\n customPages={customPages}\n memberTypeCustomPages={memberTypeCustomPages}\n screens={screens}\n baseSlug={baseSlug}\n restParams={restParams}\n navSlugs={allNavSlugs}\n />\n </div>\n </div>\n </ScreenHeaderProvider>\n ));\n\n const content = (\n <AppShellErrorBoundary\n key={activeSlug ?? \"\"}\n onError={errorReporter ? handleScreenError : undefined}\n >\n {rawContent}\n </AppShellErrorBoundary>\n );\n\n // Show loading state while app data is being fetched for the first time.\n // This check is placed after all hooks to satisfy React's rules of hooks.\n if (isLoading && !appData) {\n return <AppShellLoading />;\n }\n\n return (\n <RepUserProvider user={repUser}>\n <ThemeModeProvider\n mode={themeMode}\n onModeChange={handleThemeModeChange}\n autoModeEnabled={false}\n >\n <div\n className=\"font-body\"\n data-theme={activeTheme.id}\n data-theme-mode={themeMode === \"auto\" ? undefined : themeMode}\n >\n <AppNavigationProvider\n currentSlug={activeSlug}\n previousSlug={previousSlug}\n navItems={filteredNavItems}\n basePath={normalizedBasePath}\n navigate={handleNavigate}\n >\n <WidgetInteractionProvider\n onItemClick={handleWidgetItemClick}\n onNavigate={handleNavigate}\n >\n <BottomNavVisibilityProvider>\n <AppShellLayout\n sidebarContent={\n <SdkNavigation\n navItems={filteredNavItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n />\n }\n headerContent={\n <SdkHeader\n mobileTabs={mobileSecondLevelTabs}\n currentSlug={activeSlug}\n title={screenTitle}\n {...(restParams !== \"\" && {\n onBack: () => handleNavigate(baseSlug),\n })}\n onNavigate={handleNavigate}\n onLogout={logout}\n />\n }\n sidebarHeader={\n sidebarHeader !== undefined ? (\n sidebarHeader\n ) : (\n <SdkCompanySwitcher />\n )\n }\n sidebarFooter={resolvedSidebarFooter}\n useBottomNav\n afterContent={\n <SdkBottomNav\n navItems={filteredMobileNavItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n appDefinitionId={appData?.definition_id ?? 0}\n />\n }\n >\n <ScreenAreaFullscreenProvider>\n {content}\n </ScreenAreaFullscreenProvider>\n </AppShellLayout>\n </BottomNavVisibilityProvider>\n </WidgetInteractionProvider>\n </AppNavigationProvider>\n <Toaster />\n </div>\n </ThemeModeProvider>\n </RepUserProvider>\n );\n}\n","/**\n * Read Vite environment variables at runtime.\n *\n * Isolated into its own module so that Jest tests can mock it\n * (import.meta is a syntax-level construct that CJS/Jest cannot parse).\n */\nexport function getViteEnv(key: string): string | undefined {\n const env = import.meta.env as unknown as Record<string, string> | undefined;\n return env?.[key];\n}\n","import type { FluidSDKConfig } from \"../types/config\";\nimport { getViteEnv } from \"./env\";\n\n/**\n * Creates a FluidSDKConfig with sensible defaults.\n *\n * Default behavior:\n * - baseUrl: reads from `VITE_API_URL` env var, falls back to `\"\"` (same-origin relative)\n * - Auth: relies on session cookies sent via `credentials: 'include'`\n *\n * Pass overrides to customize any field:\n * ```ts\n * const config = createDefaultFluidConfig({ baseUrl: \"https://my-api.example.com\" });\n * ```\n */\nexport function createDefaultFluidConfig(\n overrides?: Partial<FluidSDKConfig>,\n): FluidSDKConfig {\n return {\n baseUrl: getViteEnv(\"VITE_API_URL\") ?? \"\",\n smartyEmbeddedKey: getViteEnv(\"NEXT_PUBLIC_SMARTY_EMBEDDED_KEY\"),\n googleMapsApiKey: getViteEnv(\"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY\"),\n ...overrides,\n };\n}\n","import {\n Component,\n StrictMode,\n type ComponentType,\n type ErrorInfo,\n type ReactNode,\n} from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport type { RemoteDomWidgetPackageDescriptor } from \"@fluid-app/portal-core/remote-dom-widget-package\";\nimport { FluidProvider } from \"../providers/FluidProvider\";\nimport { AppShell, type AppShellProps } from \"../shell/AppShell\";\nimport type { PortalErrorReporter } from \"../errors/portal-error-reporter\";\nimport { createDefaultFluidConfig } from \"../config/defaults\";\nimport type { FluidSDKConfig } from \"../types/config\";\nimport type { MemberTypeCustomPages } from \"@fluid-app/portal-core/member-type-content\";\nimport type { RemoteWidgetOptions } from \"../types/remote-widgets\";\n\n/**\n * Last-resort error boundary for crashes above the AppShell (provider\n * setup, theme/auth bootstrap). Renders above FluidThemeProvider, so the\n * theme CSS custom properties (`--color-foreground`, `--color-muted`, etc.)\n * are not yet applied — using inline styles + literal colors avoids\n * referencing theme tokens that would resolve to nothing.\n */\nclass RootBoundary extends Component<\n { children: ReactNode; errorReporter?: PortalErrorReporter },\n { error: Error | null }\n> {\n override state = { error: null as Error | null };\n\n static getDerivedStateFromError(error: Error): { error: Error } {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: ErrorInfo): void {\n if (this.props.errorReporter) {\n console.warn(\"[portal] Root crashed; reporting error:\", error, info);\n try {\n this.props.errorReporter(error, {\n boundary: \"root\",\n componentStack: info.componentStack,\n pathname:\n typeof window === \"undefined\"\n ? undefined\n : window.location.pathname,\n });\n } catch (callbackError) {\n // A throwing reporter must not mask the original crash.\n console.error(\"[portal] root error reporter threw:\", callbackError);\n console.error(\"[portal] Root crashed:\", error, info);\n }\n return;\n }\n\n console.error(\"[portal] Root crashed:\", error, info);\n }\n\n override render(): ReactNode {\n if (!this.state.error) return this.props.children;\n return (\n <div\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n fontFamily: \"system-ui, sans-serif\",\n }}\n >\n <div style={{ maxWidth: \"32rem\", textAlign: \"center\" }}>\n <h1 style={{ fontSize: \"1.25rem\", fontWeight: 600 }}>\n The app failed to load\n </h1>\n <p style={{ marginTop: \"0.5rem\", color: \"#666\" }}>\n Something went wrong while starting the portal. Please reload the\n page; if the problem persists, contact support.\n </p>\n <button\n type=\"button\"\n onClick={() => {\n window.location.reload();\n }}\n style={{\n marginTop: \"1.5rem\",\n padding: \"0.5rem 1rem\",\n borderRadius: \"0.375rem\",\n border: \"1px solid #ccc\",\n background: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n Reload page\n </button>\n </div>\n </div>\n );\n }\n}\n\n/** Configuration for both catalog and explicitly supplied Remote DOM packages. */\nexport interface PortalConfig {\n readonly customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /**\n * Per-member-type page overrides: memberTypeSlug -> (pageSlug -> component).\n * Serves different screens to different member types (e.g. an `affiliate`\n * dashboard) at the same slug. Wins over `customPages` for that member type.\n */\n readonly memberTypeCustomPages?: MemberTypeCustomPages;\n readonly remoteWidgets?: RemoteWidgetOptions;\n readonly fluid?: Partial<FluidSDKConfig>;\n readonly rootId?: string;\n readonly shell?: Omit<\n AppShellProps,\n \"customPages\" | \"memberTypeCustomPages\" | \"errorReporter\"\n >;\n readonly providers?: ComponentType<{ children: ReactNode }>;\n readonly errorReporter?: PortalErrorReporter;\n readonly disableStrictMode?: boolean;\n}\n\nexport type { RemoteDomWidgetPackageDescriptor, RemoteWidgetOptions };\n\n/** Mounts a portal with the shared SDK provider and canonical widget catalog. */\nexport function createPortal(config: PortalConfig = {}): void {\n const rootId = config.rootId ?? \"root\";\n const rootElement = document.getElementById(rootId);\n if (!rootElement) {\n throw new Error(`Portal root element \"#${rootId}\" not found`);\n }\n\n let app: ReactNode = (\n <AppShell\n customPages={config.customPages}\n memberTypeCustomPages={config.memberTypeCustomPages}\n errorReporter={config.errorReporter}\n {...config.shell}\n />\n );\n if (config.providers) {\n const Providers = config.providers;\n app = <Providers>{app}</Providers>;\n }\n\n const tree = (\n <RootBoundary errorReporter={config.errorReporter}>\n <FluidProvider\n config={createDefaultFluidConfig(config.fluid)}\n {...(config.remoteWidgets === undefined\n ? {}\n : { remoteWidgets: config.remoteWidgets })}\n >\n {app}\n </FluidProvider>\n </RootBoundary>\n );\n\n createRoot(rootElement).render(\n config.disableStrictMode ? tree : <StrictMode>{tree}</StrictMode>,\n );\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { Profile } from \"../types/profile\";\nimport { APP_DATA_QUERY_KEY, fetchRawManifest } from \"./use-fluid-app\";\n\n/**\n * Base query key for profile data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n *\n * @deprecated Use {@link APP_DATA_QUERY_KEY} with `useFluidApp` instead.\n */\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n/**\n * Hook to fetch the portal profile (themes, navigation, screens).\n *\n * Internally fetches from the fluidos API (same data source as\n * {@link useFluidApp}) and selects the `profile` slice, so no\n * legacy `/api/rep_app/manifest` call is made.\n *\n * @deprecated Use `useFluidApp()` instead — it returns the full\n * `RepAppData` including `profile`, `screens`, and more.\n *\n * @example\n * ```tsx\n * function Navigation() {\n * const { data: profile, isLoading } = useFluidProfile();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <nav>\n * {profile?.navigation.navigation_items.map(item => (\n * <NavItem key={item.id} item={item} />\n * ))}\n * </nav>\n * );\n * }\n * ```\n */\nexport function useFluidProfile(): UseQueryResult<Profile> {\n const appDefinitionApi = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n Profile,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => fetchRawManifest(appDefinitionApi),\n select: (raw) => {\n const appData = transformManifestToRepAppData(raw);\n return appData.profile;\n },\n });\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport type { AppDefinitionResponse } from \"@fluid-app/portal-core/app-definition-types\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\n\n/**\n * Base query key for the app definition endpoint.\n * The runtime key includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const APP_DEFINITION_QUERY_KEY = [\"fluid\", \"app-definition\"] as const;\n\n/**\n * Hook to fetch the active app manifest from the portal-tenant BFF.\n *\n * Returns the manifest metadata (id, name, version)\n * sourced from `GET /api/app/manifest` via the {@link AppDefinitionApi} port.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { data } = useAppDefinition();\n * const definitionId = data?.definition.id ?? 0;\n * }\n * ```\n */\nexport function useAppDefinition(options?: {\n enabled?: boolean;\n}): UseQueryResult<AppDefinitionResponse> {\n const api = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery({\n queryKey: scopeKey(APP_DEFINITION_QUERY_KEY),\n queryFn: () => api.fetchDefinition(),\n ...(options?.enabled !== undefined && { enabled: options.enabled }),\n });\n}\n","import { useThemeContext } from \"../providers/FluidThemeProvider\";\nimport type { ThemeDefinition } from \"../types/theme\";\n\n/**\n * Result of useFluidTheme hook\n */\nexport interface UseFluidThemeResult {\n /** Currently active theme */\n currentTheme: ThemeDefinition | null;\n /** Switch to a different theme */\n setTheme: (theme: ThemeDefinition) => void;\n /** Switch between light and dark mode */\n setThemeMode: (mode: \"light\" | \"dark\") => void;\n /** Current theme mode (convenience accessor) */\n mode: \"light\" | \"dark\" | undefined;\n}\n\n/**\n * Hook to access and control theme settings\n *\n * @example\n * ```tsx\n * function ThemeSwitcher({ themes }: { themes: ThemeDefinition[] }) {\n * const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();\n *\n * return (\n * <div>\n * <select\n * value={currentTheme?.name}\n * onChange={(e) => {\n * const theme = themes.find(t => t.name === e.target.value);\n * if (theme) setTheme(theme);\n * }}\n * >\n * {themes.map(theme => (\n * <option key={theme.name} value={theme.name}>\n * {theme.name}\n * </option>\n * ))}\n * </select>\n *\n * <button onClick={() => setThemeMode(mode === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle {mode === \"dark\" ? \"Light\" : \"Dark\"} Mode\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidTheme(): UseFluidThemeResult {\n const { currentTheme, setTheme, setThemeMode, mode } = useThemeContext();\n\n return {\n currentTheme,\n setTheme,\n setThemeMode,\n mode,\n };\n}\n","import { useCallback } from \"react\";\nimport type { MemberPermissionKey } from \"@fluid-app/portal-core/account-types\";\nimport {\n canAccessRepSurfaces,\n hasMemberPermission,\n memberTypeName,\n memberTypeSlug,\n} from \"@fluid-app/portal-core/member-permissions\";\nimport { useAccount } from \"./use-account\";\n\nexport interface MemberTypeInfo {\n /** Real per-company member-type slug (rep/customer/affiliate/...), or null while loading. */\n slug: string | null;\n /** Admin-facing display label (e.g. \"Affiliate\"), or null when unknown. */\n name: string | null;\n /** Whether the member may access rep-only surfaces (rep_eligible). */\n canAccessRepSurfaces: boolean;\n /** Predicate for any member-type permission key. */\n can: (key: MemberPermissionKey) => boolean;\n /** True until the account query resolves. */\n isLoading: boolean;\n}\n\n/**\n * Resolve the current member's type identity and capabilities.\n *\n * The single hook UI should use to display the member type (name/slug) and\n * to gate behavior on permissions, instead of comparing the binary\n * `member_type` string. Backed by the same cached `useAccount()` query.\n */\nexport function useMemberType(): MemberTypeInfo {\n const { data: account, isLoading } = useAccount();\n\n // Stable reference so consumers can safely list `can` in dependency arrays.\n const can = useCallback(\n (key: MemberPermissionKey) => hasMemberPermission(account, key),\n [account],\n );\n\n return {\n slug: memberTypeSlug(account),\n name: memberTypeName(account),\n canAccessRepSurfaces: canAccessRepSurfaces(account),\n can,\n isLoading,\n };\n}\n","import { useCallback, useRef } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseCompanySwitchOptions {\n // Preserved for API compatibility; no longer used internally.\n}\n\n/**\n * Hook that handles company switching for the portal.\n *\n * Calls PUT /api/authentication/company/{id}/switch, clears the TanStack\n * Query cache and IndexedDB persisted cache, then reloads the page so all\n * queries re-fetch with the new company context.\n *\n * The server updates the session cookie automatically via the switch\n * endpoint response; no client-side token storage is needed.\n */\nexport function useCompanySwitch(_options?: UseCompanySwitchOptions): {\n switchCompany: (companyId: number) => void;\n isPending: boolean;\n isError: boolean;\n error: Error | null;\n} {\n const client = usePortalTenantClient();\n const queryClient = useQueryClient();\n const switchingRef = useRef(false);\n\n const { mutate, isPending, isError, error } = useMutation({\n mutationFn: (companyId: number) =>\n client.put(`/api/authentication/company/${companyId}/switch`),\n onSuccess: async () => {\n // Clear in-memory cache and IndexedDB to prevent stale data\n // rehydration after reload.\n queryClient.clear();\n try {\n await deleteDatabase(\"company-switch\");\n } catch {\n // Non-fatal: company-scoped query keys prevent cross-company reads\n // even if the IDB delete fails.\n }\n // Reset the FairShare SDK's shopping identity so the new company's\n // session doesn't inherit the previous membership's cart/customer\n // (the SDK's cart/session cookies aren't company-scoped) — CURRENT-2180.\n resetFairShareSdk();\n\n // Session cookie is updated server-side by the switch endpoint.\n // Reload to re-fetch all queries with the new company context.\n window.location.reload();\n },\n onError: () => {\n switchingRef.current = false;\n },\n });\n\n const switchCompany = useCallback(\n (companyId: number) => {\n if (switchingRef.current) return;\n switchingRef.current = true;\n mutate(companyId);\n },\n [mutate],\n );\n\n return {\n switchCompany: switchCompany,\n isPending: isPending,\n isError: isError,\n error: error,\n };\n}\n","import type { CalendarEvent } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number, hour: number, minute: number = 0): Date {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n d.setHours(hour, minute, 0, 0);\n return d;\n}\n\nexport const DEMO_CALENDAR_EVENTS: readonly CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Standup\",\n description: { body: \"Daily sync with the sales team\" },\n color: \"#4f46e5\",\n start: daysFromNow(0, 9, 0).toISOString(),\n end: daysFromNow(0, 9, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n },\n {\n id: 2,\n title: \"Client Review - Acme Corp\",\n description: { body: \"Quarterly business review with key accounts\" },\n color: \"#059669\",\n start: daysFromNow(2, 14, 0).toISOString(),\n end: daysFromNow(2, 15, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"Conference Room B\",\n },\n {\n id: 3,\n title: \"Product Training Webinar\",\n description: { body: \"New product line training for the field\" },\n color: \"#d97706\",\n start: daysFromNow(4, 11, 0).toISOString(),\n end: daysFromNow(4, 12, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n url: \"https://example.com/webinar\",\n },\n {\n id: 4,\n title: \"Regional Conference\",\n description: { body: \"Annual West Coast regional conference\" },\n color: \"#dc2626\",\n start: daysFromNow(10, 8, 0).toISOString(),\n end: daysFromNow(12, 17, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"San Diego Convention Center\",\n isAllDay: true,\n },\n];\n","/**\n * Calendar events hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CalendarEvent, CalendarEventDescription } from \"./hook-types\";\nimport { DEMO_CALENDAR_EVENTS } from \"./demo-data/calendar-events\";\n\n// Re-export types so external consumers are unaffected\nexport type { CalendarEvent, CalendarEventDescription };\n\n/**\n * Result type for useCalendarEvents hook.\n * Uses QueryResult<CalendarEvent[]> with default Error type.\n */\nexport type UseCalendarEventsResult = QueryResult<CalendarEvent[]>;\n\n/**\n * Hook to fetch calendar events.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCalendarEvents(): UseCalendarEventsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CALENDAR_EVENTS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { Todo } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nexport const DEMO_TODOS: readonly Todo[] = [\n {\n id: 1,\n body: \"Follow up with Sarah about Q2 order\",\n dueAt: daysFromNow(1),\n completedAt: null,\n createdAt: daysFromNow(-2),\n contactName: \"Sarah Johnson\",\n },\n {\n id: 2,\n body: \"Send pricing proposal to Acme Corp\",\n dueAt: daysFromNow(7),\n completedAt: null,\n createdAt: daysFromNow(-1),\n contactName: \"Mike Chen\",\n },\n {\n id: 3,\n body: \"Schedule onboarding call with new team member\",\n dueAt: daysFromNow(3),\n completedAt: null,\n createdAt: daysFromNow(0),\n contactName: \"Emily Rodriguez\",\n },\n {\n id: 4,\n body: \"Review and approve Q1 expense report\",\n dueAt: daysFromNow(-1),\n completedAt: daysFromNow(-1),\n createdAt: daysFromNow(-5),\n contactName: null,\n },\n {\n id: 5,\n body: \"Prepare presentation for Friday team meeting\",\n dueAt: daysFromNow(4),\n completedAt: daysFromNow(0),\n createdAt: daysFromNow(-3),\n contactName: null,\n },\n];\n","/**\n * Todos hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { Todo } from \"./hook-types\";\nimport { DEMO_TODOS } from \"./demo-data/todos\";\n\n// Re-export type so external consumers are unaffected\nexport type { Todo };\n\n/**\n * Result type for useTodos hook.\n * Uses QueryResult<Todo[]> with default Error type.\n */\nexport type UseTodosResult = QueryResult<Todo[]>;\n\n/**\n * Hook to fetch todo items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useTodos(): UseTodosResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_TODOS],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Hook type utilities and type predicates.\n *\n * This module provides:\n * - Generic hook result types with default type parameters\n * - Type predicates for query state narrowing\n * - Reusable patterns for type-safe property access in hooks\n *\n * Following generics best practices:\n * - generics-default-type-parameters: Default E to Error for common case\n * - generics-type-predicates: Type predicates for result state narrowing\n * - generics-constrain-type-parameters: K extends keyof T for property access\n */\n\n// =============================================================================\n// Base Result Types with Default Type Parameters\n// =============================================================================\n\n/**\n * Base result type for query hooks with default error type.\n * Uses default type parameter for E (generics-default-type-parameters rule).\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n *\n * @example\n * // Error type defaults to Error\n * type UsersResult = QueryResult<User[]>;\n *\n * // Can override when needed\n * type CustomResult = QueryResult<User[], ApiError>;\n */\nexport interface QueryResult<T, E = Error> {\n readonly data: T;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for hooks that may not have data yet.\n * Extends QueryResult with nullable data.\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface QueryResultNullable<T, E = Error> {\n readonly data: T | null | undefined;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for list/collection hooks with aggregates.\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ListQueryResult<T, E = Error> extends QueryResult<T[], E> {\n readonly totalCount: number;\n}\n\n/**\n * Result type for list hooks with value aggregation (e.g., deals with total value).\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ValueListQueryResult<T, E = Error> extends ListQueryResult<\n T,\n E\n> {\n readonly totalValue: number;\n}\n\n// =============================================================================\n// Type Predicates for Query State Narrowing\n// =============================================================================\n\n/**\n * Type predicate to check if a query result has successfully loaded data.\n * Narrows the data type from T | null | undefined to T.\n *\n * @example\n * const result = useContact(id);\n * if (hasData(result)) {\n * // TypeScript knows result.data is Contact, not Contact | null\n * console.log(result.data.name);\n * }\n */\nexport function hasData<T, E = Error>(\n result: QueryResultNullable<T, E>,\n): result is QueryResultNullable<T, E> & { readonly data: T } {\n return result.data != null && !result.isLoading && !result.isError;\n}\n\n/**\n * Type predicate to check if a query result is in loading state.\n * Useful for conditional rendering.\n *\n * @example\n * if (isLoading(result)) {\n * return <Spinner />;\n * }\n */\nexport function isLoading<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return result.isLoading;\n}\n\n/**\n * Type predicate to check if a query result has an error.\n * Narrows to include the error property.\n *\n * @example\n * if (isErrorResult(result)) {\n * console.error(result.error); // error is E, not undefined\n * }\n */\nexport function isErrorResult<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): result is (QueryResult<T, E> | QueryResultNullable<T, E>) & {\n readonly isError: true;\n readonly error: E;\n} {\n return result.isError && result.error !== undefined;\n}\n\n/**\n * Type predicate to check if a query result is in idle state (not loading, no error, has data).\n *\n * @example\n * if (isIdle(result)) {\n * // Safe to access and render data\n * }\n */\nexport function isIdle<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return !result.isLoading && !result.isError;\n}\n\n// =============================================================================\n// Generic Property Selector Pattern\n// =============================================================================\n\n/**\n * Type-safe property selector for hook results.\n * Uses K extends keyof T constraint (generics-function-constraints rule).\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n *\n * @example\n * const users = [{ name: \"Alice\", age: 30 }];\n * const names = selectProperty(users, \"name\"); // string[]\n * const ages = selectProperty(users, \"age\"); // number[]\n * selectProperty(users, \"invalid\"); // Error: \"invalid\" not in \"name\" | \"age\"\n */\nexport function selectProperty<T, K extends keyof T>(\n items: readonly T[],\n key: K,\n): T[K][] {\n return items.map((item) => item[key]);\n}\n\n/**\n * Type-safe property getter for a single item.\n * Returns undefined if item is null/undefined.\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n */\nexport function getProperty<T, K extends keyof T>(\n item: T | null | undefined,\n key: K,\n): T[K] | undefined {\n return item?.[key];\n}\n\n// =============================================================================\n// Hook Factory Types\n// =============================================================================\n\n/**\n * Generic type for hooks that fetch a single resource by ID.\n * Useful for creating consistent API across different resource types.\n *\n * @typeParam T - The resource type\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseSingleResourceHook<T, E = Error> = (\n id: string,\n) => QueryResultNullable<T, E>;\n\n/**\n * Generic type for hooks that fetch a list of resources with optional params.\n * Uses generics-default-type-parameters for the params type.\n *\n * @typeParam T - The item type\n * @typeParam P - The params type (defaults to empty object)\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseListResourceHook<\n T,\n P extends Record<string, unknown> = Record<string, never>,\n E = Error,\n> = (params?: P) => ListQueryResult<T, E>;\n\n/**\n * Transforms a nullable result to a non-nullable one if data exists.\n * Useful when you've already checked hasData().\n */\nexport type WithData<R extends QueryResultNullable<unknown>> =\n R extends QueryResultNullable<infer T, infer E>\n ? QueryResultNullable<T, E> & { readonly data: T }\n : never;\n\n// =============================================================================\n// Domain Types\n// Shared between hook files and demo-data files to avoid circular imports.\n// =============================================================================\n\n/**\n * Activity slug constants as a const object.\n * Derive the ActivitySlug type from this single source of truth.\n */\nexport const ACTIVITY_SLUGS = {\n abandonedCart: \"abandoned_cart\",\n announcements: \"announcements\",\n cartItemsAdded: \"cart_items_added\",\n commentReply: \"comment_reply\",\n directMessage: \"direct_message\",\n fantasyPoint: \"fantasy_point\",\n newLead: \"new_lead\",\n orderPlaced: \"order_placed\",\n pageViews: \"page_views\",\n pageViewsContact: \"page_views_contact\",\n tasks: \"tasks\",\n upcomingEvent: \"upcoming_event\",\n video: \"video\",\n videoComplete: \"video_complete\",\n videoCompleteContact: \"video_complete_contact\",\n videoContact: \"video_contact\",\n messageReceived: \"message_received\",\n messageSent: \"message_sent\",\n newCartItemsAdded: \"new_cart_items_added\",\n smartLinkClicked: \"smart_link_clicked\",\n reviewLeft: \"review_left\",\n} as const;\n\n/** Activity slug union type derived from ACTIVITY_SLUGS constant. */\nexport type ActivitySlug = (typeof ACTIVITY_SLUGS)[keyof typeof ACTIVITY_SLUGS];\n\n/** Type predicate to check if a string is a valid ActivitySlug. */\nexport function isActivitySlug(value: string): value is ActivitySlug {\n return Object.values(ACTIVITY_SLUGS).includes(value as ActivitySlug);\n}\n\n/** Transformed activity for display. */\nexport interface Activity {\n readonly id: number;\n readonly userName: string;\n readonly avatarUrl: string | null;\n readonly activityType: string;\n readonly targetName: string;\n readonly timestamp: string;\n readonly slug: ActivitySlug;\n}\n\n/** Description/rich text metadata for a calendar event. */\nexport interface CalendarEventDescription {\n readonly id?: number | null;\n readonly name?: string | null;\n readonly body?: string | null;\n readonly record_type?: string | null;\n readonly record_id?: number | null;\n readonly created_at?: string | null;\n readonly updated_at?: string | null;\n readonly locale?: string | null;\n}\n\n/** Calendar event data from the API. */\nexport interface CalendarEvent {\n readonly id: number;\n readonly title: string;\n readonly description?: CalendarEventDescription | null;\n readonly color?: string | null;\n readonly url?: string | null;\n readonly start: string;\n readonly end: string;\n readonly active?: boolean | null;\n readonly time_zone?: string | null;\n readonly status?: string | null;\n readonly image_url?: string | null;\n readonly images?: readonly unknown[] | null;\n readonly venue?: string | null;\n readonly countries?: readonly string[] | null;\n readonly hasTomorrow?: boolean | null;\n readonly hasYesterday?: boolean | null;\n readonly isAllDay?: boolean;\n}\n\n/** Catch up suggestion data from the API. */\nexport interface CatchUp {\n readonly id: number;\n readonly suggestion_title: string;\n}\n\n/** MySite data returned by the hook. */\nexport interface MySiteData {\n readonly url: string | null;\n readonly views: number;\n readonly leads: number;\n readonly userName: string;\n}\n\n/** Transformed todo for display. */\nexport interface Todo {\n readonly id: number;\n readonly body: string;\n readonly dueAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly contactName: string | null;\n}\n","import type { Activity } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\nexport const DEMO_ACTIVITIES: readonly Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"New Order\",\n targetName: \"Wellness Starter Kit\",\n timestamp: hoursAgo(1),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Referral from LinkedIn campaign\",\n timestamp: hoursAgo(3),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Emily Rodriguez\",\n avatarUrl: null,\n activityType: \"Page View\",\n targetName: \"Product Catalog\",\n timestamp: hoursAgo(5),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"David Park\",\n avatarUrl: null,\n activityType: \"Cart Items Added\",\n targetName: \"Premium Bundle (3 items)\",\n timestamp: hoursAgo(8),\n slug: \"cart_items_added\",\n },\n {\n id: 5,\n userName: \"Lisa Thompson\",\n avatarUrl: null,\n activityType: \"Video Watched\",\n targetName: \"Getting Started Tutorial\",\n timestamp: hoursAgo(12),\n slug: \"video_complete\",\n },\n {\n id: 6,\n userName: \"James Wilson\",\n avatarUrl: null,\n activityType: \"Message Received\",\n targetName: \"Question about shipping\",\n timestamp: hoursAgo(18),\n slug: \"message_received\",\n },\n {\n id: 7,\n userName: \"Rachel Kim\",\n avatarUrl: null,\n activityType: \"Smart Link Clicked\",\n targetName: \"Holiday Promo Link\",\n timestamp: hoursAgo(24),\n slug: \"smart_link_clicked\",\n },\n {\n id: 8,\n userName: \"Tom Martinez\",\n avatarUrl: null,\n activityType: \"Review Left\",\n targetName: \"Essential Oil Set - 5 stars\",\n timestamp: hoursAgo(36),\n slug: \"review_left\",\n },\n];\n","/**\n * Activities hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport {\n ACTIVITY_SLUGS,\n isActivitySlug,\n type Activity,\n type ActivitySlug,\n} from \"./hook-types\";\nimport { DEMO_ACTIVITIES } from \"./demo-data/activities\";\n\n// Re-export types and constants so external consumers are unaffected\nexport { ACTIVITY_SLUGS, isActivitySlug };\nexport type { Activity, ActivitySlug };\n\n/**\n * Result type for useActivities hook.\n * Uses QueryResult generic with the activities + optional windowed totalCount.\n */\nexport type UseActivitiesData = {\n activities: Activity[];\n totalCount?: number;\n};\nexport type UseActivitiesResult = QueryResult<UseActivitiesData>;\n\n/**\n * Hook to fetch recent activities. The optional `daysAgo` arg windows the\n * result (e.g. `useActivities({ daysAgo: 7 })`) and surfaces the windowed\n * total via `data.totalCount` — used by the Recent Activity widget chip.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useActivities(_args?: {\n daysAgo?: number;\n}): UseActivitiesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: {\n activities: [...DEMO_ACTIVITIES],\n totalCount: DEMO_ACTIVITIES.length,\n },\n isLoading: false,\n isError: false,\n };\n}\n","import type { CatchUp } from \"../hook-types\";\n\nexport const DEMO_CATCHUPS: readonly CatchUp[] = [\n {\n id: 1,\n suggestion_title: \"Sarah Johnson hasn't ordered in 30 days\",\n },\n {\n id: 2,\n suggestion_title: \"Mike Chen's subscription is expiring soon\",\n },\n {\n id: 3,\n suggestion_title: \"Emily Rodriguez left a cart with 3 items\",\n },\n];\n","/**\n * Catch ups hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CatchUp } from \"./hook-types\";\nimport { DEMO_CATCHUPS } from \"./demo-data/catchups\";\n\n// Re-export type so external consumers are unaffected\nexport type { CatchUp };\n\n/**\n * Result type for useCatchUps hook.\n * Uses QueryResult<CatchUp[]> with default Error type.\n */\nexport type UseCatchUpsResult = QueryResult<CatchUp[]>;\n\n/**\n * Hook to fetch catch up items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCatchUps(): UseCatchUpsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CATCHUPS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { MySiteData } from \"../hook-types\";\n\nexport const DEMO_MYSITE: MySiteData = {\n url: \"https://my-portal.example.com\",\n views: 1243,\n leads: 37,\n userName: \"Demo User\",\n};\n","/**\n * MySite hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResultNullable } from \"./hook-types\";\nimport type { MySiteData } from \"./hook-types\";\nimport { DEMO_MYSITE } from \"./demo-data/mysite\";\n\n// Re-export type so external consumers are unaffected\nexport type { MySiteData };\n\n/**\n * Result type for useMySite hook.\n * Uses QueryResultNullable since MySite data may not be available.\n */\nexport type UseMySiteResult = QueryResultNullable<MySiteData>;\n\n/**\n * Hook to fetch MySite data.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useMySite(): UseMySiteResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: DEMO_MYSITE,\n isLoading: false,\n isError: false,\n };\n}\n","import type { Conversation, Message } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONVERSATIONS: readonly Conversation[] = [\n {\n id: \"conv-1\",\n title: \"Sarah Johnson\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-2\",\n name: \"Sarah Johnson\",\n email: \"sarah.johnson@example.com\",\n isOnline: true,\n },\n ],\n lastMessage: {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n unreadCount: 1,\n status: \"active\",\n createdAt: daysAgo(30),\n updatedAt: hoursAgo(1),\n },\n {\n id: \"conv-2\",\n title: \"Mike Chen\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-3\",\n name: \"Mike Chen\",\n email: \"mike.chen@acmecorp.com\",\n isOnline: false,\n },\n ],\n lastMessage: {\n id: \"msg-2-2\",\n conversationId: \"conv-2\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"I've attached the updated pricing sheet. Let me know if you have any questions!\",\n timestamp: hoursAgo(5),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(14),\n updatedAt: hoursAgo(5),\n },\n {\n id: \"conv-3\",\n title: \"Team Updates\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-4\",\n name: \"Emily Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n },\n { id: \"user-5\", name: \"David Park\", email: \"david.park@email.com\" },\n ],\n lastMessage: {\n id: \"msg-3-4\",\n conversationId: \"conv-3\",\n senderId: \"user-4\",\n senderName: \"Emily Rodriguez\",\n type: \"text\",\n content:\n \"The new product samples arrived today. Will distribute to the team tomorrow.\",\n timestamp: daysAgo(1),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(60),\n updatedAt: daysAgo(1),\n },\n];\n\nexport const DEMO_MESSAGES: readonly Message[] = [\n {\n id: \"msg-1-1\",\n conversationId: \"conv-1\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"Hi Sarah! I wanted to follow up on the Wellness Starter Kit. We have a special promotion running this month.\",\n timestamp: hoursAgo(3),\n isRead: true,\n },\n {\n id: \"msg-1-2\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content:\n \"That's perfect timing! I was just thinking about reordering. What's the promo?\",\n timestamp: hoursAgo(2),\n isRead: true,\n },\n {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n];\n","/**\n * Conversations hooks - stub implementations for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Conversation, Message } from \"../types/screen-types\";\nimport type { QueryResult } from \"./hook-types\";\nimport { DEMO_CONVERSATIONS, DEMO_MESSAGES } from \"./demo-data/conversations\";\n\n// =============================================================================\n// useConversations Hook\n// =============================================================================\n\n/**\n * Result type for useConversations hook.\n * Uses QueryResult<Conversation[]> with default Error type.\n */\nexport type UseConversationsResult = QueryResult<Conversation[]>;\n\n/**\n * Hook to fetch all conversations.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @returns UseConversationsResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: conversations, isLoading, isError } = useConversations();\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return conversations.map(conv => <ConversationItem key={conv.id} {...conv} />);\n * ```\n */\nexport function useConversations(): UseConversationsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONVERSATIONS],\n isLoading: false,\n isError: false,\n };\n}\n\n// =============================================================================\n// useConversationMessages Hook\n// =============================================================================\n\n/**\n * Result type for useConversationMessages hook.\n * Uses QueryResult<Message[]> with default Error type.\n */\nexport type UseConversationMessagesResult = QueryResult<Message[]>;\n\n/**\n * Hook to fetch messages for a specific conversation.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param conversationId - The ID of the conversation to fetch messages for\n * @returns UseConversationMessagesResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: messages, isLoading, isError } = useConversationMessages(conversationId);\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return messages.map(msg => <MessageBubble key={msg.id} {...msg} />);\n * ```\n */\nexport function useConversationMessages(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _conversationId: string,\n): UseConversationMessagesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _conversationId\n return {\n data: [...DEMO_MESSAGES],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Screen Types - Type definitions for core feature screens\n *\n * All status and type unions are derived from constants for single source of truth.\n * Use the constants (e.g., CONVERSATION_STATUSES.active) for type-safe comparisons.\n */\n\n// =============================================================================\n// Messaging Types\n// =============================================================================\n\n/**\n * Conversation status constant - single source of truth.\n */\nexport const CONVERSATION_STATUSES = {\n active: \"active\",\n archived: \"archived\",\n muted: \"muted\",\n} as const;\n\n/**\n * Union type derived from CONVERSATION_STATUSES constant.\n */\nexport type ConversationStatus =\n (typeof CONVERSATION_STATUSES)[keyof typeof CONVERSATION_STATUSES];\n\n/**\n * Message type constant - single source of truth.\n */\nexport const MESSAGE_TYPES = {\n text: \"text\",\n image: \"image\",\n file: \"file\",\n system: \"system\",\n} as const;\n\n/**\n * Union type derived from MESSAGE_TYPES constant.\n */\nexport type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];\n\nexport interface Participant {\n readonly id: string;\n readonly name: string;\n readonly email: string;\n readonly avatarUrl?: string;\n readonly isOnline?: boolean;\n}\n\n/**\n * Message attachment type - extracted for reusability and clarity.\n */\nexport interface MessageAttachment {\n readonly id: string;\n readonly name: string;\n readonly url: string;\n readonly type: string;\n readonly size?: number;\n}\n\nexport interface Message {\n readonly id: string;\n readonly conversationId: string;\n readonly senderId: string;\n readonly senderName: string;\n readonly senderAvatarUrl?: string;\n readonly type: MessageType;\n readonly content: string;\n readonly timestamp: string;\n readonly isRead: boolean;\n readonly attachments?: readonly MessageAttachment[];\n}\n\nexport interface Conversation {\n readonly id: string;\n readonly title: string;\n readonly participants: readonly Participant[];\n readonly lastMessage?: Message;\n readonly unreadCount: number;\n readonly status: ConversationStatus;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n\n// =============================================================================\n// Contacts Types\n// =============================================================================\n\n/**\n * Contact status constant - single source of truth.\n */\nexport const CONTACT_STATUSES = {\n active: \"active\",\n inactive: \"inactive\",\n lead: \"lead\",\n prospect: \"prospect\",\n} as const;\n\n/**\n * Union type derived from CONTACT_STATUSES constant.\n */\nexport type ContactStatus =\n (typeof CONTACT_STATUSES)[keyof typeof CONTACT_STATUSES];\n\n/**\n * Contact type constant - single source of truth.\n */\nexport const CONTACT_TYPES = {\n individual: \"individual\",\n company: \"company\",\n} as const;\n\n/**\n * Union type derived from CONTACT_TYPES constant.\n */\nexport type ContactType = (typeof CONTACT_TYPES)[keyof typeof CONTACT_TYPES];\n\nexport interface ContactAddress {\n readonly street?: string;\n readonly city?: string;\n readonly state?: string;\n readonly postalCode?: string;\n readonly country?: string;\n}\n\nexport interface Contact {\n readonly id: string;\n readonly firstName: string;\n readonly lastName: string;\n readonly email: string;\n readonly phone?: string;\n readonly company?: string;\n readonly jobTitle?: string;\n readonly avatarUrl?: string;\n readonly status: ContactStatus;\n readonly type: ContactType;\n readonly address?: ContactAddress;\n readonly tags?: readonly string[];\n readonly notes?: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n","import type { Contact } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONTACTS: readonly Contact[] = [\n {\n id: \"contact-1\",\n firstName: \"Sarah\",\n lastName: \"Johnson\",\n email: \"sarah.johnson@example.com\",\n phone: \"+1 (555) 123-4567\",\n company: \"Wellness Works Inc.\",\n jobTitle: \"Purchasing Manager\",\n status: \"active\",\n type: \"individual\",\n tags: [\"VIP\", \"Repeat Buyer\"],\n createdAt: daysAgo(90),\n updatedAt: daysAgo(2),\n },\n {\n id: \"contact-2\",\n firstName: \"Mike\",\n lastName: \"Chen\",\n email: \"mike.chen@acmecorp.com\",\n phone: \"+1 (555) 234-5678\",\n company: \"Acme Corp\",\n jobTitle: \"Director of Operations\",\n status: \"active\",\n type: \"individual\",\n tags: [\"Enterprise\"],\n createdAt: daysAgo(60),\n updatedAt: daysAgo(5),\n },\n {\n id: \"contact-3\",\n firstName: \"Emily\",\n lastName: \"Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n company: \"HealthFirst\",\n status: \"lead\",\n type: \"individual\",\n notes: \"Interested in bulk pricing for team orders\",\n createdAt: daysAgo(7),\n updatedAt: daysAgo(1),\n },\n {\n id: \"contact-4\",\n firstName: \"David\",\n lastName: \"Park\",\n email: \"david.park@email.com\",\n phone: \"+1 (555) 345-6789\",\n status: \"prospect\",\n type: \"individual\",\n tags: [\"Referral\"],\n createdAt: daysAgo(14),\n updatedAt: daysAgo(10),\n },\n {\n id: \"contact-5\",\n firstName: \"Lisa\",\n lastName: \"Thompson\",\n email: \"lisa.t@globalfit.com\",\n company: \"Global Fitness\",\n jobTitle: \"CEO\",\n status: \"active\",\n type: \"individual\",\n address: {\n city: \"Los Angeles\",\n state: \"CA\",\n country: \"US\",\n },\n createdAt: daysAgo(120),\n updatedAt: daysAgo(15),\n },\n {\n id: \"contact-6\",\n firstName: \"James\",\n lastName: \"Wilson\",\n email: \"jwilson@retired.net\",\n status: \"inactive\",\n type: \"individual\",\n notes: \"Moved out of area, no longer purchasing\",\n createdAt: daysAgo(200),\n updatedAt: daysAgo(45),\n },\n];\n\nexport const DEMO_CONTACT: Contact = DEMO_CONTACTS[0]!;\n","/**\n * Contacts hooks - stub implementation for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Contact, ContactStatus } from \"../types/screen-types\";\nimport { CONTACT_STATUSES } from \"../types/screen-types\";\nimport type { ListQueryResult, QueryResultNullable } from \"./hook-types\";\nimport { DEMO_CONTACTS, DEMO_CONTACT } from \"./demo-data/contacts\";\n\n/**\n * Type predicate to check if a status string is a valid ContactStatus.\n * Enables runtime validation with type narrowing.\n */\nexport function isContactStatus(value: string): value is ContactStatus {\n return Object.values(CONTACT_STATUSES).includes(value as ContactStatus);\n}\n\n/**\n * Parameters for filtering and paginating contacts.\n * Uses readonly properties and proper ContactStatus type for status.\n */\nexport interface UseContactsParams {\n /** Search query to filter contacts by name, email, or company */\n readonly search?: string;\n /** Filter contacts by status - uses ContactStatus union type for type safety */\n readonly status?: ContactStatus;\n /** Maximum number of contacts to return */\n readonly limit?: number;\n}\n\n/**\n * Result type for the useContacts hook.\n * Uses ListQueryResult<Contact> with totalCount and default Error type.\n */\nexport type UseContactsResult = ListQueryResult<Contact>;\n\n/**\n * Result type for the useContact hook.\n * Uses QueryResultNullable since a specific contact may not exist.\n */\nexport type UseContactResult = QueryResultNullable<Contact>;\n\n/**\n * Hook to fetch a list of contacts with optional filtering and pagination.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param params - Optional parameters for filtering and pagination\n * @returns Object containing contacts data, loading state, error state, and total count\n *\n * @example\n * ```tsx\n * const { data: contacts, isLoading, totalCount } = useContacts({\n * search: 'john',\n * status: 'active',\n * limit: 20\n * });\n * ```\n */\nexport function useContacts(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _params?: UseContactsParams,\n): UseContactsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONTACTS],\n isLoading: false,\n isError: false,\n totalCount: DEMO_CONTACTS.length,\n };\n}\n\n/**\n * Hook to fetch a single contact by ID.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param contactId - The unique identifier of the contact to fetch\n * @returns Object containing contact data, loading state, and error state\n *\n * @example\n * ```tsx\n * const { data: contact, isLoading, isError } = useContact('contact-123');\n * ```\n */\nexport function useContact(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _contactId: string,\n): UseContactResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _contactId\n return {\n data: DEMO_CONTACT,\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Auth Types\n *\n * These types define the JWT payload structure and authentication\n * configuration options.\n */\n\n// ============================================================================\n// User Types - Derive from constant for single source of truth\n// ============================================================================\n\n/**\n * User type constant - single source of truth for user role values.\n * Use USER_TYPES.admin instead of \"admin\" for type-safe comparisons.\n */\nexport const USER_TYPES = {\n admin: \"admin\",\n rep: \"rep\",\n root_admin: \"root_admin\",\n customer: \"customer\",\n} as const;\n\n/**\n * Union type of all user types, derived from USER_TYPES constant.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type UserType = (typeof USER_TYPES)[keyof typeof USER_TYPES];\n\n/**\n * Runtime validation for user types.\n * @param value - The value to check\n * @returns true if value is a valid UserType\n */\nexport function isUserType(value: string): value is UserType {\n return Object.values(USER_TYPES).includes(value as UserType);\n}\n\n/**\n * JWT payload structure from Fluid Commerce authentication.\n * Contains user identity and role information.\n */\nexport interface JWTPayload {\n /** User ID */\n id?: number | undefined;\n /** User email address */\n email?: string | undefined;\n /** Full name of the user */\n full_name?: string | undefined;\n /** User role type */\n user_type: UserType;\n /** Original user type (for impersonation scenarios) */\n og_user_type?: UserType | undefined;\n /** Company ID the user belongs to */\n company_id?: number | undefined;\n /** Token expiration timestamp (Unix seconds) */\n exp?: number | undefined;\n /** Authentication type (e.g., \"standard\", \"impersonation\") */\n auth_type?: string | undefined;\n}\n\n/**\n * Configuration options for FluidAuthProvider.\n * All options have sensible defaults.\n */\nexport interface FluidAuthConfig {\n /**\n * URL parameter name for the auth token.\n * @default \"fluidUserToken\"\n */\n tokenKey?: string;\n\n /**\n * Cookie name for storing the auth token.\n * @default \"auth_token\"\n */\n cookieKey?: string;\n\n /**\n * Cookie max age in seconds.\n * @default 777600 (9 days)\n */\n cookieMaxAge?: number;\n\n /**\n * Grace period in milliseconds to account for clock skew\n * when checking token expiration.\n * @default 30000 (30 seconds)\n */\n gracePeriodMs?: number;\n\n /**\n * Callback invoked when authentication fails (no valid token).\n * When omitted, the SDK redirects to `authUrl` with the current URL\n * as a redirect parameter so users can log in and return.\n */\n onAuthFailure?: () => void;\n\n /**\n * Base URL for the authentication page.\n * Used by the default auth failure redirect when `onAuthFailure` is not provided.\n * Ignored when a custom `onAuthFailure` callback is set.\n * @default \"https://auth.fluid.app\"\n */\n authUrl?: string;\n\n /**\n * Enable dev-mode auth bypass.\n * When true AND running in Vite dev mode (import.meta.env.DEV),\n * auth will use a synthetic mock user instead of requiring a real JWT.\n * The mock user allows UI rendering but API calls will fail (token is null).\n * @default false\n */\n devBypass?: boolean;\n\n /**\n * JWKS (JSON Web Key Set) URL for signature verification.\n * When provided, JWT signatures will be verified against keys\n * from this endpoint before accepting the token.\n *\n * This provides defense-in-depth client-side verification.\n * Without this, tokens are only decoded (not verified) client-side,\n * and real verification happens server-side on API calls.\n *\n * @example \"https://api.fluid.app/.well-known/jwks.json\"\n */\n jwksUrl?: string;\n}\n\n/**\n * Value provided by the FluidAuthContext.\n * All properties are readonly since context values should not be mutated by consumers.\n */\nexport interface FluidAuthContextValue {\n /** Whether the user is authenticated with a valid token */\n readonly isAuthenticated: boolean;\n /** Whether authentication is still being initialized */\n readonly isLoading: boolean;\n /** Decoded JWT payload if authenticated, null otherwise */\n readonly user: JWTPayload | null;\n /** Raw JWT token string if authenticated, null otherwise */\n readonly token: string | null;\n /** Clear authentication state and stored tokens */\n readonly clearAuth: () => void;\n /** Authentication error if any occurred during initialization */\n readonly error: Error | null;\n}\n\n/**\n * Result of token validation.\n * Uses a discriminated union for type-safe handling of valid/invalid states.\n */\nexport type TokenValidationResult =\n | {\n /** Token is valid */\n isValid: true;\n /** Decoded JWT payload */\n payload: JWTPayload;\n /** No error when valid */\n error?: undefined;\n }\n | {\n /** Token is invalid */\n isValid: false;\n /** Decoded JWT payload if parseable but expired */\n payload?: JWTPayload;\n /** Error message explaining why validation failed */\n error: string;\n };\n","import { z } from \"zod\";\nimport type { FetchClientInstance } from \"@fluid-app/api-client-core\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface Company {\n id: number;\n name: string;\n logo_url?: string | null;\n icon_url?: string | null;\n}\n\nexport interface SwitchCompanyResponseCompany {\n id: number;\n name: string;\n fluid_shop: string;\n logo_url?: string | null;\n jwt: string;\n}\n\nexport interface SwitchCompanyResponseMeta {\n request_id: string;\n timestamp: string;\n}\n\nexport interface SwitchCompanyResponse {\n company: SwitchCompanyResponseCompany;\n meta: SwitchCompanyResponseMeta;\n}\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nexport const companySchema: z.ZodType<Company> = z.object({\n id: z.number(),\n name: z.string(),\n logo_url: z.string().optional().nullable(),\n icon_url: z.string().nullable().optional(),\n});\n\nexport const switchCompanyResponseCompanySchema: z.ZodType<SwitchCompanyResponseCompany> =\n z.object({\n id: z.number().int().positive(),\n name: z.string(),\n fluid_shop: z.string(),\n logo_url: z.string().url().nullable().optional(),\n jwt: z.string(),\n });\n\nexport const switchCompanyResponseMetaSchema: z.ZodType<SwitchCompanyResponseMeta> =\n z.object({\n request_id: z.string(),\n timestamp: z.string(),\n });\n\nexport const switchCompanyResponseSchema: z.ZodType<SwitchCompanyResponse> =\n z.object({\n company: switchCompanyResponseCompanySchema,\n meta: switchCompanyResponseMetaSchema,\n });\n\n// ============================================================================\n// API function\n// ============================================================================\n\nexport function switchCompany(\n client: FetchClientInstance,\n companyId: number,\n): Promise<SwitchCompanyResponse> {\n return client\n .put<unknown>(`/authentication/company/${companyId}/switch`)\n .then((data) => switchCompanyResponseSchema.parse(data));\n}\n","/**\n * Core page template IDs\n */\nexport const CORE_PAGE_IDS = {\n PROFILE: \"core-profile\",\n MESSAGING: \"core-messaging\",\n CONTACTS: \"core-contacts\",\n ORDERS: \"core-orders\",\n SUBSCRIPTIONS: \"core-subscriptions\",\n CUSTOMERS: \"core-customers\",\n MY_SITE: \"core-my-site\",\n SHAREABLES: \"core-shareables\",\n SHOP: \"core-shop\",\n APP_DOWNLOAD: \"core-app-download\",\n} as const;\n","// Screen Components\nexport { ProfileScreen, profileScreenPropertySchema } from \"./ProfileScreen\";\nexport {\n MessagingScreen,\n messagingScreenPropertySchema,\n} from \"./MessagingScreen\";\nexport { ContactsScreen, contactsScreenPropertySchema } from \"./ContactsScreen\";\nexport { OrdersScreen, ordersScreenPropertySchema } from \"./OrdersScreen\";\nexport {\n SubscriptionsScreen,\n subscriptionsScreenPropertySchema,\n} from \"./SubscriptionsScreen\";\nexport {\n CustomersScreen,\n customersScreenPropertySchema,\n} from \"./CustomersScreen\";\nexport { MySiteScreen, mySiteScreenPropertySchema } from \"./MySiteScreen\";\nexport {\n ShareablesScreen,\n shareablesScreenPropertySchema,\n} from \"./ShareablesScreen\";\nexport { ShopScreen, shopScreenPropertySchema } from \"./ShopScreen\";\nexport {\n AppDownloadScreen,\n appDownloadScreenPropertySchema,\n} from \"./AppDownloadScreen\";\n\n// Re-export all property schemas as a lazy-loadable collection\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\n\nexport const screenPropertySchemas: Record<\n string,\n () => Promise<WidgetPropertySchema>\n> = {\n ProfileScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ProfileScreen\").then((m) => m.profileScreenPropertySchema),\n MessagingScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MessagingScreen\").then((m) => m.messagingScreenPropertySchema),\n ContactsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ContactsScreen\").then((m) => m.contactsScreenPropertySchema),\n OrdersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./OrdersScreen\").then((m) => m.ordersScreenPropertySchema),\n SubscriptionsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./SubscriptionsScreen\").then(\n (m) => m.subscriptionsScreenPropertySchema,\n ),\n CustomersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./CustomersScreen\").then((m) => m.customersScreenPropertySchema),\n MySiteScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MySiteScreen\").then((m) => m.mySiteScreenPropertySchema),\n ShareablesScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShareablesScreen\").then((m) => m.shareablesScreenPropertySchema),\n ShopScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShopScreen\").then((m) => m.shopScreenPropertySchema),\n AppDownloadScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./AppDownloadScreen\").then(\n (m) => m.appDownloadScreenPropertySchema,\n ),\n};\n\n// Page Template Registration\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\nimport { CORE_PAGE_IDS } from \"./core-page-ids\";\n\nexport { CORE_PAGE_IDS } from \"./core-page-ids\";\n\n/**\n * Register core page templates.\n * These are automatically registered when the SDK is imported.\n */\nfunction registerCorePageTemplates(): void {\n // Profile Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.PROFILE,\n slug: \"profile\",\n name: \"Profile\",\n description: \"User profile management\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"profile\", \"account\", \"user\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ProfileScreen\",\n id: \"profile-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Messaging Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MESSAGING,\n slug: \"messaging\",\n name: \"Messaging\",\n description: \"Messaging interface provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.COMMUNICATION,\n tags: [\"messaging\", \"chat\", \"conversations\", \"communication\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MessagingScreen\",\n id: \"messaging-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Contacts Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CONTACTS,\n slug: \"contacts\",\n name: \"Contacts\",\n description: \"Contact management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"contacts\", \"people\", \"address-book\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ContactsScreen\",\n id: \"contacts-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Orders Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.ORDERS,\n slug: \"orders\",\n name: \"Orders\",\n description: \"Order management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"orders\", \"commerce\", \"sales\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"OrdersScreen\",\n id: \"orders-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Subscriptions Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SUBSCRIPTIONS,\n slug: \"subscriptions\",\n name: \"Subscriptions\",\n description: \"Subscription management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"subscriptions\", \"recurring\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"SubscriptionsScreen\",\n id: \"subscriptions-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Customers Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CUSTOMERS,\n slug: \"customers\",\n name: \"Customers\",\n description: \"Customer management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"customers\", \"people\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"CustomersScreen\",\n id: \"customers-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shop Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHOP,\n slug: \"shop\",\n name: \"Shop\",\n description: \"Product shop with catalog browsing and product details\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shop\", \"store\", \"products\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShopScreen\",\n id: \"shop-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // MySite Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MY_SITE,\n slug: \"my-site\",\n name: \"MySite\",\n description: \"Personal site management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"my-site\", \"website\", \"portal\", \"personal\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MySiteScreen\",\n id: \"my-site-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // App Download Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.APP_DOWNLOAD,\n slug: \"app-download\",\n name: \"App Download\",\n description: \"Mobile app download page with store links and QR code\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"app-download\", \"mobile\", \"qr-code\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"AppDownloadScreen\",\n id: \"app-download-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shareables Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHAREABLES,\n slug: \"share\",\n name: \"Shareables\",\n description:\n \"Marketing assets and shareable content provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shareables\", \"marketing\", \"assets\", \"media\", \"products\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShareablesScreen\",\n id: \"shareables-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n}\n\n// Auto-register core templates on module load\nregisterCorePageTemplates();\n","import { useState, useMemo } from \"react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { LayoutGrid } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { getSystemNavigationBySection } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport {\n translateNavLabel,\n translateSectionLabel,\n} from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { filterRepOnlySections } from \"../navigation/filter-nav-items\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { deniesRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\n\nexport interface QuickLinksDropdownProps {\n onNavigate: (slug: string) => void;\n}\n\nexport function QuickLinksDropdown({\n onNavigate,\n}: QuickLinksDropdownProps): React.JSX.Element {\n const [open, setOpen] = useState(false);\n const shellApi = useShellTranslation();\n const { t } = shellApi;\n\n const { data: account } = useAccount();\n const canRep = !deniesRepSurfaces(account);\n\n const sections = useMemo(() => {\n const all = getSystemNavigationBySection();\n return canRep ? all : filterRepOnlySections(all);\n }, [canRep]);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shrink-0\"\n aria-label={t(\"nav_quick_links\")}\n >\n <LayoutGrid className=\"size-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className=\"w-175 max-w-[90vw] overflow-hidden rounded-lg p-0 shadow-lg\"\n >\n <div className=\"flex flex-col\">\n {/* Header */}\n <div className=\"bg-background flex items-center gap-2 border-b p-4\">\n <LayoutGrid className=\"text-muted-foreground size-5\" />\n <h3 className=\"text-foreground text-sm font-semibold\">\n {t(\"nav_shortcuts\")}\n </h3>\n </div>\n\n {/* Sections */}\n <div className=\"bg-muted space-y-6 p-6\">\n {Object.entries(sections).map(([sectionName, items]) => (\n <div key={sectionName} className=\"space-y-3\">\n <h3 className=\"text-foreground text-sm font-semibold\">\n {translateSectionLabel(shellApi, sectionName)}\n </h3>\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => (\n <Button\n key={item.slug}\n variant=\"ghost\"\n className=\"text-sidebar-foreground hover:bg-sidebar-primary hover:text-sidebar-primary-foreground h-auto justify-start gap-2 rounded-lg px-3 py-2 text-sm\"\n onClick={() => {\n if (item.slug) onNavigate(item.slug);\n setOpen(false);\n }}\n >\n {item.icon && (\n <RepIcon name={item.icon} className=\"size-4 shrink-0\" />\n )}\n <span className=\"truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n </Button>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n","import React, {\n forwardRef,\n type AnchorHTMLAttributes,\n type MouseEvent,\n} from \"react\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface AppLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /** Slug to navigate to (e.g. \"contacts/123\") */\n to: string;\n}\n\n/**\n * SPA-aware link that renders a real `<a href>` for accessibility\n * (right-click, ctrl+click, screen readers) but intercepts normal\n * clicks for client-side navigation.\n */\nexport const AppLink: React.ForwardRefExoticComponent<\n AppLinkProps & React.RefAttributes<HTMLAnchorElement>\n> = forwardRef<HTMLAnchorElement, AppLinkProps>(function AppLink(\n { to, onClick, children, ...rest },\n ref,\n) {\n const { navigate, buildHref } = useAppNavigation();\n\n const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {\n // Allow custom onClick handlers to run first\n onClick?.(e);\n if (e.defaultPrevented) return;\n\n // Skip SPA navigation for modifier keys, non-primary button, or external target\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n if (e.button !== 0) return;\n if (rest.target && rest.target !== \"_self\") return;\n\n e.preventDefault();\n navigate(to);\n };\n\n return (\n <a ref={ref} href={buildHref(to)} onClick={handleClick} {...rest}>\n {children}\n </a>\n );\n});\n","/**\n * Scaffold Manifest\n *\n * Tracks which files belong to the portal scaffold and their category.\n * Used by `fluid doctor` to detect drift between a scaffolded portal\n * and the canonical templates shipped with the SDK.\n *\n * Categories:\n * - \"entry\" — App bootstrap files (main.tsx, index.css)\n * - \"config\" — Developer-owned config (portal.config.ts, navigation.config.ts)\n * - \"infrastructure\" — Build/tooling config (vite, tsconfig, index.html, linting)\n */\n\nexport type FileCategory = \"entry\" | \"config\" | \"infrastructure\";\n\nexport interface ManifestEntry {\n readonly category: FileCategory;\n}\n\n/**\n * Canonical scaffold file manifest.\n *\n * Files marked as \"entry\" or \"infrastructure\" are checked by `fluid doctor`.\n * Files marked as \"config\" are developer-owned and intentionally excluded from drift checks.\n */\nexport const SCAFFOLD_MANIFEST = {\n version: 2,\n files: {\n \"src/main.tsx\": { category: \"entry\" },\n \"src/index.css\": { category: \"entry\" },\n \"index.html\": { category: \"infrastructure\" },\n \"tsconfig.json\": { category: \"infrastructure\" },\n \"vite.config.ts\": { category: \"infrastructure\" },\n \".oxlintrc.json\": { category: \"infrastructure\" },\n },\n /** Developer-owned files — never checked for drift */\n developerOwned: [\n \"src/portal.config.ts\",\n \"src/navigation.config.ts\",\n \"src/screens/*\",\n ],\n} as const;\n\nexport type ScaffoldFile = keyof typeof SCAFFOLD_MANIFEST.files;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["useBottomNavHidden","SidebarProvider","Sidebar","SidebarHeader","SidebarContent","SidebarFooter","SidebarInset","SYSTEM_NAVIGATION_ITEMS","ContactRound","Repeat","Bell","Box","Boxes","Briefcase","Calendar","ShoppingCart","CircleUser","MessageCircle","Compass","Mail","File","FileText","FileVideo","Folder","FolderOpen","Settings","Globe","GraduationCap","Heart","House","Image","ListMusic","Search","MessageSquare","PackageOpen","Star","Store","LayoutGrid","User","UserCog","Users","normalizeSlug","shellEn","useSidebar","useShellTranslation","useNativeCapabilities","useBottomNavHidden","React","Ellipsis","MobileBottomSheet","useShellTranslation","useShellTranslation","Collapsible","SidebarMenuItem","CollapsibleTrigger","SidebarMenuButton","ChevronRight","CollapsibleContent","SidebarMenuSub","normalizeSlug","useShellTranslation","SidebarMenu","SidebarGroupLabel","normalizeSlug","SidebarMenuItem","SidebarMenuButton","useActiveLocale","useLanguagesApi","storeKeys","SUPPORTED_LANGUAGES","useSidebar","useRepUser","useShellTranslation","MobileBottomSheet","ChevronLeft","Check","Smartphone","Globe","ChevronRight","Moon","Sun","LogOut","useSidebar","useShellTranslation","normalizeSlug","IconButton","ChevronLeft","Menu","useActiveLocale","useShellTranslation","useLanguagesApi","storeKeys","SUPPORTED_LANGUAGES","Popover","PopoverTrigger","Button","Globe","PopoverContent","Check","Building2","cn","Skeleton","useStore","useShellTranslation","FallbackCard","Compass","Button","useShellTranslation","alertWidgetPropertySchema","bulletListWidgetPropertySchema","calendarWidgetPropertySchema","cardWidgetPropertySchema","carouselWidgetPropertySchema","chartWidgetPropertySchema","containerWidgetPropertySchema","embedWidgetPropertySchema","imageWidgetPropertySchema","layoutWidgetPropertySchema","linkWidgetPropertySchema","listWidgetPropertySchema","mySiteWidgetPropertySchema","nestedWidgetPropertySchema","pointsWidgetPropertySchema","quickLinksWidgetPropertySchema","quickShareWidgetPropertySchema","recentActivityWidgetPropertySchema","separatorWidgetPropertySchema","shopWidgetPropertySchema","spacerWidgetPropertySchema","tableWidgetPropertySchema","textWidgetPropertySchema","toDoWidgetPropertySchema","videoWidgetPropertySchema","useRegistry","isDefaultWidgetComponent","DataAwareWidget","useShellTranslation","ProfileScreen","OrdersScreen","SubscriptionsScreen","MessagingScreen","ContactsScreen","ShopScreen","CustomersScreen","ShareablesScreen","MySiteScreen","useAccount","deniesRepSurfaces","slugifyName","isRepOnlySlug","Suspense","isSystemNavigationItem","CoreScreenPlaceholder","ShellTranslationContext","shellEn","FallbackCard","TriangleAlert","Button","Component","deleteDatabase","useShellTranslation","Moon","Sun","SidebarMenu","SidebarMenuItem","SidebarMenuButton","LogOut","isRepOnlySlug","useSidebar","useScreenHeaderSlotRefs","PanelLeft","useShellTranslation","useOptionalScreenAreaFullscreenCoordinator","useScreenAreaFullscreenSnapshot","matchSlugPrefix","normalizeSlug","normalizeSlug","useFluidApp","useAccount","getDefaultThemeDefinition","resolveTheme","deniesRepSurfaces","collectNavSlugs","extractSlugFromPathname","matchSlugPrefix","ScreenHeaderProvider","RepUserProvider","AppNavigationProvider","WidgetInteractionProvider","BottomNavVisibilityProvider","ScreenAreaFullscreenProvider","Toaster","Component","FluidProvider","StrictMode","useAppDefinitionApi","useCompanyScopedQueryKey","APP_DATA_QUERY_KEY","fetchRawManifest","transformManifestToRepAppData","useAppDefinitionApi","useCompanyScopedQueryKey","useThemeContext","useAccount","hasMemberPermission","memberTypeSlug","memberTypeName","canAccessRepSurfaces","usePortalTenantClient","deleteDatabase","now","daysFromNow","now","now","hoursAgo","now","daysAgo","z","useShellTranslation","useAccount","deniesRepSurfaces","getSystemNavigationBySection","Popover","PopoverTrigger","Button","LayoutGrid","PopoverContent","useAppNavigation"],"sources":["../src/types/page-template.ts","../src/registries/page-template-registry.ts","../src/core/resolve-pages.ts","../src/providers/PageTemplateProvider.tsx","../../react/src/shell/use-suppress-initial-iframe-focus-scroll.ts","../../react/src/shell/AppShellLayout.tsx","../../react/src/shell/ThemeModeContext.tsx","../../core/src/navigation/default-navigation.ts","../../react/src/components/RepIcon.tsx","../../core/src/navigation/navigation-storage.ts","../../core/src/navigation/navigation-utils.ts","../../core/src/navigation/translate-nav-label.ts","../src/shell/SdkBottomNav.tsx","../src/shell/AppShellLoading.tsx","../src/shell/CollapsibleNavItem.tsx","../src/shell/SdkNavigation.tsx","../src/shell/use-mobile-languages.ts","../src/shell/SdkMobileProfileMenu.tsx","../src/shell/SdkHeader.tsx","../src/shell/LanguageSelector.tsx","../../../company-switcher/ui/src/components/CompanyImage.tsx","../../../company-switcher/ui/src/components/CompanyItem.tsx","../../../company-switcher/ui/src/components/CompanySwitcherSkeleton.tsx","../src/shell/SdkCompanySwitcher.tsx","../src/shell/go-back-or-home.ts","../src/screens/ScreenNotFound.tsx","../src/screens/AccessDeniedScreen.tsx","../src/core/default-property-schema-registry.ts","../src/utils/warn-unknown-widget-props.ts","../src/shell/BuilderScreenView.tsx","../../core/src/member-type-content.ts","../src/shell/system-screen-map.ts","../src/shell/PageRouter.tsx","../src/shell/AppShellErrorBoundary.tsx","../src/shell/native-theme-bridge.ts","../src/utils/reset-fairshare-sdk.ts","../src/hooks/use-logout.ts","../src/shell/SdkLogoutButton.tsx","../src/navigation/filter-nav-items.ts","../../react/src/shell/ScreenHeader.tsx","../src/shell/TranslatedScreenHeader.tsx","../src/shell/mobile-back-target.ts","../src/shell/AppShell.tsx","../src/config/env.ts","../src/config/defaults.ts","../src/entry/create-portal.tsx","../src/hooks/use-fluid-profile.ts","../src/hooks/use-app-definition.ts","../src/hooks/use-fluid-theme.ts","../src/hooks/use-member-type.ts","../src/hooks/use-company-switch.ts","../src/hooks/demo-data/calendar-events.ts","../src/hooks/use-calendar-events.ts","../src/hooks/demo-data/todos.ts","../src/hooks/use-todos.ts","../src/hooks/hook-types.ts","../src/hooks/demo-data/activities.ts","../src/hooks/use-activities.ts","../src/hooks/demo-data/catchups.ts","../src/hooks/use-catchups.ts","../src/hooks/demo-data/mysite.ts","../src/hooks/use-mysite.ts","../src/hooks/demo-data/conversations.ts","../src/hooks/use-conversations.ts","../src/types/screen-types.ts","../src/hooks/demo-data/contacts.ts","../src/hooks/use-contacts.ts","../../../platform/auth/src/types.ts","../../../platform/auth/src/company-switch.ts","../src/screens/core-page-ids.ts","../src/screens/index.ts","../src/shell/QuickLinksDropdown.tsx","../src/shell/AppLink.tsx","../src/scaffold/manifest.ts"],"sourcesContent":["import type { WidgetSchema } from \"./widget-schema\";\n\n/**\n * Category for organizing page templates in the registry\n */\nexport interface PageCategory {\n /** Unique identifier for the category */\n readonly id: string;\n /** Display label */\n label: string;\n /** Icon identifier (e.g., lucide icon name) */\n icon?: string;\n}\n\n/**\n * A reusable page template that can be shared across multiple navigations\n */\nexport interface PageTemplate {\n /** Unique identifier for the template */\n readonly id: string;\n /** URL-friendly slug */\n slug: string;\n /** Display name */\n name: string;\n /** Description of the template's purpose */\n description?: string;\n /** Category ID for organization */\n category: string;\n /** Tags for filtering and search */\n tags?: readonly string[];\n /** The widget tree that defines the page content */\n component_tree: readonly WidgetSchema[];\n /** Semantic version of the template */\n version: string;\n /** Whether this is a core feature that cannot be removed */\n isCore?: boolean;\n /** Default prop values that can be customized */\n defaultProps?: Readonly<Record<string, unknown>>;\n /** Thumbnail image URL for UI display */\n thumbnail?: string;\n}\n\n/**\n * Reference to a shared page template within a navigation\n */\nexport interface PageReference {\n /** ID of the page template being referenced */\n page_template_id: string;\n /** Screen ID to assign to this page in the navigation */\n screen_id: number;\n /** Optional prop overrides (only prop values, not widget structure) */\n overrides?: readonly PageOverride[];\n}\n\n/**\n * Override for a specific widget's props within a page template\n */\nexport interface PageOverride {\n /** ID of the widget to override (must match WidgetSchema.id in the template) */\n readonly widget_id: string;\n /** Props to override (merged with original props) */\n props: Readonly<Record<string, unknown>>;\n}\n\n/**\n * Built-in page category IDs\n */\nexport const PAGE_CATEGORIES = {\n CORE: \"core\",\n COMMERCE: \"commerce\",\n COMMUNICATION: \"communication\",\n DATA: \"data\",\n CUSTOM: \"custom\",\n} as const;\n\nexport type PageCategoryId =\n (typeof PAGE_CATEGORIES)[keyof typeof PAGE_CATEGORIES];\n","import type {\n PageTemplate,\n PageCategory,\n PageCategoryId,\n} from \"../types/page-template\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\n\n/**\n * Registry for managing reusable page templates.\n *\n * The registry provides a central store for page templates that can be\n * shared across multiple navigations. Core pages (like Messaging, Contacts)\n * are pre-registered and cannot be removed.\n *\n * @example\n * ```ts\n * // Register a custom page template\n * PageTemplateRegistry.register({\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Custom Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Hello' } }],\n * });\n *\n * // Get a template by ID\n * const template = PageTemplateRegistry.get('custom-dashboard');\n *\n * // List all templates in a category\n * const corePages = PageTemplateRegistry.getByCategory('core');\n * ```\n */\nclass PageTemplateRegistryImpl {\n private templates = new Map<string, PageTemplate>();\n private categories: PageCategory[] = [];\n\n constructor() {\n // Initialize default categories\n this.categories = [\n { id: PAGE_CATEGORIES.CORE, label: \"Core Features\", icon: \"star\" },\n {\n id: PAGE_CATEGORIES.COMMERCE,\n label: \"Commerce\",\n icon: \"shopping-cart\",\n },\n {\n id: PAGE_CATEGORIES.COMMUNICATION,\n label: \"Communication\",\n icon: \"message-circle\",\n },\n {\n id: PAGE_CATEGORIES.DATA,\n label: \"Data & Analytics\",\n icon: \"bar-chart\",\n },\n { id: PAGE_CATEGORIES.CUSTOM, label: \"Custom\", icon: \"puzzle\" },\n ];\n }\n\n /**\n * Register a new page template.\n * @throws Error if a template with the same ID already exists\n */\n register(template: PageTemplate): void {\n if (this.templates.has(template.id)) {\n throw new Error(\n `Page template with ID \"${template.id}\" is already registered`,\n );\n }\n this.templates.set(template.id, template);\n }\n\n /**\n * Unregister a page template by ID.\n * Core templates cannot be unregistered.\n * @returns true if the template was removed, false if it didn't exist or is a core template\n */\n unregister(id: string): boolean {\n const template = this.templates.get(id);\n if (!template) {\n return false;\n }\n if (template.isCore) {\n console.warn(\n `Cannot unregister core page template \"${id}\". Core templates are required.`,\n );\n return false;\n }\n return this.templates.delete(id);\n }\n\n /**\n * Get a page template by ID.\n */\n get(id: string): PageTemplate | undefined {\n return this.templates.get(id);\n }\n\n /**\n * Get all page templates in a specific category.\n */\n getByCategory(category: PageCategoryId | string): PageTemplate[] {\n return Array.from(this.templates.values()).filter(\n (t) => t.category === category,\n );\n }\n\n /**\n * List all registered page templates.\n */\n listAll(): PageTemplate[] {\n return Array.from(this.templates.values());\n }\n\n /**\n * List all core page templates (isCore: true).\n */\n listCore(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => t.isCore);\n }\n\n /**\n * List all non-core page templates.\n */\n listOptional(): PageTemplate[] {\n return Array.from(this.templates.values()).filter((t) => !t.isCore);\n }\n\n /**\n * List all registered categories.\n */\n listCategories(): PageCategory[] {\n return [...this.categories];\n }\n\n /**\n * Add a custom category.\n */\n addCategory(category: PageCategory): void {\n if (this.categories.some((c) => c.id === category.id)) {\n console.warn(`Category with ID \"${category.id}\" already exists`);\n return;\n }\n this.categories.push(category);\n }\n\n /**\n * Check if a template exists by ID.\n */\n has(id: string): boolean {\n return this.templates.has(id);\n }\n\n /**\n * Get the count of registered templates.\n */\n get size(): number {\n return this.templates.size;\n }\n\n /**\n * Clear all non-core templates.\n * Useful for testing or resetting the registry.\n */\n clearNonCore(): void {\n for (const [id, template] of this.templates) {\n if (!template.isCore) {\n this.templates.delete(id);\n }\n }\n }\n}\n\n/**\n * Global page template registry singleton.\n *\n * This registry is automatically populated with core page templates\n * (Messaging, Contacts) when the SDK is imported.\n */\nexport const PageTemplateRegistry: PageTemplateRegistryImpl =\n new PageTemplateRegistryImpl();\n","import type { Navigation, ScreenDefinition, WidgetSchema } from \"../types\";\nimport type {\n PageReference,\n PageOverride,\n PageTemplate,\n} from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\n\n/**\n * Apply prop overrides to widgets in a component tree.\n * Recursively walks widget.props.children so overrides targeting\n * nested widgets (inside LayoutWidget, ContainerWidget, etc.) are applied.\n *\n * @param componentTree - The original component tree from the page template\n * @param overrides - Array of widget-specific prop overrides\n * @returns A new component tree with overrides applied\n */\nfunction applyOverrides(\n componentTree: readonly WidgetSchema[],\n overrides: readonly PageOverride[],\n): WidgetSchema[] {\n if (!overrides.length) {\n // Return a mutable copy to satisfy ScreenDefinition.component_tree type\n return [...componentTree];\n }\n\n // Build a map of widget_id -> override for O(1) lookup (built once, shared across recursion)\n const overrideMap = new Map(overrides.map((o) => [o.widget_id, o.props]));\n\n return componentTree.map((widget) =>\n applyWidgetOverrides(widget, overrideMap),\n );\n}\n\n/**\n * Recursively apply overrides to a widget and its children.\n * Only clones widgets that actually change (have an override or contain children that might).\n */\nfunction applyWidgetOverrides(\n widget: WidgetSchema,\n overrideMap: ReadonlyMap<string, Record<string, unknown>>,\n): WidgetSchema {\n const override = widget.id ? overrideMap.get(widget.id) : undefined;\n const children = widget.props.children;\n\n // Recurse into children if they exist (LayoutWidget, ContainerWidget)\n const hasChildren = Array.isArray(children) && children.length > 0;\n\n if (!override && !hasChildren) {\n return widget;\n }\n\n const newProps = override\n ? { ...widget.props, ...override }\n : { ...widget.props };\n\n if (hasChildren) {\n newProps.children = (children as readonly WidgetSchema[]).map((child) =>\n applyWidgetOverrides(child, overrideMap),\n );\n }\n\n return { ...widget, props: newProps };\n}\n\n/**\n * Resolve a page reference to a screen definition.\n *\n * @param ref - The page reference from navigation\n * @returns A ScreenDefinition or undefined if the template doesn't exist\n */\nfunction resolvePageReference(\n ref: Readonly<PageReference>,\n): ScreenDefinition | undefined {\n const template = PageTemplateRegistry.get(ref.page_template_id);\n if (!template) {\n console.warn(\n `Page template \"${ref.page_template_id}\" not found in registry`,\n );\n return undefined;\n }\n\n // Apply any overrides to the component tree\n // Spread to create mutable array for ScreenDefinition.component_tree\n const componentTree = ref.overrides\n ? applyOverrides(template.component_tree, ref.overrides)\n : [...template.component_tree];\n\n return {\n id: ref.screen_id,\n slug: template.slug,\n name: template.name,\n component_tree: componentTree,\n };\n}\n\n/**\n * Resolve all page references and local screens into a unified screen list.\n *\n * This function merges:\n * 1. Screen definitions from page_refs (shared templates)\n * 2. Local screen definitions (for backwards compatibility and custom screens)\n *\n * When a screen_id appears in both page_refs and screens, the local screen\n * takes precedence (allows local overrides of template pages).\n *\n * @param navigation - The navigation configuration\n * @returns A unified array of ScreenDefinition objects\n *\n * @example\n * ```ts\n * const navigation: Navigation = {\n * definition_id: 1,\n * id: 1,\n * name: \"Main Nav\",\n * navigation_items: [...],\n * screens: [\n * // Local custom screen\n * { id: 1, slug: \"home\", name: \"Home\", component_tree: [...] }\n * ],\n * page_refs: [\n * // Reference to shared messaging template\n * { page_template_id: \"core-messaging\", screen_id: 2 }\n * ],\n * };\n *\n * const allScreens = resolveNavigationPages(navigation);\n * // Returns: [home screen, messaging screen from template]\n * ```\n */\nexport function resolveNavigationPages(\n navigation: Readonly<Navigation>,\n): ScreenDefinition[] {\n // Start with local screens (these take precedence)\n const localScreenIds = new Set(navigation.screens.map((s) => s.id));\n const result: ScreenDefinition[] = [...navigation.screens];\n\n // Resolve page references (skip if local screen already exists with same ID)\n if (navigation.page_refs) {\n for (const ref of navigation.page_refs) {\n if (localScreenIds.has(ref.screen_id)) {\n // Local screen takes precedence\n continue;\n }\n\n const screen = resolvePageReference(ref);\n if (screen) {\n result.push(screen);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Get all available page templates for use in navigation.\n *\n * @returns Array of page templates from the registry\n */\nexport function getAvailablePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listAll();\n}\n\n/**\n * Get core page templates that are required for basic functionality.\n *\n * @returns Array of core page templates\n */\nexport function getCorePageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listCore();\n}\n\n/**\n * Get optional page templates that can be added to navigation.\n *\n * @returns Array of optional (non-core) page templates\n */\nexport function getOptionalPageTemplates(): PageTemplate[] {\n return PageTemplateRegistry.listOptional();\n}\n\n/**\n * Check if a navigation has all required core pages.\n *\n * @param navigation - The navigation to check\n * @returns Object with validation result and missing page IDs\n */\nexport function validateNavigationPages(navigation: Readonly<Navigation>): {\n readonly valid: boolean;\n readonly missingCorePages: readonly string[];\n} {\n const corePages = PageTemplateRegistry.listCore();\n const referencedTemplateIds = new Set(\n navigation.page_refs?.map((ref) => ref.page_template_id) ?? [],\n );\n\n // Check which core pages are referenced\n const missingCorePages = corePages\n .filter((page) => !referencedTemplateIds.has(page.id))\n .map((page) => page.id);\n\n return {\n valid: missingCorePages.length === 0,\n missingCorePages,\n };\n}\n","import React, {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport type { PageTemplate } from \"../types/page-template\";\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { resolveNavigationPages } from \"../core/resolve-pages\";\nimport type { Navigation, ScreenDefinition } from \"../types\";\n\n/** Stable empty array for the default `templates` prop (avoids new reference each render) */\nconst EMPTY_TEMPLATES: readonly PageTemplate[] = [];\n\n/**\n * Context value for page template resolution.\n * All properties are readonly since context values should not be mutated by consumers.\n */\ninterface PageTemplateContextValue {\n /**\n * Resolve a navigation's page_refs and screens into a unified screen list\n */\n readonly resolvePages: (navigation: Navigation) => ScreenDefinition[];\n /**\n * Get all available page templates\n */\n readonly listTemplates: () => PageTemplate[];\n /**\n * Get a specific template by ID\n */\n readonly getTemplate: (id: string) => PageTemplate | undefined;\n /**\n * Check if a template exists\n */\n readonly hasTemplate: (id: string) => boolean;\n}\n\nconst PageTemplateContext = createContext<PageTemplateContextValue | null>(\n null,\n);\n\n/**\n * Props for PageTemplateProvider\n */\ninterface PageTemplateProviderProps {\n children: React.ReactNode;\n /**\n * Additional custom page templates to register.\n * These are registered when the provider mounts and unregistered when it unmounts.\n */\n templates?: readonly PageTemplate[];\n}\n\n/**\n * Provider for page template resolution.\n *\n * This provider:\n * 1. Registers any custom templates passed via props\n * 2. Provides methods for resolving navigation pages\n * 3. Cleans up custom templates on unmount\n *\n * @example\n * ```tsx\n * // With custom templates\n * const customTemplates: PageTemplate[] = [\n * {\n * id: 'custom-dashboard',\n * slug: 'dashboard',\n * name: 'Dashboard',\n * category: 'custom',\n * version: '1.0.0',\n * component_tree: [{ type: 'TextWidget', props: { text: 'Custom Dashboard' } }],\n * },\n * ];\n *\n * <PageTemplateProvider templates={customTemplates}>\n * <App />\n * </PageTemplateProvider>\n *\n * // Without custom templates (uses only core templates)\n * <PageTemplateProvider>\n * <App />\n * </PageTemplateProvider>\n * ```\n */\nexport function PageTemplateProvider({\n children,\n templates = EMPTY_TEMPLATES,\n}: PageTemplateProviderProps): React.JSX.Element {\n // Track which templates we registered so we can clean them up\n const registeredIds = useRef<string[]>([]);\n\n // Derive a stable primitive key from template IDs\n // (rerender-dependencies rule: use primitive dependencies in effects)\n const templateKey = templates.map((t) => t.id).join(\",\");\n\n // Register custom templates when template IDs change\n useEffect(() => {\n const registered: string[] = [];\n\n for (const template of templates) {\n if (!PageTemplateRegistry.has(template.id)) {\n try {\n PageTemplateRegistry.register(template);\n registered.push(template.id);\n } catch (error) {\n console.warn(\n `Failed to register page template \"${template.id}\":`,\n error,\n );\n }\n }\n }\n\n registeredIds.current = registered;\n\n // Cleanup when template IDs change or on unmount\n return () => {\n for (const id of registeredIds.current) {\n PageTemplateRegistry.unregister(id);\n }\n registeredIds.current = [];\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [templateKey]);\n\n // Memoize context value to prevent unnecessary re-renders\n // Use `satisfies` to validate shape at compile time while preserving inference\n const contextValue = useMemo(\n () =>\n ({\n resolvePages: resolveNavigationPages,\n listTemplates: () => PageTemplateRegistry.listAll(),\n getTemplate: (id: string) => PageTemplateRegistry.get(id),\n hasTemplate: (id: string) => PageTemplateRegistry.has(id),\n }) satisfies PageTemplateContextValue,\n [],\n );\n\n return (\n <PageTemplateContext.Provider value={contextValue}>\n {children}\n </PageTemplateContext.Provider>\n );\n}\n\n/**\n * Hook to access page template functionality.\n *\n * @throws Error if used outside of PageTemplateProvider\n *\n * @example\n * ```tsx\n * function NavigationRenderer({ navigation }: { navigation: Navigation }) {\n * const { resolvePages } = usePageTemplates();\n * const screens = resolvePages(navigation);\n *\n * return (\n * <div>\n * {screens.map((screen) => (\n * <Screen key={screen.id} definition={screen} />\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePageTemplates(): PageTemplateContextValue {\n const context = useContext(PageTemplateContext);\n if (!context) {\n throw new Error(\n \"usePageTemplates must be used within a PageTemplateProvider\",\n );\n }\n return context;\n}\n\n/**\n * Hook to resolve navigation pages directly.\n * Convenience wrapper around usePageTemplates().resolvePages.\n *\n * @param navigation - The navigation to resolve\n * @returns Array of resolved screen definitions\n */\nexport function useResolvedPages(navigation: Navigation): ScreenDefinition[] {\n const { resolvePages } = usePageTemplates();\n return useMemo(() => resolvePages(navigation), [resolvePages, navigation]);\n}\n","import { useEffect, type RefObject } from \"react\";\n\n/**\n * Iframes embed opaque third-party content that can call `.focus()` on its\n * internal elements during load — video players, calendar pickers, form\n * inputs, search boxes. The browser then scrolls the nearest scrolling\n * ancestor to bring the focused iframe into view, yanking the viewport to a\n * widget the user didn't ask for.\n *\n * Suppress that scroll on the given scroll container until the user has\n * explicitly interacted with the page; afterwards, focus-scrolls are\n * treated as user-initiated and left alone. The container's `scrollTop` at\n * effect setup is captured and restored, so any pre-effect scroll position\n * (e.g. from future hash-anchor restoration) is preserved.\n */\nexport function useSuppressInitialIframeFocusScroll(\n ref: RefObject<HTMLElement | null>,\n): void {\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n const initialScrollTop = el.scrollTop;\n let userInteracted = false;\n\n const handleFocusIn = (event: FocusEvent) => {\n if (userInteracted) return;\n if (!(event.target instanceof HTMLIFrameElement)) return;\n requestAnimationFrame(() => {\n if (!userInteracted) el.scrollTop = initialScrollTop;\n });\n };\n\n const markInteracted = () => {\n userInteracted = true;\n };\n\n el.addEventListener(\"focusin\", handleFocusIn);\n window.addEventListener(\"pointerdown\", markInteracted, { once: true });\n window.addEventListener(\"keydown\", markInteracted, { once: true });\n window.addEventListener(\"wheel\", markInteracted, {\n once: true,\n passive: true,\n });\n\n return () => {\n el.removeEventListener(\"focusin\", handleFocusIn);\n window.removeEventListener(\"pointerdown\", markInteracted);\n window.removeEventListener(\"keydown\", markInteracted);\n window.removeEventListener(\"wheel\", markInteracted);\n };\n }, [ref]);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useLayoutEffect, useRef } from \"react\";\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarInset,\n SidebarProvider,\n} from \"./sidebar\";\nimport { useSuppressInitialIframeFocusScroll } from \"./use-suppress-initial-iframe-focus-scroll\";\nimport { useBottomNavHidden } from \"./BottomNavVisibilityContext\";\n\nexport interface AppShellLayoutProps {\n /** Navigation content rendered inside the sidebar's scrollable area */\n sidebarContent: React.ReactNode;\n /** Header content rendered above the main content area */\n headerContent: React.ReactNode;\n /** Main page content */\n children: React.ReactNode;\n /** Optional slot at the top of the sidebar (e.g. logo, search) */\n sidebarHeader?: React.ReactNode;\n /** Optional slot at the bottom of the sidebar (e.g. user info) */\n sidebarFooter?: React.ReactNode;\n /** Content rendered after SidebarInset (e.g. MobileBottomNav) */\n afterContent?: React.ReactNode;\n /** Enable bottom nav mode (hides sidebar on mobile, adds bottom padding) */\n useBottomNav?: boolean;\n}\n\n/**\n * Pure visual frame that replicates the RepApp layout:\n * - 13rem collapsible sidebar\n * - 52px header area\n * - rounded-xl bg-background shadow-lg content area with bg-muted gutters\n *\n * This component handles zero business logic — it simply provides the visual shell.\n */\nexport function AppShellLayout({\n sidebarContent,\n headerContent,\n children,\n sidebarHeader,\n sidebarFooter,\n afterContent,\n useBottomNav = false,\n}: AppShellLayoutProps): React.JSX.Element {\n const screenContentRef = useRef<HTMLDivElement>(null);\n const bottomNavHidden = useBottomNavHidden();\n\n // Reserve space for the fixed bottom nav on mobile. When a drill-down surface\n // hides the nav (e.g. an open conversation), drop the padding entirely so the\n // now full-screen surface owns its bottom edge — including the safe-area\n // inset (e.g. the messaging composer already pads for the home indicator).\n // Padding it here too would double the inset. Animated to match the nav's\n // slide, so content and chrome move together.\n const mobileBottomPadding = useBottomNav\n ? bottomNavHidden\n ? \"max-md:pb-0\"\n : \"max-md:pb-[calc(3.5rem+env(safe-area-inset-bottom))]\"\n : \"\";\n\n // Publish the screen content slot's measured pixel height as\n // `--portal-fullscreen-embed-height` so descendants (e.g. EmbedWidget in\n // inline + Full Screen mode) can anchor against the visible portal screen\n // area instead of the browser viewport. Mirrors the admin builder's\n // ScreenContentSlot in `packages/portal/builder/src/components/core/RepApp.tsx`.\n useLayoutEffect(() => {\n const el = screenContentRef.current;\n if (!el) return;\n const update = () => {\n el.style.setProperty(\n \"--portal-fullscreen-embed-height\",\n `${el.clientHeight}px`,\n );\n };\n update();\n const obs = new ResizeObserver(update);\n obs.observe(el);\n return () => obs.disconnect();\n }, []);\n\n useSuppressInitialIframeFocusScroll(screenContentRef);\n\n return (\n <SidebarProvider useBottomNav={useBottomNav}>\n <div className=\"bg-muted relative flex max-h-dvh w-full overflow-hidden\">\n {/* Navigation Sidebar */}\n <Sidebar collapsible=\"icon\">\n {sidebarHeader && <SidebarHeader>{sidebarHeader}</SidebarHeader>}\n <SidebarContent className=\"p-4\">{sidebarContent}</SidebarContent>\n {sidebarFooter && <SidebarFooter>{sidebarFooter}</SidebarFooter>}\n </Sidebar>\n\n {/* Main Content Area */}\n <SidebarInset className=\"flex flex-1 flex-col overflow-hidden\">\n {/* Header */}\n {headerContent}\n\n {/* Screen Content */}\n <div\n className={`bg-muted min-h-0 flex-1 transition-[padding] duration-300 ease-out motion-reduce:transition-none md:pt-4 md:pr-4 md:pb-4 md:pl-2 ${mobileBottomPadding}`}\n >\n <div\n ref={screenContentRef}\n data-fluid-portal-screen-area=\"\"\n tabIndex={-1}\n className=\"ring-foreground/5 scrollbar-none bg-background text-foreground h-full overflow-auto rounded-xl shadow-sm ring-1 max-md:rounded-b-none max-md:shadow-none max-md:ring-0\"\n >\n {children}\n </div>\n </div>\n </SidebarInset>\n\n {/* After content (e.g. MobileBottomNav) */}\n {afterContent}\n </div>\n </SidebarProvider>\n );\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport {\n createContext,\n type ReactNode,\n use,\n useCallback,\n useMemo,\n useSyncExternalStore,\n} from \"react\";\n\nexport type ThemeMode = \"auto\" | \"light\" | \"dark\";\n\nexport type DisplayMode = \"light\" | \"dark\";\n\nexport interface ThemeModeContextValue {\n mode: ThemeMode;\n displayMode: DisplayMode;\n setMode: (mode: ThemeMode) => void;\n autoModeEnabled: boolean;\n cycleMode: () => void;\n dataAttribute: string | undefined;\n}\n\nconst ThemeModeContext = createContext<ThemeModeContextValue | null>(null);\n\nconst darkMediaQuery =\n typeof window !== \"undefined\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n\nfunction subscribeToPrefersColorScheme(callback: () => void) {\n darkMediaQuery?.addEventListener(\"change\", callback);\n return () => darkMediaQuery?.removeEventListener(\"change\", callback);\n}\n\nfunction getSystemPrefersDark(): boolean {\n return darkMediaQuery?.matches ?? false;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\ninterface ThemeModeProviderProps {\n children: ReactNode;\n mode: ThemeMode;\n onModeChange: (mode: ThemeMode) => void;\n /** When false, auto mode is skipped in the cycle (light↔dark only). Default true. */\n autoModeEnabled?: boolean;\n}\n\nexport function ThemeModeProvider({\n children,\n mode,\n onModeChange,\n autoModeEnabled = true,\n}: ThemeModeProviderProps): React.JSX.Element {\n const systemPrefersDark = useSyncExternalStore(\n subscribeToPrefersColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\n const systemMode: DisplayMode = systemPrefersDark ? \"dark\" : \"light\";\n\n const displayMode: DisplayMode = mode === \"auto\" ? systemMode : mode;\n\n const cycleMode = useCallback(() => {\n if (autoModeEnabled) {\n // Toggle displayed mode. If target matches system preference, use \"auto\".\n const target: DisplayMode = displayMode === \"light\" ? \"dark\" : \"light\";\n onModeChange(target === systemMode ? \"auto\" : target);\n } else {\n // light ↔ dark\n onModeChange(mode === \"light\" ? \"dark\" : \"light\");\n }\n }, [mode, displayMode, systemMode, onModeChange, autoModeEnabled]);\n\n const dataAttribute = getThemeModeAttribute(mode);\n\n const value = useMemo(\n () => ({\n mode,\n displayMode,\n setMode: onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n }),\n [\n mode,\n displayMode,\n onModeChange,\n autoModeEnabled,\n cycleMode,\n dataAttribute,\n ],\n );\n\n return (\n <ThemeModeContext.Provider value={value}>\n {children}\n </ThemeModeContext.Provider>\n );\n}\n\n/** Access the current theme mode, setter, and cycle helper. Must be used within a `ThemeModeProvider`. */\nexport function useThemeMode(): ThemeModeContextValue {\n const ctx = use(ThemeModeContext);\n if (!ctx) {\n throw new Error(\"useThemeMode must be used within a ThemeModeProvider\");\n }\n return ctx;\n}\n\n/** Maps a ThemeMode to the value for `data-theme-mode`. Returns undefined for \"auto\". */\nexport function getThemeModeAttribute(mode: ThemeMode): string | undefined {\n return mode === \"auto\" ? undefined : mode;\n}\n","import {\n SYSTEM_NAVIGATION_ITEMS,\n type SystemNavigationSlug,\n} from \"./system-navigation-items\";\nimport type { NavigationItem } from \"../types/navigation\";\n\n/**\n * Build a NavigationItem from a system navigation slug.\n * Icons and labels come from SYSTEM_NAVIGATION_ITEMS (single source of truth).\n */\nfunction systemNavItem(slug: SystemNavigationSlug): NavigationItem {\n const item = SYSTEM_NAVIGATION_ITEMS[slug];\n return {\n slug: item.slug,\n label: item.label,\n icon: item.icon,\n children: [],\n };\n}\n\nfunction sectionHeader(label: string): NavigationItem {\n return { label, children: [] };\n}\n\n/**\n * Returns the default navigation items for a portal app when no\n * API data is available. All icons/labels derive from\n * SYSTEM_NAVIGATION_ITEMS so they can never drift.\n */\nexport function getDefaultNavigation(): NavigationItem[] {\n return [\n sectionHeader(\"We Commerce\"),\n systemNavItem(\"shop\"),\n systemNavItem(\"messages\"),\n systemNavItem(\"contacts\"),\n systemNavItem(\"profile\"),\n systemNavItem(\"orders\"),\n systemNavItem(\"subscriptions\"),\n sectionHeader(\"Marketing Assets\"),\n systemNavItem(\"share/products\"),\n ];\n}\n","import type React from \"react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport {\n Bell,\n Box,\n Boxes,\n Briefcase,\n Calendar,\n CircleUser,\n Compass,\n ContactRound,\n File,\n FileText,\n FileVideo,\n Folder,\n FolderOpen,\n Globe,\n GraduationCap,\n Heart,\n House,\n Image,\n LayoutGrid,\n ListMusic,\n Mail,\n MessageCircle,\n MessageSquare,\n PackageOpen,\n Repeat,\n Search,\n Settings,\n ShoppingCart,\n Star,\n Store,\n User,\n UserCog,\n Users,\n} from \"lucide-react\";\n\nconst ICON_MAP: Record<string, LucideIcon> = {\n \"address-card\": ContactRound,\n \"arrows-repeat\": Repeat,\n bell: Bell,\n box: Box,\n \"boxes-stacked\": Boxes,\n briefcase: Briefcase,\n calendar: Calendar,\n \"cart-shopping\": ShoppingCart,\n \"circle-user\": CircleUser,\n comment: MessageCircle,\n compass: Compass,\n envelope: Mail,\n file: File,\n \"file-lines\": FileText,\n \"file-video\": FileVideo,\n folder: Folder,\n \"folder-open\": FolderOpen,\n gear: Settings,\n globe: Globe,\n \"graduation-cap\": GraduationCap,\n heart: Heart,\n house: House,\n image: Image,\n \"list-music\": ListMusic,\n \"magnifying-glass\": Search,\n message: MessageSquare,\n \"package-open\": PackageOpen,\n star: Star,\n store: Store,\n \"table-cells-large\": LayoutGrid,\n user: User,\n \"user-gear\": UserCog,\n users: Users,\n};\n\ninterface RepIconProps {\n name: string;\n className?: string;\n}\n\nexport function RepIcon({ name, className }: RepIconProps): React.JSX.Element {\n const Icon = ICON_MAP[name] ?? File;\n return <Icon className={className} />;\n}\n","/**\n * Utility functions for managing navigation state in localStorage\n */\n\nconst STORAGE_PREFIX = \"portal-app-last-tab\";\n\n/**\n * Gets the localStorage key for a specific app and top-level section\n */\nfunction getStorageKey(appDefinitionId: number, topLevelSlug: string): string {\n return `${STORAGE_PREFIX}-${appDefinitionId}-${topLevelSlug}`;\n}\n\n/**\n * Retrieves the last visited tab slug for a given section\n */\nexport function getLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): string | null {\n if (typeof window === \"undefined\") return null;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n return localStorage.getItem(key);\n } catch (error) {\n console.warn(\"Failed to read from localStorage:\", error);\n return null;\n }\n}\n\n/**\n * Stores the last visited tab slug for a given section\n */\nexport function setLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n tabSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.setItem(key, tabSlug);\n } catch (error) {\n console.warn(\"Failed to write to localStorage:\", error);\n }\n}\n\n/**\n * Clears the last visited tab for a given section\n */\nexport function clearLastVisitedTab(\n appDefinitionId: number,\n topLevelSlug: string,\n): void {\n if (typeof window === \"undefined\") return;\n\n try {\n const key = getStorageKey(appDefinitionId, topLevelSlug);\n localStorage.removeItem(key);\n } catch (error) {\n console.warn(\"Failed to remove from localStorage:\", error);\n }\n}\n","import type { NavigationItem } from \"../types/navigation\";\nimport { normalizeSlug } from \"./system-navigation-items\";\nimport { getLastVisitedTab } from \"./navigation-storage\";\n\n/**\n * True when `normalizedRoute` is the section's slug or a descendant of it, so a\n * tab stays selected while drilling into its sub-routes (e.g. `shop` stays\n * active on a product detail at `shop/123`). The trailing \"/\" keeps the match\n * on a path boundary so `shop` does not match an unrelated `shopping` route.\n */\nfunction routeMatchesSection(\n normalizedRoute: string,\n slug: string | undefined,\n): boolean {\n const normalizedSlug = normalizeSlug(slug);\n if (!normalizedSlug) return false;\n return (\n normalizedRoute === normalizedSlug ||\n normalizedRoute.startsWith(`${normalizedSlug}/`)\n );\n}\n\n/**\n * Helper function to determine if the current route is within a top-level section\n */\nexport function isInSection(\n topLevelItem: NavigationItem,\n currentRoute: string,\n): boolean {\n const normalizedCurrentRoute = normalizeSlug(currentRoute);\n\n if (topLevelItem.children && topLevelItem.children.length > 0) {\n return topLevelItem.children.some((child: NavigationItem) =>\n routeMatchesSection(normalizedCurrentRoute, child.slug),\n );\n }\n\n return routeMatchesSection(normalizedCurrentRoute, topLevelItem.slug);\n}\n\n/**\n * Get the target slug when clicking a top-level navigation item\n */\nexport function getNavigationTarget(\n item: NavigationItem,\n appDefinitionId: number,\n): string | null {\n // If it has children, it's always a section parent — navigate to a child\n if (item.children && item.children.length > 0) {\n // Try to get the last visited tab from localStorage (only if parent has a slug for the key)\n if (item.slug) {\n const lastVisitedSlug = getLastVisitedTab(appDefinitionId, item.slug);\n\n // If we have a last visited tab and it exists in the children, use it\n if (lastVisitedSlug) {\n const normalizedLastVisited = normalizeSlug(lastVisitedSlug);\n const childExists = item.children.some((child: NavigationItem) => {\n const normalizedChildSlug = normalizeSlug(child.slug);\n return normalizedChildSlug === normalizedLastVisited;\n });\n if (childExists) {\n return normalizedLastVisited;\n }\n }\n }\n\n // Default to the first child's slug\n const firstChild = item.children[0];\n if (firstChild?.slug) {\n return normalizeSlug(firstChild.slug);\n }\n\n return null;\n }\n\n // If it's a leaf node with a screen, return its slug directly (normalized)\n if (item.screen_id && item.slug) {\n return normalizeSlug(item.slug);\n }\n\n // If it's a leaf node with just a slug (system nav items like account, messages, etc.)\n if (item.slug) {\n return normalizeSlug(item.slug);\n }\n\n return null;\n}\n","import type { TranslationApi } from \"@fluid-app/i18n/translation-api\";\nimport type { ShellDict } from \"../shell-translation-dictionary\";\nimport { shellEn } from \"../shell-translation-dictionary\";\n\n/**\n * Derive a shell translation key from a navigation slug.\n *\n * Converts slugs like \"app-download\" → \"nav_label_app_download\"\n * and \"share/media\" → \"nav_label_share_media\".\n */\nfunction slugToTranslationKey(slug: string): string {\n return `nav_label_${slug.replace(/-/g, \"_\").replace(/\\//g, \"_\")}`;\n}\n\n/**\n * Derive a shell translation key from a section header label.\n *\n * Converts labels like \"We Commerce\" → \"nav_section_we_commerce\"\n * and \"Marketing Assets\" → \"nav_section_marketing_assets\".\n */\nfunction sectionToTranslationKey(label: string): string {\n return `nav_section_${label.toLowerCase().replace(/\\s+/g, \"_\")}`;\n}\n\n/**\n * Check whether a builder has customized the label by comparing it against the\n * English default. If the fallback label differs from the English dictionary\n * value, the builder intentionally renamed the nav item and we should honour\n * their custom label rather than overriding it with a translation.\n */\nfunction isCustomLabel(key: string, fallbackLabel: string): boolean {\n const englishDefault = (shellEn as Record<string, string>)[key];\n return englishDefault !== undefined && fallbackLabel !== englishDefault;\n}\n\n/**\n * Translate a navigation item label using the shell translation dictionary.\n *\n * Looks up `nav_label_<normalized_slug>` in the dictionary. Falls back to the\n * raw `label` when:\n * - the slug is absent or has no matching translation key\n * - the builder has customised the label (differs from the English default)\n */\nexport function translateNavLabel(\n api: TranslationApi<ShellDict>,\n slug: string | undefined,\n fallbackLabel: string,\n): string {\n if (!slug) return fallbackLabel;\n const t = api.t as (key: string) => string;\n const slugKey = slugToTranslationKey(slug);\n\n if (!isCustomLabel(slugKey, fallbackLabel)) {\n const translated = t(slugKey);\n if (translated !== slugKey) return translated;\n }\n\n // Slug didn't match (e.g. account has slug \"profile\" but label \"Account\").\n // Try a key derived from the label itself before falling back to raw English.\n const labelKey = `nav_label_${fallbackLabel.toLowerCase().replace(/[-\\s]+/g, \"_\")}`;\n if (labelKey !== slugKey) {\n const translated = t(labelKey);\n if (translated !== labelKey) return translated;\n }\n\n return fallbackLabel;\n}\n\n/**\n * Translate a sidebar section header label using the shell translation dictionary.\n *\n * Looks up `nav_section_<normalized_label>` in the dictionary. Falls back to the\n * raw `label` when no matching translation key exists or when the builder has\n * customised the section name.\n */\nexport function translateSectionLabel(\n api: TranslationApi<ShellDict>,\n label: string,\n): string {\n const key = sectionToTranslationKey(label);\n // If the builder renamed the section, honour their custom label\n if (isCustomLabel(key, label)) return label;\n const translated = (api.t as (key: string) => string)(key);\n return translated === key ? label : translated;\n}\n","import * as React from \"react\";\nimport { Ellipsis } from \"lucide-react\";\nimport { MobileBottomSheet } from \"@fluid-app/ui-components/components/MobileBottomSheet\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n isInSection,\n getNavigationTarget,\n} from \"@fluid-app/portal-core/navigation/utils\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { useNativeCapabilities } from \"@fluid-app/portal-core/native-capabilities-api-context\";\nimport { useBottomNavHidden } from \"@fluid-app/portal-react/shell/BottomNavVisibilityContext\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport type { NavigationItem } from \"../types/navigation\";\n\ninterface SdkBottomNavProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n appDefinitionId?: number;\n maxVisibleItems?: number;\n}\n\nexport function SdkBottomNav({\n navItems,\n currentSlug,\n onNavigate,\n appDefinitionId = 0,\n maxVisibleItems = 6,\n}: SdkBottomNavProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const shellApi = useShellTranslation();\n const { t } = shellApi;\n const nativeCapabilities = useNativeCapabilities();\n // Drill-down surfaces (e.g. an open conversation) hide the nav for a native\n // full-screen feel. Kept mounted so it can slide/fade rather than pop out.\n const hidden = useBottomNavHidden();\n const [moreOpen, setMoreOpen] = React.useState(false);\n\n if (!isMobile || !useBottomNav) return null;\n\n const navigableItems = navItems\n .filter((item) => !item.parent_id)\n .filter((item) => item.slug || (item.children && item.children.length > 0));\n\n const hasOverflow = navigableItems.length > maxVisibleItems;\n const visibleItems = hasOverflow\n ? navigableItems.slice(0, maxVisibleItems - 1)\n : navigableItems;\n const overflowItems = hasOverflow\n ? navigableItems.slice(maxVisibleItems - 1)\n : [];\n\n // The \"More\" tab counts as active when any overflow item is in section.\n const overflowIsActive = overflowItems.some((item) =>\n isInSection(item, currentSlug),\n );\n\n const handleItemClick = (item: NavigationItem) => {\n const target = getNavigationTarget(item, appDefinitionId);\n if (target) {\n nativeCapabilities.haptic(\"light\");\n onNavigate(target);\n }\n setMoreOpen(false);\n };\n\n const handleMoreClick = () => {\n nativeCapabilities.haptic(\"light\");\n setMoreOpen(true);\n };\n\n return (\n <>\n <nav\n data-fluid-bottom-nav=\"\"\n data-hidden={hidden ? \"\" : undefined}\n aria-hidden={hidden || undefined}\n inert={hidden}\n className={`border-border bg-sidebar fixed right-0 bottom-0 left-0 z-40 flex h-[calc(3.5rem+env(safe-area-inset-bottom))] items-start justify-around border-t pb-[env(safe-area-inset-bottom)] transition-[transform,opacity] duration-300 ease-out motion-reduce:transition-none ${\n hidden\n ? \"pointer-events-none translate-y-full opacity-0\"\n : \"translate-y-0 opacity-100\"\n }`}\n >\n {visibleItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n aria-current={isActive ? \"page\" : undefined}\n className={`flex h-14 flex-1 touch-manipulation flex-col items-center justify-center gap-0.5 pt-2 pb-1 text-[10px] font-medium transition-[color,transform] duration-150 active:scale-95 ${\n isActive ? \"text-primary\" : \"text-muted-foreground\"\n }`}\n >\n <RepIcon name={item.icon || \"file\"} className=\"size-5\" />\n <span className=\"max-w-[72px] truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n </button>\n );\n })}\n\n {hasOverflow && (\n <button\n type=\"button\"\n onClick={handleMoreClick}\n aria-current={overflowIsActive ? \"page\" : undefined}\n className={`flex h-14 flex-1 touch-manipulation flex-col items-center justify-center gap-0.5 pt-2 pb-1 text-[10px] font-medium transition-[color,transform] duration-150 active:scale-95 ${\n overflowIsActive ? \"text-primary\" : \"text-muted-foreground\"\n }`}\n >\n <Ellipsis className=\"size-5\" />\n <span>{t(\"nav_more\")}</span>\n </button>\n )}\n </nav>\n\n <MobileBottomSheet\n open={moreOpen}\n onOpenChange={setMoreOpen}\n title={t(\"nav_more\")}\n surface=\"sidebar\"\n >\n {overflowItems.map((item) => {\n const isActive = isInSection(item, currentSlug);\n return (\n <button\n key={item.id ?? item.slug ?? item.label}\n type=\"button\"\n onClick={() => handleItemClick(item)}\n className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors ${\n isActive\n ? \"bg-sidebar-primary text-sidebar-primary-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent\"\n }`}\n >\n <RepIcon name={item.icon || \"file\"} className=\"size-5\" />\n <span>{translateNavLabel(shellApi, item.slug, item.label)}</span>\n </button>\n );\n })}\n </MobileBottomSheet>\n </>\n );\n}\n","import { useEffect } from \"react\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\nconst SPIN_STYLE_ID = \"fluid-app-shell-loading-spin\";\n\n/**\n * Inject the spin keyframes style once into the document head.\n */\nfunction ensureSpinStyle(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(SPIN_STYLE_ID)) return;\n\n const style = document.createElement(\"style\");\n style.id = SPIN_STYLE_ID;\n style.textContent = `@keyframes spin { to { transform: rotate(360deg); } }`;\n document.head.appendChild(style);\n}\n\n/**\n * Full-page loading spinner shown while AppShell is fetching app data\n * for the first time. Uses inline styles (not Tailwind) since the theme\n * hasn't loaded yet.\n */\nexport function AppShellLoading(): React.JSX.Element {\n const { t } = useShellTranslation();\n\n useEffect(() => {\n ensureSpinStyle();\n }, []);\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: \"100vh\",\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n backgroundColor: \"#f9fafb\",\n }}\n >\n <div\n style={{\n width: \"40px\",\n height: \"40px\",\n border: \"3px solid #e5e7eb\",\n borderTopColor: \"#3b82f6\",\n borderRadius: \"50%\",\n animation: \"spin 1s linear infinite\",\n }}\n />\n <p\n style={{\n marginTop: \"1rem\",\n color: \"#6b7280\",\n fontSize: \"0.875rem\",\n }}\n >\n {t(\"loading\")}\n </p>\n </div>\n );\n}\n","import { useEffect, useState } from \"react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { ChevronRight } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport {\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport {\n Collapsible,\n CollapsibleTrigger,\n CollapsibleContent,\n} from \"@fluid-app/ui-primitives\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport type { NavigationItem } from \"../types/navigation\";\n\nexport interface CollapsibleNavItemProps {\n item: NavigationItem;\n isActive: boolean;\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function CollapsibleNavItem({\n item,\n isActive,\n currentSlug,\n onNavigate,\n}: CollapsibleNavItemProps): React.JSX.Element {\n const [open, setOpen] = useState(isActive);\n const shellApi = useShellTranslation();\n\n useEffect(() => {\n if (isActive) setOpen(true);\n }, [isActive]);\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n asChild\n className=\"group/collapsible\"\n >\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton>\n <RepIcon name={item.icon || \"file\"} />\n <span className=\"truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n <ChevronRight className=\"ml-auto size-3 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.children.map((child) => {\n const childSlug = normalizeSlug(child.slug);\n const childIsActive = normalizeSlug(currentSlug) === childSlug;\n\n return (\n <SidebarMenuItem key={child.slug || child.label}>\n <SidebarMenuButton\n isActive={childIsActive}\n onClick={() => onNavigate(childSlug)}\n >\n <RepIcon name={child.icon || \"file\"} className=\"size-3\" />\n <span>\n {translateNavLabel(shellApi, child.slug, child.label)}\n </span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n );\n}\n","import {\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { isInSection } from \"@fluid-app/portal-core/navigation/utils\";\nimport {\n translateNavLabel,\n translateSectionLabel,\n} from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { CollapsibleNavItem } from \"./CollapsibleNavItem\";\n\nexport interface SdkNavigationProps {\n navItems: NavigationItem[];\n currentSlug: string;\n onNavigate: (slug: string) => void;\n}\n\nexport function SdkNavigation({\n navItems,\n currentSlug,\n onNavigate,\n}: SdkNavigationProps): React.JSX.Element {\n const shellApi = useShellTranslation();\n\n return (\n <SidebarMenu>\n {navItems.map((item, index) => {\n // Skip items without slug AND without label\n if (!item.slug && !item.label) return null;\n\n const isActive = isInSection(item, currentSlug);\n const hasChildren = item.children?.length > 0;\n\n // Items with children — collapsible\n if (hasChildren) {\n return (\n <CollapsibleNavItem\n key={item.slug || item.label}\n item={item}\n isActive={isActive}\n currentSlug={currentSlug}\n onNavigate={onNavigate}\n />\n );\n }\n\n // Items without a slug are section headers\n if (!item.slug) {\n return (\n <SidebarGroupLabel key={item.id ?? `section-${index}`}>\n {translateSectionLabel(shellApi, item.label)}\n </SidebarGroupLabel>\n );\n }\n\n // Leaf item — no children\n const itemSlug = normalizeSlug(item.slug);\n\n return (\n <SidebarMenuItem key={item.id ?? itemSlug}>\n <SidebarMenuButton\n isActive={isActive}\n onClick={() => onNavigate(itemSlug)}\n >\n <RepIcon name={item.icon || \"file\"} />\n <span className=\"truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n );\n })}\n </SidebarMenu>\n );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useLanguagesApi } from \"@fluid-app/store-core/languages-api-context\";\nimport { storeKeys } from \"../account/query-keys\";\nimport { SUPPORTED_LANGUAGES } from \"../hooks/use-portal-active-locale\";\n\nexport interface MobileLanguageOption {\n readonly iso: string;\n readonly name: string;\n}\n\nexport interface UseMobileLanguagesResult {\n readonly languages: readonly MobileLanguageOption[];\n readonly locale: string;\n readonly setLocale: (iso: string) => void;\n readonly currentName: string;\n}\n\n/**\n * Resolve the language options to offer in the mobile switcher: the company's\n * enabled languages, falling back to the full supported set if that request\n * hasn't returned, failed, or came back empty (better to over-offer than to\n * strand the user with no way to switch).\n */\nexport function useMobileLanguages(): UseMobileLanguagesResult {\n const { locale, setLocale } = useActiveLocale();\n const languagesApi = useLanguagesApi();\n\n const { data } = useQuery({\n queryKey: storeKeys.languages(),\n queryFn: () => languagesApi.listLanguages({ limit: 100 }),\n staleTime: 5 * 60 * 1000,\n });\n\n const enabledCodes = new Set((data?.languages ?? []).map((l) => l.code));\n const filtered = SUPPORTED_LANGUAGES.filter((l) => enabledCodes.has(l.iso));\n const languages = filtered.length > 0 ? filtered : SUPPORTED_LANGUAGES;\n const current = languages.find((l) => l.iso === locale);\n\n return { languages, locale, setLocale, currentName: current?.name ?? locale };\n}\n","import { useState, useCallback } from \"react\";\nimport {\n Moon,\n Sun,\n LogOut,\n Smartphone,\n Globe,\n Check,\n ChevronRight,\n ChevronLeft,\n} from \"lucide-react\";\nimport { MobileBottomSheet } from \"@fluid-app/ui-components/components/MobileBottomSheet\";\nimport { useMobileLanguages } from \"./use-mobile-languages\";\nimport { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useRepUser } from \"@fluid-app/portal-widgets/contexts\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\nexport interface SdkMobileProfileMenuProps {\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nfunction getInitials(name: string | null | undefined): string {\n if (!name) return \"U\";\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n (parts[0]?.[0] ?? \"\") + (parts[parts.length - 1]?.[0] ?? \"\")\n ).toUpperCase();\n }\n return (name[0] ?? \"U\").toUpperCase();\n}\n\nexport function SdkMobileProfileMenu({\n onNavigate,\n onLogout,\n}: SdkMobileProfileMenuProps): React.JSX.Element | null {\n const { isMobile, useBottomNav } = useSidebar();\n const [isOpen, setIsOpen] = useState(false);\n // Which view the sheet shows: the main profile menu or the language list\n // (an inline drill-down within the same sheet — no second sheet).\n const [view, setView] = useState<\"main\" | \"language\">(\"main\");\n const user = useRepUser();\n const themeMode = useThemeMode();\n const languages = useMobileLanguages();\n const { t } = useShellTranslation();\n\n const handleNavigate = useCallback(\n (slug: string) => {\n setIsOpen(false);\n onNavigate(slug);\n },\n [onNavigate],\n );\n\n const handleOpenChange = useCallback((open: boolean) => {\n setIsOpen(open);\n // Always reset to the main view when the sheet closes.\n if (!open) setView(\"main\");\n }, []);\n\n if (!isMobile || !useBottomNav) return null;\n\n const initials = getInitials(user?.name);\n\n return (\n <>\n <button\n type=\"button\"\n onClick={() => setIsOpen(true)}\n className=\"bg-background text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-full p-1\"\n aria-label={t(\"profile_open_menu\")}\n >\n {user?.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? t(\"profile_user_fallback\")}\n className=\"size-8 rounded-full object-cover\"\n />\n ) : (\n <span className=\"flex size-8 items-center justify-center text-xs font-medium\">\n {initials}\n </span>\n )}\n </button>\n\n <MobileBottomSheet\n open={isOpen}\n onOpenChange={handleOpenChange}\n surface=\"sidebar\"\n title={\n view === \"language\" ? (\n <button\n type=\"button\"\n onClick={() => setView(\"main\")}\n className=\"text-foreground -ml-1 flex items-center gap-1\"\n aria-label={t(\"nav_back\")}\n >\n <ChevronLeft className=\"size-5\" />\n <span>{t(\"language_select\")}</span>\n </button>\n ) : (\n t(\"profile_title\")\n )\n }\n >\n {view === \"language\" ? (\n <div className=\"flex flex-col pb-2\">\n {languages.languages.map((lang) => {\n const isActive = lang.iso === languages.locale;\n return (\n <button\n key={lang.iso}\n type=\"button\"\n onClick={() => {\n languages.setLocale(lang.iso);\n setView(\"main\");\n }}\n className=\"hover:bg-sidebar-accent flex w-full items-center justify-between gap-3 rounded-lg px-3 py-3.5 text-left\"\n >\n <span\n className={\n isActive\n ? \"text-foreground text-base font-semibold\"\n : \"text-foreground text-base\"\n }\n >\n {lang.name}\n </span>\n {isActive && (\n <Check className=\"text-primary size-5 shrink-0\" />\n )}\n </button>\n );\n })}\n </div>\n ) : (\n <>\n {user && (\n <div className=\"border-border flex items-center gap-3 border-b px-3 py-4\">\n {user.imageUrl ? (\n <img\n src={user.imageUrl}\n alt={user.name ?? t(\"profile_user_fallback\")}\n className=\"size-10 rounded-full object-cover\"\n />\n ) : (\n <span className=\"bg-muted flex size-10 items-center justify-center rounded-full text-sm font-medium\">\n {initials}\n </span>\n )}\n <div className=\"min-w-0 flex-1\">\n {user.name && (\n <p className=\"text-foreground truncate text-sm font-semibold\">\n {user.name}\n </p>\n )}\n {user.email && (\n <p className=\"text-muted-foreground truncate text-xs\">\n {user.email}\n </p>\n )}\n </div>\n </div>\n )}\n\n <div className=\"border-border border-b py-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"app-download\")}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <Smartphone className=\"size-5\" />\n <span>{t(\"profile_app_download\")}</span>\n </button>\n </div>\n\n <div className=\"space-y-1 py-2\">\n <button\n type=\"button\"\n onClick={() => setView(\"language\")}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <Globe className=\"size-5 shrink-0\" />\n <span>{t(\"language_select\")}</span>\n <span className=\"text-muted-foreground ml-auto flex min-w-0 items-center gap-1\">\n <span className=\"truncate\">{languages.currentName}</span>\n <ChevronRight className=\"size-4 shrink-0\" />\n </span>\n </button>\n\n <button\n type=\"button\"\n onClick={themeMode.cycleMode}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n {themeMode.mode === \"dark\" ? (\n <Moon className=\"size-5\" />\n ) : (\n <Sun className=\"size-5\" />\n )}\n <span>\n {themeMode.mode === \"light\"\n ? t(\"theme_light\")\n : t(\"theme_dark\")}\n </span>\n </button>\n\n {onLogout && (\n <button\n type=\"button\"\n onClick={onLogout}\n className=\"text-destructive hover:bg-sidebar-accent flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium\"\n >\n <LogOut className=\"size-5\" />\n <span>{t(\"logout\")}</span>\n </button>\n )}\n </div>\n </>\n )}\n </MobileBottomSheet>\n </>\n );\n}\n","import { useSidebar } from \"@fluid-app/portal-react/shell/sidebar\";\nimport { IconButton } from \"@fluid-app/ui-primitives\";\nimport { ChevronLeft, Menu } from \"lucide-react\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { SdkMobileProfileMenu } from \"./SdkMobileProfileMenu\";\n\nexport interface SdkHeaderProps {\n mobileTabs?: NavigationItem[];\n currentSlug: string;\n /** Current page title, shown on the left of the mobile top bar. */\n title?: string;\n /**\n * When provided, a native back button is shown at the start of the mobile\n * top bar. Set only on drill-down screens (e.g. a product detail) so tapping\n * it returns to the parent list.\n */\n onBack?: () => void;\n onNavigate: (slug: string) => void;\n onLogout?: () => void;\n}\n\nexport function SdkHeader({\n mobileTabs,\n currentSlug,\n title,\n onBack,\n onNavigate,\n onLogout,\n}: SdkHeaderProps): React.JSX.Element | null {\n const sidebar = useSidebar();\n const shellApi = useShellTranslation();\n const { t } = shellApi;\n\n // Localize the title the same way the desktop header does (translateNavLabel),\n // so nav-item titles aren't left as raw English on mobile. The \"More\" screen\n // isn't a tenant nav item, so it has no derived title — fall back to the\n // localized \"More\" label there.\n const resolvedTitle = title\n ? translateNavLabel(shellApi, currentSlug, title)\n : normalizeSlug(currentSlug) === \"more\"\n ? t(\"nav_more\")\n : undefined;\n\n const isMobileWithBottomNav = sidebar.isMobile && sidebar.useBottomNav;\n const isMobileWithHamburger = sidebar.isMobile && !sidebar.useBottomNav;\n\n // Desktop has nothing to render here (theme toggle moved to sidebar footer).\n // Skip the entire header so we don't reserve a 52px gutter for empty space.\n if (!isMobileWithBottomNav && !isMobileWithHamburger) {\n return null;\n }\n\n return (\n <header className=\"bg-sidebar shrink-0 pt-[env(safe-area-inset-top)]\">\n {isMobileWithBottomNav ? (\n <div className=\"flex h-[52px] items-center gap-2 px-4\">\n {onBack ? (\n <IconButton\n icon={ChevronLeft}\n aria-label={t(\"nav_back\")}\n onClick={onBack}\n className=\"border-border/60 bg-background/70 -ml-1 flex-none rounded-full border shadow-sm backdrop-blur-md transition-transform active:scale-95 [&_svg]:size-5\"\n />\n ) : null}\n {resolvedTitle ? (\n <h1 className=\"text-sidebar-foreground min-w-0 flex-1 truncate text-[17px] leading-tight font-semibold tracking-[-0.01em]\">\n {resolvedTitle}\n </h1>\n ) : null}\n <div className=\"text-muted-foreground ml-auto flex flex-none items-center justify-end gap-2\">\n <SdkMobileProfileMenu onNavigate={onNavigate} onLogout={onLogout} />\n </div>\n </div>\n ) : (\n <div className=\"flex h-[52px] items-center px-6\">\n <button\n type=\"button\"\n onClick={sidebar.toggleSidebar}\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex shrink-0 items-center justify-center rounded-md p-2\"\n aria-label={t(\"nav_toggle_sidebar\")}\n >\n <Menu className=\"size-5\" />\n </button>\n </div>\n )}\n\n {/* Mobile child tabs — shown below header when parent has children */}\n {isMobileWithBottomNav && mobileTabs && mobileTabs.length > 0 && (\n <nav className=\"scrollbar-none flex items-center gap-1 overflow-x-auto px-4\">\n {mobileTabs.map((tab) => {\n const tabSlug = normalizeSlug(tab.slug);\n if (!tabSlug) return null;\n\n const isActive = normalizeSlug(currentSlug) === tabSlug;\n\n return (\n <button\n key={tab.id ?? tabSlug}\n type=\"button\"\n onClick={() => onNavigate(tabSlug)}\n className={`border-b-2 px-4 py-2.5 text-sm font-medium whitespace-nowrap transition-colors ${\n isActive\n ? \"border-primary text-foreground\"\n : \"text-muted-foreground hover:border-border hover:text-foreground border-transparent\"\n }`}\n >\n {translateNavLabel(shellApi, tab.slug, tab.label)}\n </button>\n );\n })}\n </nav>\n )}\n </header>\n );\n}\n","import { useState } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { Globe, Check } from \"lucide-react\";\nimport { useActiveLocale } from \"@fluid-app/i18n/locale-context\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { useLanguagesApi } from \"@fluid-app/store-core/languages-api-context\";\nimport { storeKeys } from \"../account/query-keys\";\nimport { SUPPORTED_LANGUAGES } from \"../hooks/use-portal-active-locale\";\n\nexport function LanguageSelector(): React.JSX.Element {\n const [open, setOpen] = useState(false);\n const { locale, setLocale } = useActiveLocale();\n const { t } = useShellTranslation();\n const languagesApi = useLanguagesApi();\n\n const { data: languagesData } = useQuery({\n queryKey: storeKeys.languages(),\n queryFn: () => languagesApi.listLanguages({ limit: 100 }),\n staleTime: 5 * 60 * 1000,\n });\n\n // Fall back to the full supported list if the company-languages request\n // hasn't returned (loading), failed, or came back empty — better to over-\n // offer than strand the user with no way to switch language.\n const enabledCodes = new Set(\n (languagesData?.languages ?? []).map((l) => l.code),\n );\n const filtered = SUPPORTED_LANGUAGES.filter((l) => enabledCodes.has(l.iso));\n const languagesToRender =\n filtered.length > 0 ? filtered : SUPPORTED_LANGUAGES;\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shrink-0 gap-1.5\"\n aria-label={t(\"language_select\")}\n >\n <Globe className=\"size-3\" />\n <span className=\"text-xs uppercase\">{locale.split(\"_\")[0]}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className=\"bg-popover z-50 w-56 rounded-lg p-1 shadow-lg\"\n >\n {languagesToRender.map((lang) => (\n <Button\n key={lang.iso}\n variant=\"ghost\"\n className=\"h-auto w-full justify-between px-3 py-2 text-left text-sm whitespace-normal\"\n onClick={() => {\n setLocale(lang.iso);\n setOpen(false);\n }}\n >\n <span>{lang.name}</span>\n {lang.iso === locale && <Check className=\"size-4\" />}\n </Button>\n ))}\n </PopoverContent>\n </Popover>\n );\n}\n","import { useState } from \"react\";\nimport { Building2 } from \"lucide-react\";\n\nexport function CompanyImage({\n src,\n alt,\n size,\n}: {\n size: number;\n src: string;\n alt: string;\n}) {\n const [error, setError] = useState(false);\n\n if (!src || error)\n return (\n <Building2\n style={{ height: size, width: size }}\n className=\"text-muted-foreground/25\"\n />\n );\n\n return (\n <img\n className=\"max-h-8 max-w-8 object-contain\"\n alt={alt}\n src={src}\n width={size}\n height={size}\n onError={() => setError(true)}\n />\n );\n}\n","import type { Company } from \"@fluid-app/company-switcher-core\";\nimport { cn } from \"@fluid-app/ui-primitives\";\nimport { CompanyImage } from \"./CompanyImage\";\n\nexport function CompanyItem({\n logo_url,\n icon_url,\n name,\n compact = false,\n}: Omit<Company, \"id\"> & { compact?: boolean }) {\n const resolvedSrc = icon_url || logo_url || \"\";\n return (\n <>\n <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg\">\n <CompanyImage alt={name} src={resolvedSrc} size={32} />\n </div>\n <div\n className={cn(\n \"grid text-left text-sm leading-tight\",\n compact ? \"flex-shrink-0\" : \"flex-1\",\n )}\n >\n <span className=\"truncate font-semibold\">{name}</span>\n </div>\n </>\n );\n}\n","import { Skeleton } from \"@fluid-app/ui-primitives\";\n\nexport function CompanySwitcherSkeleton({ show }: { show: boolean }) {\n if (!show) return null;\n\n return (\n <div className=\"flex items-center gap-4 p-2\">\n <Skeleton className=\"size-5 shrink-0 rounded-sm bg-current\" />\n <Skeleton className=\"h-4 w-36 bg-current\" />\n </div>\n );\n}\n","import { useStore } from \"../hooks/use-store\";\nimport {\n CompanyItem,\n CompanySwitcherSkeleton,\n} from \"@fluid-app/company-switcher-ui\";\n\n/**\n * Static company display for single-tenant portal.\n * Shows logo + name in the sidebar header. No switching — just branding.\n */\nexport function SdkCompanySwitcher(): React.JSX.Element | null {\n const { data: store, isLoading } = useStore();\n\n if (isLoading) {\n return <CompanySwitcherSkeleton show />;\n }\n\n if (!store) {\n return null;\n }\n\n return (\n <div className=\"flex w-full items-center gap-2 rounded-md p-2\">\n <CompanyItem\n logo_url={store.logo_url}\n icon_url={store.icon_url}\n name={store.name || store.subdomain}\n />\n </div>\n );\n}\n","/**\n * Navigate one step back in the browser history if anything is on the stack;\n * otherwise navigate to the SPA root. Used by fallback screens (error\n * boundary, 404) to give the user a way out without risking a navigation\n * outside the SPA when no prior history exists.\n */\nexport function goBackOrHome(): void {\n if (window.history.length > 1) {\n window.history.back();\n } else {\n window.location.assign(\"/\");\n }\n}\n","import { Button } from \"@fluid-app/ui-primitives\";\nimport { Compass } from \"lucide-react\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { goBackOrHome } from \"../shell/go-back-or-home\";\nimport { FallbackCard } from \"./FallbackCard\";\n\n/**\n * Rendered by `PageRouter` when the current slug doesn't match any custom\n * page, system screen, or builder screen. This is a true \"not found\" — the\n * portal isn't configured to render anything for this path.\n */\nexport function ScreenNotFound({\n name,\n}: {\n readonly name?: string;\n}): React.JSX.Element {\n const { t } = useShellTranslation();\n return (\n <FallbackCard\n icon={<Compass />}\n title={t(\"not_found_title\")}\n description={\n name ? t(\"not_found_named\", { name }) : t(\"not_found_generic\")\n }\n actions={\n <Button variant=\"default\" onClick={goBackOrHome}>\n {t(\"error_go_back\")}\n </Button>\n }\n />\n );\n}\n","import { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\n/**\n * Shown when a customer attempts to access a rep-only page via direct URL.\n */\nexport function AccessDeniedScreen(): React.JSX.Element {\n const { t } = useShellTranslation();\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"border-border max-w-sm rounded-lg border border-dashed p-8 text-center\">\n <h2 className=\"text-foreground text-xl font-semibold\">\n {t(\"access_denied_title\")}\n </h2>\n <p className=\"text-muted-foreground mt-2\">\n {t(\"access_denied_description\")}\n </p>\n <p className=\"text-muted-foreground mt-1 text-sm\">\n {t(\"access_denied_detail\")}\n </p>\n </div>\n </div>\n );\n}\n","/**\n * Default property schema registry for SDK apps.\n * Maps widget type names to their editable property schemas from\n * portal-widgets — the same widget set covered by\n * `DEFAULT_SDK_WIDGET_REGISTRY` in `./default-widget-registry`.\n *\n * Used to validate authored widget props at render time (see\n * `warnUnknownWidgetProps` in `../utils/warn-unknown-widget-props`), not for\n * the builder's property-panel UI — that registry\n * (`DEFAULT_PROPERTY_SCHEMAS` in `@fluid-app/portal-builder`) is assembled\n * separately from the same per-widget schema exports and may include\n * builder-only widgets this one doesn't.\n */\n\nimport type { PropertySchemaRegistry } from \"@fluid-app/portal-core/registries\";\nimport {\n alertWidgetPropertySchema,\n bulletListWidgetPropertySchema,\n calendarWidgetPropertySchema,\n cardWidgetPropertySchema,\n carouselWidgetPropertySchema,\n chartWidgetPropertySchema,\n containerWidgetPropertySchema,\n embedWidgetPropertySchema,\n imageWidgetPropertySchema,\n layoutWidgetPropertySchema,\n linkWidgetPropertySchema,\n listWidgetPropertySchema,\n mySiteWidgetPropertySchema,\n nestedWidgetPropertySchema,\n pointsWidgetPropertySchema,\n quickLinksWidgetPropertySchema,\n quickShareWidgetPropertySchema,\n recentActivityWidgetPropertySchema,\n separatorWidgetPropertySchema,\n spacerWidgetPropertySchema,\n tableWidgetPropertySchema,\n textWidgetPropertySchema,\n toDoWidgetPropertySchema,\n videoWidgetPropertySchema,\n} from \"@fluid-app/portal-widgets/widgets\";\n// ShopWidget is imported via its direct subpath — see default-widget-registry.ts\n// for the --isolatedDeclarations rationale; the same barrel-avoidance applies\n// to its property schema.\nimport { shopWidgetPropertySchema } from \"@fluid-app/portal-widgets/widgets/ShopWidget\";\n\nexport const DEFAULT_PROPERTY_SCHEMA_REGISTRY: PropertySchemaRegistry = {\n AlertWidget: alertWidgetPropertySchema,\n BulletListWidget: bulletListWidgetPropertySchema,\n CalendarWidget: calendarWidgetPropertySchema,\n CardWidget: cardWidgetPropertySchema,\n CarouselWidget: carouselWidgetPropertySchema,\n ChartWidget: chartWidgetPropertySchema,\n ContainerWidget: containerWidgetPropertySchema,\n EmbedWidget: embedWidgetPropertySchema,\n ImageWidget: imageWidgetPropertySchema,\n LayoutWidget: layoutWidgetPropertySchema,\n LinkWidget: linkWidgetPropertySchema,\n ListWidget: listWidgetPropertySchema,\n MySiteWidget: mySiteWidgetPropertySchema,\n NestedWidget: nestedWidgetPropertySchema,\n PointsWidget: pointsWidgetPropertySchema,\n QuickLinksWidget: quickLinksWidgetPropertySchema,\n QuickShareWidget: quickShareWidgetPropertySchema,\n RecentActivityWidget: recentActivityWidgetPropertySchema,\n SeparatorWidget: separatorWidgetPropertySchema,\n ShopWidget: shopWidgetPropertySchema,\n SpacerWidget: spacerWidgetPropertySchema,\n TableWidget: tableWidgetPropertySchema,\n TextWidget: textWidgetPropertySchema,\n ToDoWidget: toDoWidgetPropertySchema,\n VideoWidget: videoWidgetPropertySchema,\n};\n","import type {\n PropertyField,\n PropertySchemaRegistry,\n WidgetPropertySchema,\n} from \"@fluid-app/portal-core/registries\";\n\n/**\n * Props every widget instance may legitimately receive that are never part\n * of a widget's own editable property schema:\n * - Standard React/DOM pass-through props (`className`, `style`, `children`,\n * `id`, `key`).\n * - Structural identity props the screen-renderer pipeline injects itself\n * for container widgets so they can support drag/select wiring\n * (`widgetId`, `widgetPath`) — see `ScreenRenderer.tsx` in\n * `@fluid-app/portal-widgets/core` and `WidgetContent` in\n * `@fluid-app/portal-builder/core/widget-rendering`, which both inject\n * these for the same widget nodes rendered here.\n * Never warn about these even though they never appear in a widget's\n * `fields` list.\n */\nexport const ALWAYS_ALLOWED_WIDGET_PROPS: ReadonlySet<string> = new Set([\n \"className\",\n \"style\",\n \"children\",\n \"id\",\n \"key\",\n \"widgetId\",\n \"widgetPath\",\n]);\n\n/** Maximum Levenshtein distance for a \"did you mean\" suggestion to be shown. */\nconst MAX_SUGGESTION_DISTANCE = 3;\n\n/**\n * Common authoring aliases → the canonical prop name to suggest. Levenshtein\n * only catches spelling slips; these are *semantic* misses (the motivating\n * incident authored `content` where the schema says `description` — edit\n * distance 9, far beyond any sane typo threshold). A suggestion is only shown\n * when the canonical name actually exists in the widget's schema.\n */\nconst PROP_NAME_SYNONYMS: Readonly<Record<string, string>> = {\n content: \"description\",\n text: \"description\",\n body: \"description\",\n copy: \"description\",\n message: \"description\",\n heading: \"title\",\n header: \"title\",\n label: \"title\",\n};\n\n/** `${widgetType}::${propName}` pairs already warned about this session. */\nconst warnedPairs = new Set<string>();\n\n// Vite exposes `import.meta.env.DEV`; other bundlers/runtimes (Next.js, Node,\n// Jest/CJS) don't define it at all. The optional chain means a missing/absent\n// `env` can never throw, so this can never fire outside a Vite dev build —\n// mirrors the existing `import.meta.env?.DEV` check in `FluidProvider.tsx`.\n// Written as a direct property access (no intermediate `as` cast) so it\n// compiles to a literal `import.meta.env` substring: the portal-sdk Jest\n// transform text-replaces exactly that substring with `globalThis.__VITE_ENV__`\n// at test time (see `jest-swc-transform.cjs`), which is how tests toggle\n// dev/prod mode. A cast that splits \"meta\" from \".env\" in the compiled output\n// would slip past that replacement and crash Jest on the raw `import.meta`\n// syntax.\nfunction isDevBuild(): boolean {\n return import.meta.env?.DEV === true;\n}\n\n/**\n * Levenshtein edit distance between two strings. Small inline helper (no new\n * dependency) — only ever called with short widget-prop-name strings.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n let previousRow: number[] = Array.from({ length: b.length + 1 }, (_, j) => j);\n\n for (let i = 1; i <= a.length; i++) {\n const currentRow: number[] = [i];\n for (let j = 1; j <= b.length; j++) {\n const substitutionCost = a[i - 1] === b[j - 1] ? 0 : 1;\n // Bounds are guaranteed by the loop ranges; noUncheckedIndexedAccess\n // still types these as possibly-undefined, so fall back defensively.\n const deletion = (previousRow[j] ?? Infinity) + 1;\n const insertion = (currentRow[j - 1] ?? Infinity) + 1;\n const substitution = (previousRow[j - 1] ?? Infinity) + substitutionCost;\n currentRow.push(Math.min(deletion, insertion, substitution));\n }\n previousRow = currentRow;\n }\n\n return previousRow[b.length] ?? Math.max(a.length, b.length);\n}\n\n/**\n * Finds the suggestion for an unknown prop: a known semantic synonym first\n * (see {@link PROP_NAME_SYNONYMS}), then the nearest known key by Levenshtein\n * distance within {@link MAX_SUGGESTION_DISTANCE}.\n */\nfunction findNearestKey(\n propName: string,\n candidates: readonly string[],\n): string | undefined {\n const synonym = PROP_NAME_SYNONYMS[propName.toLowerCase()];\n if (synonym && candidates.includes(synonym)) return synonym;\n\n let nearest: string | undefined;\n let nearestDistance = MAX_SUGGESTION_DISTANCE + 1;\n\n for (const candidate of candidates) {\n const distance = levenshteinDistance(propName, candidate);\n if (distance < nearestDistance) {\n nearestDistance = distance;\n nearest = candidate;\n }\n }\n\n return nearestDistance <= MAX_SUGGESTION_DISTANCE ? nearest : undefined;\n}\n\n/**\n * Collects every prop name a widget's property schema actually recognizes as\n * a `widget.props` key:\n * - `borderRadius` fields are composite — their top-level `key` is a schema\n * grouping identifier, not a real widget prop, so its four corner keys are\n * expanded instead (mirrors `extractPropertyValues` in\n * `@fluid-app/portal-core/registries`).\n * - `dataSource` fields are excluded entirely. Per the same\n * `extractPropertyValues` reference, this field type only exists so the\n * property panel can gate other fields' visibility on whether a data\n * source is configured — its value is read from `widget.dataSource` (the\n * top-level, sibling-of-`props` config), never from `widget.props`.\n * Authoring a literal `props.dataSource` is a real mistake (the config\n * belongs one level up), so its field `key` must not be treated as an\n * accepted prop here.\n */\nfunction collectKnownPropKeys(fields: readonly PropertyField[]): string[] {\n const keys: string[] = [];\n for (const field of fields) {\n if (field.type === \"borderRadius\") {\n keys.push(...Object.values(field.keys));\n } else if (field.type !== \"dataSource\") {\n keys.push(field.key);\n }\n }\n return keys;\n}\n\n/**\n * Merges schema field keys with `dataSourceTargetProps` — props a data\n * source may populate (e.g. ListWidget's `items`) that aren't necessarily\n * declared as editable fields — into one deduped list. Used for both the\n * known/unknown check and what's surfaced to the author (the \"Known props:\"\n * list and suggestion candidates), so a typo of a data-source-only prop\n * still gets a correct suggestion and the valid name is actually listed.\n */\nfunction collectAllKnownKeys(schema: WidgetPropertySchema): string[] {\n const fieldKeys = collectKnownPropKeys(schema.fields);\n const targetProps = schema.dataSourceTargetProps ?? [];\n return Array.from(new Set([...fieldKeys, ...targetProps]));\n}\n\nexport interface WarnUnknownWidgetPropsInput {\n /** The widget's `type` discriminant (e.g. \"TextWidget\"). */\n readonly widgetType: string;\n /** The authored props from the widget's JSON node (`widget.props`). */\n readonly props: Readonly<Record<string, unknown>>;\n /** Registry of built-in property schemas to validate against. */\n readonly registry: PropertySchemaRegistry;\n}\n\n/**\n * Dev-only console warning for widget props not declared in the widget's\n * registered property schema. Guards against the class of bug where an\n * authored screen sets a prop the widget doesn't know about (typo'd name,\n * stale field from a prior schema version) — the prop is silently dropped\n * and the widget renders its placeholder defaults with no signal to the\n * author.\n *\n * No-ops entirely outside a Vite dev build (checked first, before any prop\n * iteration, so there's zero overhead in production bundles) and for widget\n * types with no registered schema (custom/company widgets) — there's nothing\n * to validate against, so we skip silently rather than warn.\n *\n * Warns at most once per (widgetType, propName) pair per session.\n */\nexport function warnUnknownWidgetProps({\n widgetType,\n props,\n registry,\n}: WarnUnknownWidgetPropsInput): void {\n if (!isDevBuild()) return;\n\n const schema = registry[widgetType];\n if (!schema) return;\n\n const knownKeys = collectAllKnownKeys(schema);\n const knownKeySet = new Set(knownKeys);\n\n for (const propName of Object.keys(props)) {\n if (knownKeySet.has(propName)) continue;\n if (ALWAYS_ALLOWED_WIDGET_PROPS.has(propName)) continue;\n\n const pairKey = `${widgetType}::${propName}`;\n if (warnedPairs.has(pairKey)) continue;\n warnedPairs.add(pairKey);\n\n const suggestion = findNearestKey(propName, knownKeys);\n const suggestionText = suggestion ? ` Did you mean \"${suggestion}\"?` : \"\";\n\n console.warn(\n `[portal] ${widgetType} received unknown prop \"${propName}\" — not in its schema.${suggestionText} Known props: ${knownKeys.join(\", \")}.`,\n );\n }\n}\n\n/**\n * Test-only: clears the per-session (widgetType, propName) dedupe set so\n * \"warns once\" behavior can be verified across multiple test cases.\n */\nexport function resetUnknownWidgetPropWarnings(): void {\n warnedPairs.clear();\n}\n","/**\n * BuilderScreenView — renders a builder screen's component_tree with\n * DataAwareWidget support for data-bound widgets.\n *\n * Uses the shared ScreenRenderer from portal-widgets under the hood,\n * but wraps data-bound widgets with DataAwareWidget from portal-core.\n */\n\nimport React, { memo } from \"react\";\nimport type { WidgetSchema } from \"@fluid-app/portal-core/types\";\nimport type { ScreenDefinition } from \"@fluid-app/portal-core/types\";\nimport { DataAwareWidget } from \"@fluid-app/portal-react/data-sources/DataAwareWidget\";\nimport { useRegistry } from \"@fluid-app/portal-widgets/contexts\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { DEFAULT_PROPERTY_SCHEMA_REGISTRY } from \"../core/default-property-schema-registry\";\nimport { isDefaultWidgetComponent } from \"../core/default-widget-registry\";\nimport { warnUnknownWidgetProps } from \"../utils/warn-unknown-widget-props\";\n\ninterface WidgetRendererProps {\n widget: WidgetSchema;\n index: number;\n}\n\n/**\n * Renders a single widget, wrapping with DataAwareWidget if it has a dataSource.\n */\nfunction WidgetRenderer({ widget, index }: WidgetRendererProps) {\n const registry = useRegistry();\n const Component = registry[widget.type];\n\n if (!Component) {\n console.warn(\n `[BuilderScreenView] Widget type \"${String(widget.type)}\" not found in registry`,\n );\n return null;\n }\n\n // Dev-only: warns once per (widgetType, propName) pair when an authored\n // screen sets a prop the widget's schema doesn't declare — the prop is\n // otherwise silently dropped and the widget falls back to its placeholder\n // defaults with no signal to whoever authored the screen. Checked against\n // `widget.props` (the authored JSON), not any data-source-merged props\n // below, since those are only ever populated for keys the schema already\n // declares. No-ops outside dev builds and for widget types with no\n // registered schema (custom/company widgets).\n //\n // `registry` (from context) is the runtime registry apps can extend or\n // override — it isn't necessarily `DEFAULT_SDK_WIDGET_REGISTRY`. Only run\n // the check when `Component` is actually the built-in the schema\n // describes; otherwise an app-overridden built-in (different prop shape)\n // would get false positives, and a custom widget reusing a built-in type\n // name would get validated against the wrong contract.\n if (isDefaultWidgetComponent(widget.type, Component)) {\n warnUnknownWidgetProps({\n widgetType: widget.type,\n props: widget.props,\n registry: DEFAULT_PROPERTY_SCHEMA_REGISTRY,\n });\n }\n\n if (widget.dataSource) {\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <DataAwareWidget widget={widget} Component={Component} />\n </div>\n );\n }\n\n return (\n <div key={widget.id ?? index} className=\"h-full w-full overflow-x-hidden\">\n <Component {...widget.props} />\n </div>\n );\n}\n\nexport interface BuilderScreenViewProps {\n /** The screen definition to render */\n screen: ScreenDefinition;\n /** Additional CSS classes for the wrapper div */\n className?: string;\n}\n\n/**\n * Renders a builder screen's component_tree with full data source support.\n * Widgets with `dataSource` config are automatically wrapped with `DataAwareWidget`\n * which fetches data and merges it with static props before rendering.\n */\nfunction BuilderScreenViewImpl({\n screen,\n className,\n}: BuilderScreenViewProps): React.JSX.Element | null {\n const { t } = useShellTranslation();\n const widgets = screen.component_tree;\n\n if (!widgets || widgets.length === 0) {\n return (\n <div className={className ?? \"h-full w-full\"}>\n <div className=\"text-muted-foreground flex h-full items-center justify-center\">\n <p>{t(\"screen_no_content\")}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className={className ?? \"h-full w-full\"}>\n {widgets.map((widget, index) => {\n if (!widget) return null;\n return (\n <WidgetRenderer\n key={widget.id ?? index}\n widget={widget}\n index={index}\n />\n );\n })}\n </div>\n );\n}\n\nexport const BuilderScreenView: React.MemoExoticComponent<\n typeof BuilderScreenViewImpl\n> = memo(BuilderScreenViewImpl);\n","import type { ComponentType } from \"react\";\n\n/**\n * Per-member-type portal content.\n *\n * Lets a portal serve different screens to different member types — e.g.\n * an `affiliate` sees a different dashboard at the `home` slug than a\n * `customer` does — without forking the whole app. Keyed by the real\n * member-type slug (from `member_type_detail.slug`), then by page slug.\n *\n * Resolution order in the router: a per-type page for the current\n * member's slug wins over the global `customPages` entry for the same\n * slug, which in turn wins over the built-in system screen. A member type\n * with no entry falls through to the global content unchanged.\n */\n\n/** A portal page component, receiving the resolved slug and rest params. */\nexport type CustomPageComponent = ComponentType<{\n slug?: string;\n params?: string;\n}>;\n\n/** Page components keyed by navigation slug. */\nexport type CustomPages = Record<string, CustomPageComponent>;\n\n/** Per-member-type page overrides: memberTypeSlug -> (pageSlug -> component). */\nexport type MemberTypeCustomPages = Record<string, CustomPages>;\n\n/**\n * Resolve the page component to render for a given member-type slug and\n * page slug, preferring an exact page match then a base-slug (prefix)\n * match. Returns undefined when the member type has no override for the\n * route, so the caller falls back to global content.\n *\n * Known limitation: `baseSlug` is the caller's navigation-derived base (see\n * PageRouter), so a per-type override registered at a detail *prefix* that is\n * not itself a navigation slug (e.g. `share/product` when the nav only has\n * `share/products`) is only matched on an exact `currentSlug`. Data-driven,\n * prefix-aware matching against the registry's own keys is deferred to the\n * authoring phase; register the exact slug you route to for now.\n */\nexport function resolveMemberTypePage(\n registry: MemberTypeCustomPages | undefined,\n memberTypeSlug: string | null,\n currentSlug: string,\n baseSlug: string,\n): CustomPageComponent | undefined {\n if (!registry || !memberTypeSlug) return undefined;\n\n const pages = registry[memberTypeSlug];\n if (!pages) return undefined;\n\n return (\n pages[currentSlug] ??\n (baseSlug !== currentSlug ? pages[baseSlug] : undefined)\n );\n}\n","import { lazy, type ComponentType } from \"react\";\n\nconst ProfileScreen = lazy(() =>\n import(\"../screens/ProfileScreen\").then((m) => ({\n default: m.ProfileScreen,\n })),\n);\nconst OrdersScreen = lazy(() =>\n import(\"../screens/OrdersScreen\").then((m) => ({\n default: m.OrdersScreen,\n })),\n);\nconst SubscriptionsScreen = lazy(() =>\n import(\"../screens/SubscriptionsScreen\").then((m) => ({\n default: m.SubscriptionsScreen,\n })),\n);\nconst MessagingScreen = lazy(() =>\n import(\"../screens/MessagingScreen\").then((m) => ({\n default: m.MessagingScreen,\n })),\n);\nconst ContactsScreen = lazy(() =>\n import(\"../screens/ContactsScreen\").then((m) => ({\n default: m.ContactsScreen,\n })),\n);\nconst ShopScreen = lazy(() =>\n import(\"../screens/ShopScreen\").then((m) => ({ default: m.ShopScreen })),\n);\nconst CustomersScreen = lazy(() =>\n import(\"../screens/CustomersScreen\").then((m) => ({\n default: m.CustomersScreen,\n })),\n);\nconst ShareablesScreen = lazy(() =>\n import(\"../screens/ShareablesScreen\").then((m) => ({\n default: m.ShareablesScreen,\n })),\n);\nconst MySiteScreen = lazy(() =>\n import(\"../screens/MySiteScreen\").then((m) => ({\n default: m.MySiteScreen,\n })),\n);\nconst AppDownloadScreen = lazy(() =>\n import(\"../screens/AppDownloadScreen\").then((m) => ({\n default: m.AppDownloadScreen,\n })),\n);\n\nexport const SYSTEM_SLUG_SCREEN_MAP: Record<string, ComponentType> = {\n profile: ProfileScreen,\n // Legacy alias: portals that pre-date the /account → /profile rename may\n // still have \"account\" persisted in their nav config.\n account: ProfileScreen,\n orders: OrdersScreen,\n subscriptions: SubscriptionsScreen,\n messages: MessagingScreen,\n contacts: ContactsScreen,\n shop: ShopScreen,\n customers: CustomersScreen,\n \"my-site\": MySiteScreen,\n \"share/products\": ShareablesScreen,\n \"share/product\": ShareablesScreen,\n \"share/media\": ShareablesScreen,\n \"share/playlists\": ShareablesScreen,\n \"share/playlist\": ShareablesScreen,\n \"share/files\": ShareablesScreen,\n \"share/pages\": ShareablesScreen,\n \"share/page\": ShareablesScreen,\n \"app-download\": AppDownloadScreen,\n};\n\n/**\n * System screens that a portal must opt into by adding them to its active\n * navigation. Every other system screen is reachable by default; these two\n * only render when their nav slug is present.\n */\nexport const OPT_IN_SYSTEM_SCREENS: ReadonlySet<ComponentType> = new Set([\n MessagingScreen,\n ContactsScreen,\n]);\n\n/** Pre-sorted keys for greedy prefix matching (longest first). */\nconst SORTED_SCREEN_KEYS = Object.keys(SYSTEM_SLUG_SCREEN_MAP).sort(\n (a, b) => b.length - a.length,\n);\n\n/** Prefix-match a slug against screen map keys, longest match first. */\nexport function findScreenByPrefix(slug: string): ComponentType | undefined {\n for (const key of SORTED_SCREEN_KEYS) {\n if (slug === key || slug.startsWith(key + \"/\")) {\n return SYSTEM_SLUG_SCREEN_MAP[key];\n }\n }\n return undefined;\n}\n","import { Suspense, useMemo, type ComponentType } from \"react\";\nimport {\n isSystemNavigationItem,\n isRepOnlySlug,\n} from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { slugifyName } from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport type { NavigationItem, ScreenDefinition } from \"../types/navigation\";\nimport { CoreScreenPlaceholder } from \"../screens/CoreScreenPlaceholder\";\nimport { ScreenNotFound } from \"../screens/ScreenNotFound\";\nimport { AccessDeniedScreen } from \"../screens/AccessDeniedScreen\";\nimport { BuilderScreenView } from \"./BuilderScreenView\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { deniesRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\nimport {\n resolveMemberTypePage,\n type MemberTypeCustomPages,\n} from \"@fluid-app/portal-core/member-type-content\";\nimport {\n OPT_IN_SYSTEM_SCREENS,\n SYSTEM_SLUG_SCREEN_MAP,\n findScreenByPrefix,\n} from \"./system-screen-map\";\n\n/**\n * Returns true if any nav slug resolves to the given system screen component.\n * Lets detail routes (e.g. `share/product/:id`) render when their listing\n * slug (`share/products`) is in the nav, since both map to the same screen.\n */\nfunction isSystemScreenCoveredByNav(\n target: ComponentType,\n navSlugs: readonly string[],\n): boolean {\n for (const slug of navSlugs) {\n const navScreen = SYSTEM_SLUG_SCREEN_MAP[slug] ?? findScreenByPrefix(slug);\n if (navScreen === target) return true;\n }\n return false;\n}\n\nfunction isRouteCoveredByNav(\n currentSlug: string,\n baseSlug: string,\n navSlugs: readonly string[] | undefined,\n): boolean {\n if (navSlugs === undefined) return true;\n return navSlugs.some(\n (navSlug) =>\n navSlug === baseSlug ||\n currentSlug === navSlug ||\n currentSlug.startsWith(navSlug + \"/\"),\n );\n}\n\nexport interface PageRouterProps {\n currentSlug: string;\n currentNavItem?: NavigationItem | undefined;\n customPages?:\n | Record<string, ComponentType<{ slug?: string; params?: string }>>\n | undefined;\n /** Per-member-type page overrides, resolved by the current member's slug. */\n memberTypeCustomPages?: MemberTypeCustomPages | undefined;\n /** Builder screen definitions (from fluidos API) */\n screens?: ScreenDefinition[] | undefined;\n baseSlug: string;\n restParams: string;\n /**\n * Slugs from the active navigation. When provided, screens listed in\n * `OPT_IN_SYSTEM_SCREENS` only render if they're\n * reachable via the nav — matching the implicit gating builder screens\n * get from being sourced via `appData.screens`. All other system screens\n * bypass this check. Omit to render system screens unconditionally.\n */\n navSlugs?: readonly string[] | undefined;\n}\n\nexport function PageRouter({\n currentSlug,\n currentNavItem,\n customPages,\n memberTypeCustomPages,\n screens,\n baseSlug,\n restParams,\n navSlugs,\n}: PageRouterProps): React.JSX.Element {\n const { data: account } = useAccount();\n // Rep-only routes are gated on the member type's `rep_eligible` permission\n // so custom non-rep types are blocked the same as customers.\n const canRep = !deniesRepSurfaces(account);\n\n // Build a screen lookup map by ID for O(1) access\n const screenById = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n return new Map(screens.map((s) => [s.id, s]));\n }, [screens]);\n\n // Build a screen lookup map by slug for fallback resolution. Keys include\n // both the screen's own `slug` (e.g. `screen-{uuid}` for direct URL access)\n // and `slugifyName(screen.name)` to match nav-item slugs authored in the\n // admin builder via `generateSlugFromName(screen.name)` — without this,\n // the slug fallback never matches because the two formats diverge.\n const screenBySlug = useMemo(() => {\n if (!screens || screens.length === 0) return undefined;\n const map = new Map<string, ScreenDefinition>();\n for (const s of screens) {\n if (s.slug) map.set(s.slug, s);\n }\n for (const s of screens) {\n if (!s.name) continue;\n const nameSlug = slugifyName(s.name);\n if (nameSlug && !map.has(nameSlug)) map.set(nameSlug, s);\n }\n return map;\n }, [screens]);\n\n // Resolve builder screen: screen_id first, then slug fallback\n const builderScreen = useMemo(() => {\n if (currentNavItem?.screen_id && screenById) {\n const byId = screenById.get(currentNavItem.screen_id);\n if (byId) return byId;\n }\n if (screenBySlug) {\n return (\n screenBySlug.get(currentSlug) ?? screenBySlug.get(baseSlug) ?? undefined\n );\n }\n return undefined;\n }, [\n currentNavItem?.screen_id,\n screenById,\n screenBySlug,\n currentSlug,\n baseSlug,\n ]);\n\n const routeCoveredByNav =\n currentNavItem !== undefined ||\n isRouteCoveredByNav(currentSlug, baseSlug, navSlugs);\n const isRepOnlyRoute = isRepOnlySlug(currentSlug);\n\n // 0. Block customer access to rep-only slugs only when the route is\n // configured in navigation. Hidden rep-only routes should look like normal\n // missing pages instead of revealing that a rep-only screen exists.\n if (!canRep && isRepOnlyRoute && routeCoveredByNav) {\n return <AccessDeniedScreen />;\n }\n\n // 0b. Per-member-type page override — an affiliate/customer/etc. can be\n // served a different screen at the same slug. Wins over the global\n // customPages and system screens below.\n //\n // Keyed on the REAL member type slug (member_type_detail.slug), never\n // the binary fallback: content must not be selected for a member whose\n // true type is still unknown (backfill gap / loading), or a custom type\n // would be served the generic rep/customer bucket's page. Resolves to\n // null in that case and falls through to the global content below.\n //\n // A rep-only route the member cannot access is never served a per-type\n // page — it must fall through to the AccessDenied/404 handling so a\n // per-type override cannot bypass the rep-only gate.\n const memberTypeContentSlug = account?.member_type_detail?.slug ?? null;\n const MemberTypePage =\n canRep || !isRepOnlyRoute\n ? resolveMemberTypePage(\n memberTypeCustomPages,\n memberTypeContentSlug,\n currentSlug,\n baseSlug,\n )\n : undefined;\n if (MemberTypePage) {\n return <MemberTypePage slug={currentSlug} params={restParams} />;\n }\n\n // 1. Custom page match (takes priority over system defaults)\n const ExactCustomPage = customPages?.[currentSlug];\n if (ExactCustomPage) {\n return <ExactCustomPage slug={currentSlug} params={restParams} />;\n }\n\n // 2. Prefix custom page match (base slug with rest params)\n if (baseSlug !== currentSlug) {\n const PrefixCustomPage = customPages?.[baseSlug];\n if (PrefixCustomPage) {\n return <PrefixCustomPage slug={currentSlug} params={restParams} />;\n }\n }\n\n // 3. System navigation item with a dedicated screen component\n // First try exact match on baseSlug, then prefix-match currentSlug\n // against screen map keys (handles detail routes like \"share/product/123\"\n // matching \"share/product\" when the slug isn't a registered nav item).\n const SystemScreen =\n SYSTEM_SLUG_SCREEN_MAP[baseSlug] ?? findScreenByPrefix(currentSlug);\n const systemScreenRequiresNav =\n SystemScreen !== undefined &&\n (OPT_IN_SYSTEM_SCREENS.has(SystemScreen) || isRepOnlyRoute);\n const systemScreenInNav =\n navSlugs === undefined ||\n (SystemScreen !== undefined &&\n (!systemScreenRequiresNav ||\n routeCoveredByNav ||\n isSystemScreenCoveredByNav(SystemScreen, navSlugs)));\n if (SystemScreen && systemScreenInNav) {\n return (\n <Suspense\n fallback={\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"border-primary size-8 animate-spin rounded-full border-2 border-t-transparent\" />\n </div>\n }\n >\n <SystemScreen />\n </Suspense>\n );\n }\n\n // 4. Recognized system screen gated out by step 3. Treat opt-in and\n // rep-only system screens absent from the active nav as missing pages.\n if (SystemScreen && !systemScreenInNav) {\n return <ScreenNotFound name={currentNavItem?.label ?? currentSlug} />;\n }\n\n // 5. Recognized system slug without an SDK screen mapping yet.\n if (isSystemNavigationItem(baseSlug)) {\n return <CoreScreenPlaceholder name={currentNavItem?.label ?? baseSlug} />;\n }\n\n // 6. Builder screen (by screen_id or slug match)\n if (builderScreen) {\n return <BuilderScreenView screen={builderScreen} />;\n }\n\n // 7. True \"not found\" — slug didn't match any custom/system/builder screen\n return <ScreenNotFound name={currentNavItem?.label ?? currentSlug} />;\n}\n","import { Component, type ErrorInfo, type ReactNode, use } from \"react\";\nimport { Button } from \"@fluid-app/ui-primitives\";\nimport { TriangleAlert } from \"lucide-react\";\nimport { ShellTranslationContext } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport { shellEn } from \"@fluid-app/portal-core/shell-translation-dictionary\";\nimport { FallbackCard } from \"../screens/FallbackCard\";\nimport { goBackOrHome } from \"./go-back-or-home\";\n\nfunction DefaultErrorFallback({\n onReset,\n}: {\n onReset: () => void;\n}): React.JSX.Element {\n const api = use(ShellTranslationContext);\n const t = api ? api.t : (key: keyof typeof shellEn) => shellEn[key];\n return (\n <FallbackCard\n icon={<TriangleAlert />}\n title={t(\"error_title\")}\n description={t(\"error_description\")}\n actions={\n <>\n <Button variant=\"outline\" onClick={onReset}>\n {t(\"error_try_again\")}\n </Button>\n <Button variant=\"default\" onClick={goBackOrHome}>\n {t(\"error_go_back\")}\n </Button>\n </>\n }\n />\n );\n}\n\nexport interface AppShellErrorBoundaryProps {\n children: ReactNode;\n /**\n * Invoked every time the boundary catches an error. Fires again on each\n * subsequent crash if `reset()` is called and the underlying issue persists.\n * When provided, the boundary will not log to `console.error` itself —\n * forward to your reporter (e.g. Sentry) inside this callback.\n *\n * @example\n * ```tsx\n * <AppShellErrorBoundary onError={(err, info) => Sentry.captureException(err, { extra: info })}>\n * ```\n */\n onError?: (error: Error, info: ErrorInfo) => void;\n /** Override the default fallback UI. Receives a reset callback. */\n fallback?: (props: { error: Error; reset: () => void }) => ReactNode;\n}\n\ninterface State {\n error: Error | null;\n}\n\nexport class AppShellErrorBoundary extends Component<\n AppShellErrorBoundaryProps,\n State\n> {\n override state: State = { error: null };\n\n static getDerivedStateFromError(error: Error): State {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: ErrorInfo): void {\n if (this.props.onError) {\n try {\n this.props.onError(error, info);\n } catch (callbackError) {\n // A throwing onError (e.g. misconfigured Sentry transport) must not\n // mask the original crash. Surface both to console as a fallback.\n console.error(\"[portal] onError handler threw:\", callbackError);\n console.error(\"[portal] Original screen crash:\", error, info);\n }\n return;\n }\n console.error(\"[portal] Screen crashed:\", error, info);\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n override render(): ReactNode {\n const { error } = this.state;\n if (!error) return this.props.children;\n\n if (this.props.fallback) {\n return this.props.fallback({ error, reset: this.reset });\n }\n\n return <DefaultErrorFallback onReset={this.reset} />;\n }\n}\n","import type Color from \"colorjs.io\";\nimport type { ResolvedTheme } from \"@fluid-app/portal-core/theme\";\n\nexport const PORTAL_NATIVE_THEME_MESSAGE_TYPE = \"fluid.portal.theme\";\nexport const PORTAL_NATIVE_THEME_MESSAGE_VERSION = 1;\n\nexport type PortalNativeThemeMode = \"light\" | \"dark\";\n\nexport interface PortalNativeThemeMessage {\n readonly type: typeof PORTAL_NATIVE_THEME_MESSAGE_TYPE;\n readonly version: typeof PORTAL_NATIVE_THEME_MESSAGE_VERSION;\n readonly payload: {\n readonly themeId: string;\n readonly mode: PortalNativeThemeMode;\n readonly colors: {\n readonly muted: string;\n };\n };\n}\n\ninterface ReactNativeWebViewBridge {\n postMessage: (message: string) => void;\n}\n\ndeclare global {\n interface Window {\n ReactNativeWebView?: ReactNativeWebViewBridge;\n }\n}\n\nexport function colorToSrgbHex(color: Color): string {\n const srgb = color.to(\"srgb\");\n srgb.alpha = 1;\n return srgb.toString({ format: \"hex\" });\n}\n\nexport function createPortalNativeThemeMessage({\n resolvedTheme,\n mode,\n}: {\n readonly resolvedTheme: ResolvedTheme;\n readonly mode: PortalNativeThemeMode;\n}): PortalNativeThemeMessage {\n return {\n type: PORTAL_NATIVE_THEME_MESSAGE_TYPE,\n version: PORTAL_NATIVE_THEME_MESSAGE_VERSION,\n payload: {\n themeId: resolvedTheme.id,\n mode,\n colors: {\n muted: colorToSrgbHex(resolvedTheme[mode].muted.base),\n },\n },\n };\n}\n\nexport function postPortalNativeThemeMessage(\n message: PortalNativeThemeMessage,\n): void {\n if (typeof window === \"undefined\") return;\n\n window.ReactNativeWebView?.postMessage(JSON.stringify(message));\n}\n","/**\n * The FairShare SDK identity cookies that must be dropped when the portal\n * member changes. `fluid_session` maps to the server-side `session_uuid` (the\n * backend inherits a new cart's customer from the most recent cart in the same\n * session — so a stale session re-binds carts to the previous member), and\n * `fluid_authenticated_jwt` (+ its timestamp) is Bearer-synced onto carts. We\n * deliberately do NOT touch `fluid_visitor`, locale, or `fluid_affiliate`\n * (attribution is meant to persist).\n */\nconst SDK_IDENTITY_COOKIES = [\n \"fluid_session\",\n \"fluid_authenticated_jwt\",\n \"fluid_authenticated_at\",\n];\n\n/** SDK localStorage keys tied to the member's cart/identity. */\nconst SDK_IDENTITY_STORAGE = [\"fs_cart\", \"fs_authenticated_user\"];\n\n/**\n * Resets the FairShare SDK's persisted shopping identity so the next portal\n * member starts a clean session (CURRENT-2180).\n *\n * The SDK owns this state (localStorage + cookies), and its own `reset()` is\n * insufficient: it sets identity cookies WITH a `domain=` attribute (via\n * `setCookieOnApexAndSubdomains`, using the tenant host as the cookie domain)\n * but deletes them WITHOUT one, so the domain-scoped cookies survive — which is\n * exactly `fluid_session` / `fluid_authenticated_jwt`. So we call `reset()` for\n * its localStorage + in-memory teardown, then expire the identity cookies here\n * with the matching domain.\n *\n * Called on portal logout and company switch. No-op-safe when the SDK global\n * isn't present (e.g. a page that never mounted the cart script, or SSR).\n */\nexport function resetFairShareSdk(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n\n const sdk = (window as unknown as { FairShareSDK?: { reset?: () => void } })\n .FairShareSDK;\n\n if (typeof sdk?.reset === \"function\") {\n try {\n sdk.reset();\n } catch (error) {\n // Best-effort: never let SDK teardown block the logout/switch flow.\n console.error(\"[resetFairShareSdk] SDK reset failed:\", error);\n }\n }\n\n // Expire the identity cookies across every domain they could have been set\n // under. A browser only deletes a cookie when the delete's domain matches how\n // it was written, and these cookies are written under different scopes:\n // `fluid_session` under the tenant host (BFF cookieDomain), but\n // `fluid_authenticated_jwt` can come from a non-BFF login on a broader apex\n // (e.g. `.fluid.app` / `.portal.fluid.app`). So walk up every parent domain\n // of the current host (with and without a leading dot) plus host-only.\n const host = window.location.hostname;\n const labels = host.split(\".\");\n const domainVariants = [\"\"]; // host-only (no domain attribute)\n for (let i = 0; i < labels.length - 1; i++) {\n const domain = labels.slice(i).join(\".\");\n domainVariants.push(`; domain=${domain}`, `; domain=.${domain}`);\n }\n\n for (const name of SDK_IDENTITY_COOKIES) {\n for (const domain of domainVariants) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT${domain}`;\n }\n }\n\n for (const key of SDK_IDENTITY_STORAGE) {\n try {\n window.localStorage.removeItem(key);\n } catch {\n // localStorage may be unavailable in some contexts\n }\n }\n}\n","/**\n * useLogout Hook\n *\n * Provides a complete logout function that:\n * 1. Clears all client-side state (query cache, IndexedDB, sessionStorage)\n * 2. Navigates to GET /logout — Rails clears the session and redirects to the Hub\n */\n\nimport { useCallback } from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseLogoutOptions {\n /** URL to redirect to after logout. Triggers a full page navigation. */\n redirectUrl?: string;\n /** Callback invoked after all state is cleared. Ignored when `redirectUrl` is set. */\n onLogout?: () => void;\n}\n\n/**\n * Hook that returns a `logout` function which clears the server session\n * via `DELETE /logout` and all cached/persisted client state.\n *\n * @example\n * ```tsx\n * const logout = useLogout({ redirectUrl: \"/login\" });\n * <button onClick={logout}>Log out</button>\n * ```\n *\n * @example\n * ```tsx\n * const logout = useLogout({\n * onLogout: () => navigate(\"/signed-out\"),\n * });\n * ```\n */\nexport function useLogout({\n redirectUrl,\n onLogout,\n}: UseLogoutOptions = {}): () => Promise<void> {\n const queryClient = useQueryClient();\n\n const logout = useCallback(async () => {\n // 1. Cancel in-flight requests to prevent cache repopulation\n await queryClient.cancelQueries();\n\n // 2. Clear in-memory TanStack Query cache\n queryClient.clear();\n\n // 3. Clear IndexedDB persisted query cache\n try {\n await deleteDatabase();\n } catch (error) {\n // Non-fatal — continue with logout even if IndexedDB cleanup fails\n console.error(\"[useLogout] Failed to clear IndexedDB cache:\", error);\n }\n\n // 4. Clear sessionStorage — intentionally clears all keys because the\n // portal app owns the full page (not embedded as a micro-frontend).\n try {\n sessionStorage.clear();\n } catch {\n // sessionStorage may be unavailable in some contexts\n }\n\n // 5. Reset the FairShare SDK's shopping identity (cart, session, auth\n // cookies). These live in the SDK's own localStorage/cookies, which the\n // steps above don't touch — leaving them lets the next member inherit\n // the previous member's cart/customer (CURRENT-2180).\n resetFairShareSdk();\n\n // 6. Navigate to the server logout endpoint. Rails clears the session\n // cookie and redirects to the Portal Hub company list. A full page\n // navigation lets the browser follow the redirect naturally — no\n // fetch or JSON parsing needed.\n if (redirectUrl) {\n window.location.href = redirectUrl;\n } else if (onLogout) {\n onLogout();\n } else {\n window.location.href = \"/logout\";\n }\n }, [queryClient, redirectUrl, onLogout]);\n\n return logout;\n}\n","import { useCallback, useState } from \"react\";\nimport { LogOut, Moon, Sun } from \"lucide-react\";\nimport {\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n} from \"@fluid-app/portal-react/shell/sidebar\";\nimport { useThemeMode } from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\n\nexport interface SdkLogoutButtonProps {\n onLogout: () => Promise<void>;\n}\n\n/**\n * Sidebar footer slot. Renders Theme toggle + Log out.\n * Filename is historical — kept to avoid touching the AppShell wiring.\n */\nexport function SdkLogoutButton({\n onLogout,\n}: SdkLogoutButtonProps): React.JSX.Element {\n const [isPending, setIsPending] = useState(false);\n const themeMode = useThemeMode();\n const { t } = useShellTranslation();\n\n const handleClick = useCallback(async () => {\n setIsPending(true);\n try {\n await onLogout();\n } catch (error) {\n console.error(\"[SdkLogoutButton] Logout failed:\", error);\n } finally {\n setIsPending(false);\n }\n }, [onLogout]);\n\n const ThemeIcon = themeMode.displayMode === \"dark\" ? Moon : Sun;\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton\n onClick={themeMode.cycleMode}\n aria-label={t(\"theme_switch\", { mode: themeMode.mode })}\n >\n <ThemeIcon className=\"size-4\" />\n <span>{t(\"theme_label\")}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n <SidebarMenuItem>\n <SidebarMenuButton onClick={handleClick} disabled={isPending}>\n <LogOut className=\"size-4\" />\n <span>{isPending ? t(\"logout_pending\") : t(\"logout\")}</span>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n );\n}\n","import type { NavigationItem } from \"@fluid-app/portal-core/types\";\nimport {\n isRepOnlySlug,\n type SystemNavigationItem,\n} from \"@fluid-app/portal-core/navigation/system-navigation-items\";\n\n/**\n * Filter navigation items by removing rep-only slugs.\n * Handles nested children: rep-only children are removed, and parents\n * with no remaining children are also removed.\n */\nexport function filterRepOnlyNavItems(\n items: NavigationItem[],\n): NavigationItem[] {\n const result: NavigationItem[] = [];\n\n for (const item of items) {\n // Leaf item with a rep-only slug — skip\n if (!item.children?.length) {\n if (isRepOnlySlug(item.slug)) continue;\n result.push(item);\n continue;\n }\n\n // Parent with a rep-only slug — skip entire group\n if (isRepOnlySlug(item.slug)) continue;\n\n // Recursively filter children\n const filteredChildren = filterRepOnlyNavItems(item.children);\n\n // Drop parent if all children were removed\n if (filteredChildren.length === 0) continue;\n\n result.push({ ...item, children: filteredChildren });\n }\n\n return result;\n}\n\n/**\n * Filter system navigation sections by removing rep-only items.\n * Sections with no remaining items are dropped entirely.\n */\nexport function filterRepOnlySections(\n sections: Record<string, SystemNavigationItem[]>,\n): Record<string, SystemNavigationItem[]> {\n const filtered: Record<string, SystemNavigationItem[]> = {};\n for (const [section, items] of Object.entries(sections)) {\n const allowed = items.filter((item) => !isRepOnlySlug(item.slug));\n if (allowed.length > 0) filtered[section] = allowed;\n }\n return filtered;\n}\n","\"use client\";\n\nimport type React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { PanelLeft } from \"lucide-react\";\nimport { useSidebar } from \"./sidebar\";\nimport { useScreenHeaderSlotRefs } from \"./ScreenHeaderContext\";\n\nexport interface ScreenHeaderProps {\n title?: string;\n trailingContent?: ReactNode;\n override?: ScreenHeaderOverride;\n}\n\nexport interface ScreenHeaderOverride {\n title: string;\n action: ReactNode;\n}\n\nexport function ScreenHeader({\n title,\n trailingContent,\n override,\n}: ScreenHeaderProps): React.JSX.Element | null {\n const { toggleSidebar, isMobile } = useSidebar();\n const { breadcrumbsRef, actionsRef, hasBreadcrumbs } =\n useScreenHeaderSlotRefs();\n\n if (!override && !title && !hasBreadcrumbs) return null;\n\n const showTrailing = !isMobile && trailingContent;\n\n return (\n <div\n data-fluid-screen-header=\"\"\n tabIndex={-1}\n className=\"border-foreground/[0.06] bg-background sticky top-0 z-30 flex flex-row items-center border-b px-4 py-3 max-md:hidden md:px-6\"\n >\n {!isMobile && (\n <>\n <button\n type=\"button\"\n onClick={toggleSidebar}\n className=\"text-muted-foreground hover:text-foreground -ml-1 inline-flex items-center justify-center rounded-md p-1\"\n aria-label=\"Toggle Sidebar\"\n >\n <PanelLeft className=\"size-4\" />\n </button>\n <div className=\"bg-border mx-2 h-4 w-px\" />\n </>\n )}\n <div\n data-fluid-screen-header-normal-content\n hidden={override !== undefined}\n className=\"flex min-w-0 flex-1 items-center\"\n >\n <div\n ref={breadcrumbsRef}\n className=\"min-w-0 flex-1 empty:hidden [&_[data-slot=breadcrumb-list]]:text-base!\"\n />\n {!hasBreadcrumbs && title && (\n <h1 className=\"text-foreground text-base font-semibold\">{title}</h1>\n )}\n <div className=\"ml-auto flex items-center gap-2\">\n <div\n ref={actionsRef}\n className=\"flex items-center gap-2 empty:hidden\"\n />\n {showTrailing}\n </div>\n </div>\n {override && (\n <div\n data-fluid-screen-header-override-content\n className=\"flex min-w-0 flex-1 items-center gap-2\"\n >\n <h1 className=\"text-foreground min-w-0 truncate text-base font-semibold\">\n {override.title}\n </h1>\n <div className=\"ml-auto flex shrink-0 items-center\">\n {override.action}\n </div>\n </div>\n )}\n </div>\n );\n}\n","import { useLayoutEffect, useRef, type ReactNode } from \"react\";\nimport { translateNavLabel } from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport {\n ScreenHeader,\n type ScreenHeaderOverride,\n} from \"@fluid-app/portal-react/shell/ScreenHeader\";\nimport {\n useOptionalScreenAreaFullscreenCoordinator,\n useScreenAreaFullscreenSnapshot,\n} from \"./ScreenAreaFullscreenContext\";\n\nfunction FullscreenExitAction({\n label,\n onExit,\n owner,\n}: {\n readonly label: string;\n readonly onExit: () => void;\n readonly owner: symbol;\n}): React.JSX.Element {\n const buttonRef = useRef<HTMLButtonElement>(null);\n useLayoutEffect(() => buttonRef.current?.focus(), [owner]);\n\n return (\n <button\n ref={buttonRef}\n type=\"button\"\n className=\"fluid-portal-fullscreen-exit\"\n onClick={onExit}\n >\n {label}\n </button>\n );\n}\n\n/** Translates the screen title and swaps in trusted fullscreen header chrome. */\nexport function TranslatedScreenHeader({\n slug,\n title,\n trailingContent,\n}: {\n slug: string | undefined;\n title: string | undefined;\n trailingContent: ReactNode;\n}): React.JSX.Element | null {\n const shellApi = useShellTranslation();\n const fullscreenCoordinator = useOptionalScreenAreaFullscreenCoordinator();\n const fullscreen = useScreenAreaFullscreenSnapshot();\n const translatedTitle = title\n ? translateNavLabel(shellApi, slug, title)\n : undefined;\n const fullscreenOverride: ScreenHeaderOverride | undefined =\n fullscreen && fullscreenCoordinator\n ? {\n title: fullscreen.displayName,\n action: (\n <FullscreenExitAction\n label={shellApi.t(\"fullscreen_exit\")}\n onExit={() => fullscreenCoordinator.exit()}\n owner={fullscreen.owner}\n />\n ),\n }\n : undefined;\n\n return (\n <ScreenHeader\n title={translatedTitle}\n trailingContent={trailingContent}\n override={fullscreenOverride}\n />\n );\n}\n","import { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { matchSlugPrefix } from \"@fluid-app/portal-core/navigation/slug-utils\";\n\nconst LEGACY_SHAREABLE_PARENT_SLUGS = {\n \"share/page\": \"share/pages\",\n \"share/playlist\": \"share/playlists\",\n \"share/product\": \"share/products\",\n} as const;\n\nconst SHAREABLE_DETAIL_ROUTE_PREFIXES = [\n \"share/files\",\n \"share/media\",\n \"share/page\",\n \"share/pages\",\n \"share/playlist\",\n \"share/playlists\",\n \"share/product\",\n \"share/products\",\n] as const;\n\nfunction isShareableDetailRoute(normalizedSlug: string): boolean {\n return SHAREABLE_DETAIL_ROUTE_PREFIXES.some((routePrefix) =>\n normalizedSlug.startsWith(routePrefix + \"/\"),\n );\n}\n\nexport function resolveMobileBackTarget(\n currentSlug: string,\n navSlugs: string[],\n previousSlug?: string | null,\n): string | undefined {\n const navMatch = matchSlugPrefix(currentSlug, navSlugs);\n const normalized = normalizeSlug(currentSlug);\n const isDrillDownRoute =\n !!navMatch?.rest || isShareableDetailRoute(normalized);\n const normalizedPrevious = normalizeSlug(previousSlug ?? undefined);\n\n if (\n isDrillDownRoute &&\n normalizedPrevious &&\n normalizedPrevious !== normalized\n ) {\n return normalizedPrevious;\n }\n\n if (navMatch?.rest) return navMatch.matchedSlug;\n\n for (const [routePrefix, parentSlug] of Object.entries(\n LEGACY_SHAREABLE_PARENT_SLUGS,\n )) {\n if (\n normalized.startsWith(routePrefix + \"/\") &&\n navSlugs.includes(parentSlug)\n ) {\n return parentSlug;\n }\n }\n\n return undefined;\n}\n","import {\n useState,\n useEffect,\n useCallback,\n useMemo,\n useRef,\n type ReactNode,\n type ComponentType,\n type ErrorInfo,\n} from \"react\";\nimport { AppShellLayout } from \"@fluid-app/portal-react/shell/AppShellLayout\";\nimport { BottomNavVisibilityProvider } from \"@fluid-app/portal-react/shell/BottomNavVisibilityContext\";\nimport {\n ThemeModeProvider,\n type ThemeMode,\n} from \"@fluid-app/portal-react/shell/ThemeModeContext\";\nimport { normalizeSlug } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { getDefaultNavigation } from \"@fluid-app/portal-core/navigation/default-navigation\";\nimport { ScreenHeaderProvider } from \"@fluid-app/portal-react/shell/ScreenHeaderContext\";\nimport { SdkBottomNav } from \"./SdkBottomNav\";\nimport type {\n RepAppData,\n ScreenDefinition,\n} from \"@fluid-app/portal-core/types\";\nimport {\n applyTheme,\n getDefaultThemeDefinition,\n removeAllThemes,\n resolveTheme,\n} from \"@fluid-app/portal-core/theme\";\nimport { useFluidApp } from \"../hooks/use-fluid-app\";\nimport type { NavigationItem } from \"../types/navigation\";\nimport { AppShellLoading } from \"./AppShellLoading\";\nimport { SdkNavigation } from \"./SdkNavigation\";\nimport { SdkHeader } from \"./SdkHeader\";\nimport { LanguageSelector } from \"./LanguageSelector\";\nimport { SdkCompanySwitcher } from \"./SdkCompanySwitcher\";\nimport { PageRouter } from \"./PageRouter\";\nimport {\n collectNavSlugs,\n matchSlugPrefix,\n extractSlugFromPathname,\n} from \"@fluid-app/portal-core/navigation/slug-utils\";\nimport { AppNavigationProvider } from \"./AppNavigationContext\";\nimport { AppShellErrorBoundary } from \"./AppShellErrorBoundary\";\nimport type { PortalErrorReporter } from \"../errors/portal-error-reporter\";\nimport {\n createPortalNativeThemeMessage,\n postPortalNativeThemeMessage,\n type PortalNativeThemeMode,\n} from \"./native-theme-bridge\";\n\nimport { useLogout } from \"../hooks/use-logout\";\nimport { SdkLogoutButton } from \"./SdkLogoutButton\";\nimport {\n RepUserProvider,\n WidgetInteractionProvider,\n type WidgetInteractionItem,\n} from \"@fluid-app/portal-widgets/contexts\";\nimport { Toaster } from \"@fluid-app/ui-primitives\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { deniesRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\nimport type { MemberTypeCustomPages } from \"@fluid-app/portal-core/member-type-content\";\nimport { filterRepOnlyNavItems } from \"../navigation/filter-nav-items\";\nimport { ScreenAreaFullscreenProvider } from \"./ScreenAreaFullscreenContext\";\nimport { TranslatedScreenHeader } from \"./TranslatedScreenHeader\";\nimport { resolveMobileBackTarget } from \"./mobile-back-target\";\n\nconst THEME_STORAGE_KEY = \"portal-theme-mode\";\n\n// Maps a ShareableResource.shareable_type discriminator to its detail-screen\n// slug segment. The detail route is `share/<slug>/<id>` (see ShareablesScreen).\n// `Playlist` and `Library` both surface under the playlists slug — fetchResource\n// stamps \"Library\" while the metrics adapters stamp \"Playlist\". `EnrollmentPack`\n// is intentionally absent: there's no in-portal detail screen, so clicks open\n// the pack's public canonical_url in a new tab instead — see handleWidgetItemClick.\nconst SHAREABLE_TYPE_TO_SLUG: Record<string, string> = {\n Product: \"products\",\n Medium: \"media\",\n Page: \"pages\",\n Playlist: \"playlists\",\n Library: \"playlists\",\n};\n\nfunction getInitialThemeMode(): ThemeMode {\n if (typeof window === \"undefined\") return \"light\";\n const stored = localStorage.getItem(THEME_STORAGE_KEY);\n if (stored === \"light\" || stored === \"dark\") return stored;\n return \"light\";\n}\n\nfunction findFirstNavigableSlug(items: NavigationItem[]): string {\n for (const item of items) {\n if (item.slug) return normalizeSlug(item.slug);\n for (const child of item.children ?? []) {\n if (child.slug) return normalizeSlug(child.slug);\n }\n }\n return \"\";\n}\n\nfunction findCurrentSection(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n if (\n item.children?.some((child) => normalizeSlug(child.slug) === normalized)\n ) {\n return item;\n }\n }\n return undefined;\n}\n\nfunction findNavItem(\n items: NavigationItem[],\n slug: string,\n): NavigationItem | undefined {\n const normalized = normalizeSlug(slug);\n for (const item of items) {\n if (normalizeSlug(item.slug) === normalized) return item;\n for (const child of item.children ?? []) {\n if (normalizeSlug(child.slug) === normalized) return child;\n }\n }\n return undefined;\n}\n\nexport interface AppShellProps {\n /** Pre-fetched app data (skips internal useFluidApp call if provided) */\n appData?: RepAppData;\n /** Override navigation items (otherwise derived from appData/API) */\n navigation?: NavigationItem[];\n /** Custom page components keyed by slug */\n customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /** Per-member-type page overrides, resolved by the current member's slug. */\n memberTypeCustomPages?: MemberTypeCustomPages;\n /** Base path for subpath deployments (e.g. \"/portal\"). Default: \"/\" */\n basePath?: string;\n /** Controlled current slug */\n currentSlug?: string;\n /** Navigation callback */\n onNavigate?: (slug: string) => void;\n /** Custom sidebar header slot */\n sidebarHeader?: ReactNode;\n /** Custom sidebar footer slot. When provided, replaces the default logout button. */\n sidebarFooter?: ReactNode;\n /**\n * Callback invoked after the user logs out and all client state has been cleared.\n * If not provided, a default logout button is still rendered, but no post-logout\n * action is taken (consumers should pair this with `logoutRedirectUrl`\n * or provide their own `sidebarFooter`).\n */\n onLogout?: () => void;\n /** URL to redirect to after logout (e.g. \"/login\"). Takes precedence over `onLogout`. */\n logoutRedirectUrl?: string;\n /** Optional reporter for screen-level crashes. Falls back to console logging when omitted. */\n errorReporter?: PortalErrorReporter;\n /** Render prop or static children for the content area */\n children?:\n | ReactNode\n | ((props: {\n currentSlug: string;\n currentNavItem: NavigationItem | undefined;\n }) => ReactNode);\n}\n\nexport function AppShell({\n appData: appDataProp,\n navigation: navigationProp,\n customPages,\n memberTypeCustomPages,\n basePath = \"/\",\n currentSlug: controlledSlug,\n onNavigate: onNavigateProp,\n sidebarHeader,\n sidebarFooter,\n onLogout,\n logoutRedirectUrl,\n errorReporter,\n children,\n}: AppShellProps): React.JSX.Element {\n // Normalize basePath: ensure leading slash, no trailing slash\n const normalizedBasePath = useMemo(() => {\n const stripped = basePath.replace(/^\\/|\\/$/g, \"\");\n return stripped ? `/${stripped}` : \"/\";\n }, [basePath]);\n\n // Fetch full app data from the rendered-manifest endpoint\n // (disabled when appData is provided). The response carries\n // `definition_id` directly, so no separate definition fetch is needed.\n const { data: fetchedAppData, isLoading } = useFluidApp({\n enabled: !appDataProp,\n });\n const appData = appDataProp ?? fetchedAppData;\n\n const { data: account } = useAccount();\n const repUser = useMemo(\n () => ({\n name:\n account?.first_name && account?.last_name\n ? `${account.first_name} ${account.last_name}`\n : null,\n email: account?.email ?? null,\n imageUrl: account?.avatar_url ?? null,\n publicId: account?.public_id,\n }),\n [\n account?.first_name,\n account?.last_name,\n account?.email,\n account?.avatar_url,\n account?.public_id,\n ],\n );\n\n // Resolve the active theme from app data, falling back to the theme-engine\n // default when the manifest has no themes. This guarantees CSS variables\n // are always emitted so the shell renders with proper styling.\n const activeTheme = useMemo(() => {\n const themes = appData?.profile?.themes;\n const activeId = appData?.profile?.activeThemeId;\n if (themes?.length) {\n const matched = activeId\n ? themes.find((t) => t.id === activeId)\n : undefined;\n const first = themes[0];\n if (matched) return matched;\n if (first) return first;\n }\n return getDefaultThemeDefinition();\n }, [appData?.profile?.themes, appData?.profile?.activeThemeId]);\n\n // Apply theme CSS via the shared theme pipeline\n const resolvedTheme = useMemo(() => resolveTheme(activeTheme), [activeTheme]);\n\n useEffect(() => {\n applyTheme(resolvedTheme);\n return () => {\n removeAllThemes();\n };\n }, [resolvedTheme]);\n\n // Mirror data-theme and data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n root.setAttribute(\"data-theme\", activeTheme.id);\n return () => {\n root.removeAttribute(\"data-theme\");\n };\n }, [activeTheme.id]);\n\n // Resolve navigation items\n const navItems = useMemo(() => {\n if (navigationProp) return navigationProp;\n const profileNav = appData?.profile?.navigation?.navigation_items;\n if (profileNav && profileNav.length > 0) return profileNav;\n return getDefaultNavigation();\n }, [navigationProp, appData?.profile?.navigation?.navigation_items]);\n\n // Resolve mobile navigation items (fall back to desktop nav)\n const mobileNavItems = useMemo(() => {\n const mobileNav = appData?.profile?.mobile_navigation?.navigation_items;\n if (mobileNav && mobileNav.length > 0) return mobileNav;\n return navItems;\n }, [appData?.profile?.mobile_navigation?.navigation_items, navItems]);\n\n // Gate rep-only nav on the member type's `rep_eligible` permission rather\n // than the binary customer/rep split, so company-custom member types are\n // filtered by what they're actually allowed to see.\n const canRep = !deniesRepSurfaces(account);\n\n const filteredNavItems = useMemo(\n () => (canRep ? navItems : filterRepOnlyNavItems(navItems)),\n [navItems, canRep],\n );\n\n const filteredMobileNavItems = useMemo(\n () => (canRep ? mobileNavItems : filterRepOnlyNavItems(mobileNavItems)),\n [mobileNavItems, canRep],\n );\n\n // Resolve builder screen definitions\n const screens: ScreenDefinition[] | undefined = appData?.screens;\n\n // Collect all nav slugs for prefix matching (sorted longest-first)\n const navSlugs = useMemo(\n () => collectNavSlugs(filteredNavItems),\n [filteredNavItems],\n );\n\n // Combined desktop + mobile nav slugs for gating system-screen rendering\n // and prefix-matching; a slug present in either nav counts as \"in nav\".\n // Re-sort longest-first after merging — `collectNavSlugs` orders each list\n // individually, but Set insertion order would otherwise let a shorter\n // desktop prefix (e.g. \"home\") win against a more-specific mobile slug\n // (e.g. \"home/news\") in `matchSlugPrefix`.\n const allNavSlugs = useMemo(() => {\n const mobileSlugs = collectNavSlugs(filteredMobileNavItems);\n return Array.from(new Set([...navSlugs, ...mobileSlugs])).sort(\n (a, b) => b.split(\"/\").length - a.split(\"/\").length,\n );\n }, [navSlugs, filteredMobileNavItems]);\n\n // Theme mode state with localStorage persistence\n const [themeMode, setThemeMode] = useState<ThemeMode>(getInitialThemeMode);\n\n const handleThemeModeChange = useCallback((mode: ThemeMode) => {\n setThemeMode(mode);\n if (typeof window !== \"undefined\") {\n localStorage.setItem(THEME_STORAGE_KEY, mode);\n }\n }, []);\n\n // Mirror data-theme-mode onto <html> so that Radix portals\n // (rendered at document.body) inherit the correct CSS custom properties.\n useEffect(() => {\n const root = document.documentElement;\n const mode = themeMode === \"auto\" ? undefined : themeMode;\n if (mode) {\n root.setAttribute(\"data-theme-mode\", mode);\n } else {\n root.removeAttribute(\"data-theme-mode\");\n }\n return () => {\n root.removeAttribute(\"data-theme-mode\");\n };\n }, [themeMode]);\n\n // Set <meta name=\"theme-color\"> so mobile browser chrome matches the theme\n useEffect(() => {\n let meta = document.querySelector<HTMLMetaElement>(\n 'meta[name=\"theme-color\"]',\n );\n if (!meta) {\n meta = document.createElement(\"meta\");\n meta.name = \"theme-color\";\n document.head.appendChild(meta);\n }\n const metaEl = meta;\n\n const theme = resolvedTheme;\n function update(prefersDark: boolean) {\n const mode: PortalNativeThemeMode =\n themeMode === \"auto\" ? (prefersDark ? \"dark\" : \"light\") : themeMode;\n const nativeThemeMessage = createPortalNativeThemeMessage({\n resolvedTheme: theme,\n mode,\n });\n const mutedColor = nativeThemeMessage.payload.colors.muted;\n metaEl.content = mutedColor;\n document.body.style.backgroundColor = mutedColor;\n postPortalNativeThemeMessage(nativeThemeMessage);\n }\n\n const mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n update(mql.matches);\n\n if (themeMode === \"auto\") {\n const handler = (e: MediaQueryListEvent) => update(e.matches);\n mql.addEventListener(\"change\", handler);\n return () => {\n mql.removeEventListener(\"change\", handler);\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }\n\n return () => {\n metaEl.remove();\n document.body.style.backgroundColor = \"\";\n };\n }, [resolvedTheme, themeMode]);\n\n // Path-based routing for uncontrolled mode\n const [pathSlug, setPathSlug] = useState<string>(() => {\n if (typeof window === \"undefined\") return \"\";\n return extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n });\n\n const [previousSlug, setPreviousSlug] = useState<string | null>(null);\n\n // Sync initial path when navItems load — redirect to first nav slug if at root.\n // Wait for app data to load before redirecting; otherwise the fallback\n // default navigation can leak its first slug (e.g. \"shop\") into the URL\n // before the tenant's real navigation arrives.\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n if (typeof window === \"undefined\") return;\n if (!navigationProp && isLoading && !appData) return;\n\n const currentPath = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n\n if (!currentPath) {\n const firstSlug = findFirstNavigableSlug(filteredNavItems);\n if (firstSlug) {\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${firstSlug}`\n : `${normalizedBasePath}/${firstSlug}`;\n // replaceState to avoid polluting back-button history\n history.replaceState(null, \"\", targetPath);\n setPathSlug(firstSlug);\n }\n }\n }, [\n filteredNavItems,\n controlledSlug,\n normalizedBasePath,\n isLoading,\n appData,\n navigationProp,\n ]);\n\n // Listen for popstate (browser back/forward)\n useEffect(() => {\n if (controlledSlug !== undefined) return;\n\n const handlePopState = () => {\n const slug = extractSlugFromPathname(\n window.location.pathname,\n normalizedBasePath,\n );\n setPreviousSlug(null);\n setPathSlug(slug);\n };\n\n window.addEventListener(\"popstate\", handlePopState);\n return () => window.removeEventListener(\"popstate\", handlePopState);\n }, [controlledSlug, normalizedBasePath]);\n\n // Logout hook — always called (rules of hooks) but only wired to UI below\n const logout = useLogout({\n redirectUrl: logoutRedirectUrl,\n onLogout,\n });\n\n const activeSlug = controlledSlug ?? pathSlug;\n\n // Read via ref so handleNavigate stays referentially stable across navigations.\n const activeSlugRef = useRef(activeSlug);\n activeSlugRef.current = activeSlug;\n\n const navigateToSlug = useCallback(\n (slug: string, options?: { recordPrevious?: boolean }) => {\n const current = activeSlugRef.current;\n const recordPrevious = options?.recordPrevious ?? true;\n if (slug !== current && recordPrevious) {\n setPreviousSlug(current || null);\n } else if (slug !== current) {\n setPreviousSlug(null);\n }\n if (onNavigateProp) {\n onNavigateProp(slug);\n return;\n }\n const targetPath =\n normalizedBasePath === \"/\"\n ? `/${slug}`\n : `${normalizedBasePath}/${slug}`;\n history.pushState(null, \"\", targetPath);\n setPathSlug(slug);\n },\n [onNavigateProp, normalizedBasePath],\n );\n\n const handleNavigate = useCallback(\n (slug: string) => {\n navigateToSlug(slug);\n },\n [navigateToSlug],\n );\n\n // Route widget item clicks (table rows, nested cards, etc.) to the matching\n // detail screen. Discriminates first on `shareable_type` for content\n // resources (products, media, pages, libraries), then enrollment packs (no\n // in-portal detail screen — open the BFF-provided canonical_url in a new\n // tab, same pattern as shop bundles in shop-app.tsx), then falls back to\n // subscription/order tokens for the BFF-backed normalized shapes.\n const handleWidgetItemClick = useCallback(\n (item: WidgetInteractionItem) => {\n const subscriptionToken = item.subscription_token;\n if (typeof subscriptionToken === \"string\" && subscriptionToken) {\n handleNavigate(`subscriptions/${subscriptionToken}`);\n return;\n }\n\n const shareableType = item.shareable_type;\n if (typeof shareableType === \"string\") {\n if (shareableType === \"EnrollmentPack\") {\n // share_link server-redirects to the same affiliate URL as canonical_url.\n const canonicalUrl = item.canonical_url;\n const shareLink = item.share_link;\n const externalUrl =\n typeof canonicalUrl === \"string\" && canonicalUrl\n ? canonicalUrl\n : typeof shareLink === \"string\" && shareLink\n ? shareLink\n : null;\n if (externalUrl) {\n window.open(externalUrl, \"_blank\", \"noopener,noreferrer\");\n }\n return;\n }\n if (item.id != null) {\n const slug = SHAREABLE_TYPE_TO_SLUG[shareableType];\n if (slug) {\n handleNavigate(`share/${slug}/${String(item.id)}`);\n return;\n }\n }\n }\n\n const orderToken = item.token;\n if (typeof orderToken === \"string\" && orderToken) {\n handleNavigate(`orders/${orderToken}`);\n }\n },\n [handleNavigate],\n );\n\n // Resolve sidebar footer: explicit prop wins, otherwise render default logout button\n const resolvedSidebarFooter = useMemo(\n () =>\n sidebarFooter !== undefined ? (\n sidebarFooter\n ) : (\n <SdkLogoutButton onLogout={logout} />\n ),\n [sidebarFooter, logout],\n );\n\n // Derive base slug and rest params via prefix matching. Use the combined\n // desktop+mobile slug set so a tap on a mobile-only nav item still resolves\n // to a registered prefix instead of falling through to the raw URL slug.\n const slugMatch = useMemo(\n () => matchSlugPrefix(activeSlug, allNavSlugs),\n [activeSlug, allNavSlugs],\n );\n const baseSlug = slugMatch?.matchedSlug ?? normalizeSlug(activeSlug);\n const restParams = slugMatch?.rest ?? \"\";\n const mobileBackTarget = useMemo(\n () => resolveMobileBackTarget(activeSlug, allNavSlugs, previousSlug),\n [activeSlug, allNavSlugs, previousSlug],\n );\n const handleMobileBack = useCallback(() => {\n if (!mobileBackTarget) return;\n navigateToSlug(mobileBackTarget, { recordPrevious: false });\n }, [mobileBackTarget, navigateToSlug]);\n\n // Derive mobile sub-tabs for the current section\n const mobileCurrentSection = findCurrentSection(\n filteredMobileNavItems,\n baseSlug,\n );\n const mobileSecondLevelTabs = mobileCurrentSection?.children ?? [];\n const currentNavItem =\n findNavItem(filteredNavItems, baseSlug) ??\n findNavItem(filteredMobileNavItems, baseSlug);\n\n // Resolve screen title from nav label or screen definition name\n const matchingScreen = screens?.find(\n (s) => s.id === currentNavItem?.screen_id,\n );\n const screenTitle =\n currentNavItem?.label || matchingScreen?.name || undefined;\n\n const handleScreenError = useCallback(\n (error: Error, info: ErrorInfo) => {\n errorReporter?.(error, {\n boundary: \"screen\",\n componentStack: info.componentStack,\n pathname:\n typeof window === \"undefined\" ? undefined : window.location.pathname,\n activeSlug,\n baseSlug,\n restParams,\n screenTitle,\n navItemSlug: currentNavItem?.slug,\n screenId: currentNavItem?.screen_id ?? matchingScreen?.id,\n });\n },\n [\n errorReporter,\n activeSlug,\n baseSlug,\n restParams,\n screenTitle,\n currentNavItem?.slug,\n currentNavItem?.screen_id,\n matchingScreen?.id,\n ],\n );\n\n // Determine content to render. Wrap in an error boundary keyed on the\n // active slug so a screen-level crash never takes down the navigation\n // chrome — and navigating to a different slug auto-recovers the boundary.\n const rawContent =\n typeof children === \"function\"\n ? children({ currentSlug: activeSlug, currentNavItem })\n : (children ?? (\n <ScreenHeaderProvider>\n <div className=\"flex h-full flex-col\">\n <TranslatedScreenHeader\n slug={currentNavItem?.slug}\n title={screenTitle}\n trailingContent={<LanguageSelector />}\n />\n <div\n className=\"relative isolate min-h-0 flex-1\"\n data-fluid-portal-screen-body=\"\"\n >\n <PageRouter\n currentSlug={activeSlug}\n currentNavItem={currentNavItem}\n customPages={customPages}\n memberTypeCustomPages={memberTypeCustomPages}\n screens={screens}\n baseSlug={baseSlug}\n restParams={restParams}\n navSlugs={allNavSlugs}\n />\n </div>\n </div>\n </ScreenHeaderProvider>\n ));\n\n const content = (\n <AppShellErrorBoundary\n key={activeSlug ?? \"\"}\n onError={errorReporter ? handleScreenError : undefined}\n >\n {rawContent}\n </AppShellErrorBoundary>\n );\n\n // Show loading state while app data is being fetched for the first time.\n // This check is placed after all hooks to satisfy React's rules of hooks.\n if (isLoading && !appData) {\n return <AppShellLoading />;\n }\n\n return (\n <RepUserProvider user={repUser}>\n <ThemeModeProvider\n mode={themeMode}\n onModeChange={handleThemeModeChange}\n autoModeEnabled={false}\n >\n <div\n className=\"font-body\"\n data-theme={activeTheme.id}\n data-theme-mode={themeMode === \"auto\" ? undefined : themeMode}\n >\n <AppNavigationProvider\n currentSlug={activeSlug}\n previousSlug={previousSlug}\n navItems={filteredNavItems}\n basePath={normalizedBasePath}\n navigate={handleNavigate}\n >\n <WidgetInteractionProvider\n onItemClick={handleWidgetItemClick}\n onNavigate={handleNavigate}\n >\n <BottomNavVisibilityProvider>\n <AppShellLayout\n sidebarContent={\n <SdkNavigation\n navItems={filteredNavItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n />\n }\n headerContent={\n <SdkHeader\n mobileTabs={mobileSecondLevelTabs}\n currentSlug={activeSlug}\n title={screenTitle}\n {...(mobileBackTarget !== undefined && {\n onBack: handleMobileBack,\n })}\n onNavigate={handleNavigate}\n onLogout={logout}\n />\n }\n sidebarHeader={\n sidebarHeader !== undefined ? (\n sidebarHeader\n ) : (\n <SdkCompanySwitcher />\n )\n }\n sidebarFooter={resolvedSidebarFooter}\n useBottomNav\n afterContent={\n <SdkBottomNav\n navItems={filteredMobileNavItems}\n currentSlug={activeSlug}\n onNavigate={handleNavigate}\n appDefinitionId={appData?.definition_id ?? 0}\n />\n }\n >\n <ScreenAreaFullscreenProvider>\n {content}\n </ScreenAreaFullscreenProvider>\n </AppShellLayout>\n </BottomNavVisibilityProvider>\n </WidgetInteractionProvider>\n </AppNavigationProvider>\n <Toaster />\n </div>\n </ThemeModeProvider>\n </RepUserProvider>\n );\n}\n","/**\n * Read Vite environment variables at runtime.\n *\n * Isolated into its own module so that Jest tests can mock it\n * (import.meta is a syntax-level construct that CJS/Jest cannot parse).\n */\nexport function getViteEnv(key: string): string | undefined {\n const env = import.meta.env as unknown as Record<string, string> | undefined;\n return env?.[key];\n}\n","import type { FluidSDKConfig } from \"../types/config\";\nimport { getViteEnv } from \"./env\";\n\n/**\n * Creates a FluidSDKConfig with sensible defaults.\n *\n * Default behavior:\n * - baseUrl: reads from `VITE_API_URL` env var, falls back to `\"\"` (same-origin relative)\n * - Auth: relies on session cookies sent via `credentials: 'include'`\n *\n * Pass overrides to customize any field:\n * ```ts\n * const config = createDefaultFluidConfig({ baseUrl: \"https://my-api.example.com\" });\n * ```\n */\nexport function createDefaultFluidConfig(\n overrides?: Partial<FluidSDKConfig>,\n): FluidSDKConfig {\n return {\n baseUrl: getViteEnv(\"VITE_API_URL\") ?? \"\",\n smartyEmbeddedKey: getViteEnv(\"NEXT_PUBLIC_SMARTY_EMBEDDED_KEY\"),\n googleMapsApiKey: getViteEnv(\"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY\"),\n ...overrides,\n };\n}\n","import {\n Component,\n StrictMode,\n type ComponentType,\n type ErrorInfo,\n type ReactNode,\n} from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport type { RemoteDomWidgetPackageDescriptor } from \"@fluid-app/portal-core/remote-dom-widget-package\";\nimport { FluidProvider } from \"../providers/FluidProvider\";\nimport { AppShell, type AppShellProps } from \"../shell/AppShell\";\nimport type { PortalErrorReporter } from \"../errors/portal-error-reporter\";\nimport { createDefaultFluidConfig } from \"../config/defaults\";\nimport type { FluidSDKConfig } from \"../types/config\";\nimport type { MemberTypeCustomPages } from \"@fluid-app/portal-core/member-type-content\";\nimport type { RemoteWidgetOptions } from \"../types/remote-widgets\";\n\n/**\n * Last-resort error boundary for crashes above the AppShell (provider\n * setup, theme/auth bootstrap). Renders above FluidThemeProvider, so the\n * theme CSS custom properties (`--color-foreground`, `--color-muted`, etc.)\n * are not yet applied — using inline styles + literal colors avoids\n * referencing theme tokens that would resolve to nothing.\n */\nclass RootBoundary extends Component<\n { children: ReactNode; errorReporter?: PortalErrorReporter },\n { error: Error | null }\n> {\n override state = { error: null as Error | null };\n\n static getDerivedStateFromError(error: Error): { error: Error } {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: ErrorInfo): void {\n if (this.props.errorReporter) {\n console.warn(\"[portal] Root crashed; reporting error:\", error, info);\n try {\n this.props.errorReporter(error, {\n boundary: \"root\",\n componentStack: info.componentStack,\n pathname:\n typeof window === \"undefined\"\n ? undefined\n : window.location.pathname,\n });\n } catch (callbackError) {\n // A throwing reporter must not mask the original crash.\n console.error(\"[portal] root error reporter threw:\", callbackError);\n console.error(\"[portal] Root crashed:\", error, info);\n }\n return;\n }\n\n console.error(\"[portal] Root crashed:\", error, info);\n }\n\n override render(): ReactNode {\n if (!this.state.error) return this.props.children;\n return (\n <div\n style={{\n minHeight: \"100vh\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: \"1rem\",\n fontFamily: \"system-ui, sans-serif\",\n }}\n >\n <div style={{ maxWidth: \"32rem\", textAlign: \"center\" }}>\n <h1 style={{ fontSize: \"1.25rem\", fontWeight: 600 }}>\n The app failed to load\n </h1>\n <p style={{ marginTop: \"0.5rem\", color: \"#666\" }}>\n Something went wrong while starting the portal. Please reload the\n page; if the problem persists, contact support.\n </p>\n <button\n type=\"button\"\n onClick={() => {\n window.location.reload();\n }}\n style={{\n marginTop: \"1.5rem\",\n padding: \"0.5rem 1rem\",\n borderRadius: \"0.375rem\",\n border: \"1px solid #ccc\",\n background: \"#fff\",\n cursor: \"pointer\",\n }}\n >\n Reload page\n </button>\n </div>\n </div>\n );\n }\n}\n\n/** Configuration for both catalog and explicitly supplied Remote DOM packages. */\nexport interface PortalConfig {\n readonly customPages?: Record<\n string,\n ComponentType<{ slug?: string; params?: string }>\n >;\n /**\n * Per-member-type page overrides: memberTypeSlug -> (pageSlug -> component).\n * Serves different screens to different member types (e.g. an `affiliate`\n * dashboard) at the same slug. Wins over `customPages` for that member type.\n */\n readonly memberTypeCustomPages?: MemberTypeCustomPages;\n readonly remoteWidgets?: RemoteWidgetOptions;\n readonly fluid?: Partial<FluidSDKConfig>;\n readonly rootId?: string;\n readonly shell?: Omit<\n AppShellProps,\n \"customPages\" | \"memberTypeCustomPages\" | \"errorReporter\"\n >;\n readonly providers?: ComponentType<{ children: ReactNode }>;\n readonly errorReporter?: PortalErrorReporter;\n readonly disableStrictMode?: boolean;\n}\n\nexport type { RemoteDomWidgetPackageDescriptor, RemoteWidgetOptions };\n\n/** Mounts a portal with the shared SDK provider and canonical widget catalog. */\nexport function createPortal(config: PortalConfig = {}): void {\n const rootId = config.rootId ?? \"root\";\n const rootElement = document.getElementById(rootId);\n if (!rootElement) {\n throw new Error(`Portal root element \"#${rootId}\" not found`);\n }\n\n let app: ReactNode = (\n <AppShell\n customPages={config.customPages}\n memberTypeCustomPages={config.memberTypeCustomPages}\n errorReporter={config.errorReporter}\n {...config.shell}\n />\n );\n if (config.providers) {\n const Providers = config.providers;\n app = <Providers>{app}</Providers>;\n }\n\n const tree = (\n <RootBoundary errorReporter={config.errorReporter}>\n <FluidProvider\n config={createDefaultFluidConfig(config.fluid)}\n {...(config.remoteWidgets === undefined\n ? {}\n : { remoteWidgets: config.remoteWidgets })}\n >\n {app}\n </FluidProvider>\n </RootBoundary>\n );\n\n createRoot(rootElement).render(\n config.disableStrictMode ? tree : <StrictMode>{tree}</StrictMode>,\n );\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\nimport { transformManifestToRepAppData } from \"../transforms\";\nimport type { RawManifestResponse } from \"../transforms\";\nimport type { Profile } from \"../types/profile\";\nimport { APP_DATA_QUERY_KEY, fetchRawManifest } from \"./use-fluid-app\";\n\n/**\n * Base query key for profile data.\n * Kept for backwards compatibility — the runtime key used by the hook\n * includes a company prefix via {@link useCompanyScopedQueryKey}.\n *\n * @deprecated Use {@link APP_DATA_QUERY_KEY} with `useFluidApp` instead.\n */\nexport const PROFILE_QUERY_KEY = [\"fluid\", \"profile\"] as const;\n\n/**\n * Hook to fetch the portal profile (themes, navigation, screens).\n *\n * Internally fetches from the fluidos API (same data source as\n * {@link useFluidApp}) and selects the `profile` slice, so no\n * legacy `/api/rep_app/manifest` call is made.\n *\n * @deprecated Use `useFluidApp()` instead — it returns the full\n * `RepAppData` including `profile`, `screens`, and more.\n *\n * @example\n * ```tsx\n * function Navigation() {\n * const { data: profile, isLoading } = useFluidProfile();\n *\n * if (isLoading) return <Spinner />;\n *\n * return (\n * <nav>\n * {profile?.navigation.navigation_items.map(item => (\n * <NavItem key={item.id} item={item} />\n * ))}\n * </nav>\n * );\n * }\n * ```\n */\nexport function useFluidProfile(): UseQueryResult<Profile> {\n const appDefinitionApi = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery<\n RawManifestResponse,\n Error,\n Profile,\n readonly (string | number)[]\n >({\n queryKey: scopeKey(APP_DATA_QUERY_KEY),\n queryFn: () => fetchRawManifest(appDefinitionApi),\n select: (raw) => {\n const appData = transformManifestToRepAppData(raw);\n return appData.profile;\n },\n });\n}\n","import { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { useAppDefinitionApi } from \"@fluid-app/portal-core/app-definition-api-context\";\nimport type { AppDefinitionResponse } from \"@fluid-app/portal-core/app-definition-types\";\nimport { useCompanyScopedQueryKey } from \"./query-keys\";\n\n/**\n * Base query key for the app definition endpoint.\n * The runtime key includes a company prefix via {@link useCompanyScopedQueryKey}.\n */\nexport const APP_DEFINITION_QUERY_KEY = [\"fluid\", \"app-definition\"] as const;\n\n/**\n * Hook to fetch the active app manifest from the portal-tenant BFF.\n *\n * Returns the manifest metadata (id, name, version)\n * sourced from `GET /api/app/manifest` via the {@link AppDefinitionApi} port.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { data } = useAppDefinition();\n * const definitionId = data?.definition.id ?? 0;\n * }\n * ```\n */\nexport function useAppDefinition(options?: {\n enabled?: boolean;\n}): UseQueryResult<AppDefinitionResponse> {\n const api = useAppDefinitionApi();\n const { scopeKey } = useCompanyScopedQueryKey();\n\n return useQuery({\n queryKey: scopeKey(APP_DEFINITION_QUERY_KEY),\n queryFn: () => api.fetchDefinition(),\n ...(options?.enabled !== undefined && { enabled: options.enabled }),\n });\n}\n","import { useThemeContext } from \"../providers/FluidThemeProvider\";\nimport type { ThemeDefinition } from \"../types/theme\";\n\n/**\n * Result of useFluidTheme hook\n */\nexport interface UseFluidThemeResult {\n /** Currently active theme */\n currentTheme: ThemeDefinition | null;\n /** Switch to a different theme */\n setTheme: (theme: ThemeDefinition) => void;\n /** Switch between light and dark mode */\n setThemeMode: (mode: \"light\" | \"dark\") => void;\n /** Current theme mode (convenience accessor) */\n mode: \"light\" | \"dark\" | undefined;\n}\n\n/**\n * Hook to access and control theme settings\n *\n * @example\n * ```tsx\n * function ThemeSwitcher({ themes }: { themes: ThemeDefinition[] }) {\n * const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();\n *\n * return (\n * <div>\n * <select\n * value={currentTheme?.name}\n * onChange={(e) => {\n * const theme = themes.find(t => t.name === e.target.value);\n * if (theme) setTheme(theme);\n * }}\n * >\n * {themes.map(theme => (\n * <option key={theme.name} value={theme.name}>\n * {theme.name}\n * </option>\n * ))}\n * </select>\n *\n * <button onClick={() => setThemeMode(mode === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle {mode === \"dark\" ? \"Light\" : \"Dark\"} Mode\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFluidTheme(): UseFluidThemeResult {\n const { currentTheme, setTheme, setThemeMode, mode } = useThemeContext();\n\n return {\n currentTheme,\n setTheme,\n setThemeMode,\n mode,\n };\n}\n","import { useCallback } from \"react\";\nimport type { MemberPermissionKey } from \"@fluid-app/portal-core/account-types\";\nimport {\n canAccessRepSurfaces,\n hasMemberPermission,\n memberTypeName,\n memberTypeSlug,\n} from \"@fluid-app/portal-core/member-permissions\";\nimport { useAccount } from \"./use-account\";\n\nexport interface MemberTypeInfo {\n /** Real per-company member-type slug (rep/customer/affiliate/...), or null while loading. */\n slug: string | null;\n /** Admin-facing display label (e.g. \"Affiliate\"), or null when unknown. */\n name: string | null;\n /** Whether the member may access rep-only surfaces (rep_eligible). */\n canAccessRepSurfaces: boolean;\n /** Predicate for any member-type permission key. */\n can: (key: MemberPermissionKey) => boolean;\n /** True until the account query resolves. */\n isLoading: boolean;\n}\n\n/**\n * Resolve the current member's type identity and capabilities.\n *\n * The single hook UI should use to display the member type (name/slug) and\n * to gate behavior on permissions, instead of comparing the binary\n * `member_type` string. Backed by the same cached `useAccount()` query.\n */\nexport function useMemberType(): MemberTypeInfo {\n const { data: account, isLoading } = useAccount();\n\n // Stable reference so consumers can safely list `can` in dependency arrays.\n const can = useCallback(\n (key: MemberPermissionKey) => hasMemberPermission(account, key),\n [account],\n );\n\n return {\n slug: memberTypeSlug(account),\n name: memberTypeName(account),\n canAccessRepSurfaces: canAccessRepSurfaces(account),\n can,\n isLoading,\n };\n}\n","import { useCallback, useRef } from \"react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { deleteDatabase } from \"@fluid-app/query-persister\";\nimport { usePortalTenantClient } from \"../providers/PortalTenantClientProvider\";\nimport { resetFairShareSdk } from \"../utils/reset-fairshare-sdk\";\n\nexport interface UseCompanySwitchOptions {\n // Preserved for API compatibility; no longer used internally.\n}\n\n/**\n * Hook that handles company switching for the portal.\n *\n * Calls PUT /api/authentication/company/{id}/switch, clears the TanStack\n * Query cache and IndexedDB persisted cache, then reloads the page so all\n * queries re-fetch with the new company context.\n *\n * The server updates the session cookie automatically via the switch\n * endpoint response; no client-side token storage is needed.\n */\nexport function useCompanySwitch(_options?: UseCompanySwitchOptions): {\n switchCompany: (companyId: number) => void;\n isPending: boolean;\n isError: boolean;\n error: Error | null;\n} {\n const client = usePortalTenantClient();\n const queryClient = useQueryClient();\n const switchingRef = useRef(false);\n\n const { mutate, isPending, isError, error } = useMutation({\n mutationFn: (companyId: number) =>\n client.put(`/api/authentication/company/${companyId}/switch`),\n onSuccess: async () => {\n // Clear in-memory cache and IndexedDB to prevent stale data\n // rehydration after reload.\n queryClient.clear();\n try {\n await deleteDatabase(\"company-switch\");\n } catch {\n // Non-fatal: company-scoped query keys prevent cross-company reads\n // even if the IDB delete fails.\n }\n // Reset the FairShare SDK's shopping identity so the new company's\n // session doesn't inherit the previous membership's cart/customer\n // (the SDK's cart/session cookies aren't company-scoped) — CURRENT-2180.\n resetFairShareSdk();\n\n // Session cookie is updated server-side by the switch endpoint.\n // Reload to re-fetch all queries with the new company context.\n window.location.reload();\n },\n onError: () => {\n switchingRef.current = false;\n },\n });\n\n const switchCompany = useCallback(\n (companyId: number) => {\n if (switchingRef.current) return;\n switchingRef.current = true;\n mutate(companyId);\n },\n [mutate],\n );\n\n return {\n switchCompany: switchCompany,\n isPending: isPending,\n isError: isError,\n error: error,\n };\n}\n","import type { CalendarEvent } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number, hour: number, minute: number = 0): Date {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n d.setHours(hour, minute, 0, 0);\n return d;\n}\n\nexport const DEMO_CALENDAR_EVENTS: readonly CalendarEvent[] = [\n {\n id: 1,\n title: \"Team Standup\",\n description: { body: \"Daily sync with the sales team\" },\n color: \"#4f46e5\",\n start: daysFromNow(0, 9, 0).toISOString(),\n end: daysFromNow(0, 9, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n },\n {\n id: 2,\n title: \"Client Review - Acme Corp\",\n description: { body: \"Quarterly business review with key accounts\" },\n color: \"#059669\",\n start: daysFromNow(2, 14, 0).toISOString(),\n end: daysFromNow(2, 15, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"Conference Room B\",\n },\n {\n id: 3,\n title: \"Product Training Webinar\",\n description: { body: \"New product line training for the field\" },\n color: \"#d97706\",\n start: daysFromNow(4, 11, 0).toISOString(),\n end: daysFromNow(4, 12, 30).toISOString(),\n active: true,\n status: \"confirmed\",\n url: \"https://example.com/webinar\",\n },\n {\n id: 4,\n title: \"Regional Conference\",\n description: { body: \"Annual West Coast regional conference\" },\n color: \"#dc2626\",\n start: daysFromNow(10, 8, 0).toISOString(),\n end: daysFromNow(12, 17, 0).toISOString(),\n active: true,\n status: \"confirmed\",\n venue: \"San Diego Convention Center\",\n isAllDay: true,\n },\n];\n","/**\n * Calendar events hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CalendarEvent, CalendarEventDescription } from \"./hook-types\";\nimport { DEMO_CALENDAR_EVENTS } from \"./demo-data/calendar-events\";\n\n// Re-export types so external consumers are unaffected\nexport type { CalendarEvent, CalendarEventDescription };\n\n/**\n * Result type for useCalendarEvents hook.\n * Uses QueryResult<CalendarEvent[]> with default Error type.\n */\nexport type UseCalendarEventsResult = QueryResult<CalendarEvent[]>;\n\n/**\n * Hook to fetch calendar events.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCalendarEvents(): UseCalendarEventsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CALENDAR_EVENTS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { Todo } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction daysFromNow(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() + days);\n return d.toISOString();\n}\n\nexport const DEMO_TODOS: readonly Todo[] = [\n {\n id: 1,\n body: \"Follow up with Sarah about Q2 order\",\n dueAt: daysFromNow(1),\n completedAt: null,\n createdAt: daysFromNow(-2),\n contactName: \"Sarah Johnson\",\n },\n {\n id: 2,\n body: \"Send pricing proposal to Acme Corp\",\n dueAt: daysFromNow(7),\n completedAt: null,\n createdAt: daysFromNow(-1),\n contactName: \"Mike Chen\",\n },\n {\n id: 3,\n body: \"Schedule onboarding call with new team member\",\n dueAt: daysFromNow(3),\n completedAt: null,\n createdAt: daysFromNow(0),\n contactName: \"Emily Rodriguez\",\n },\n {\n id: 4,\n body: \"Review and approve Q1 expense report\",\n dueAt: daysFromNow(-1),\n completedAt: daysFromNow(-1),\n createdAt: daysFromNow(-5),\n contactName: null,\n },\n {\n id: 5,\n body: \"Prepare presentation for Friday team meeting\",\n dueAt: daysFromNow(4),\n completedAt: daysFromNow(0),\n createdAt: daysFromNow(-3),\n contactName: null,\n },\n];\n","/**\n * Todos hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { Todo } from \"./hook-types\";\nimport { DEMO_TODOS } from \"./demo-data/todos\";\n\n// Re-export type so external consumers are unaffected\nexport type { Todo };\n\n/**\n * Result type for useTodos hook.\n * Uses QueryResult<Todo[]> with default Error type.\n */\nexport type UseTodosResult = QueryResult<Todo[]>;\n\n/**\n * Hook to fetch todo items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useTodos(): UseTodosResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_TODOS],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Hook type utilities and type predicates.\n *\n * This module provides:\n * - Generic hook result types with default type parameters\n * - Type predicates for query state narrowing\n * - Reusable patterns for type-safe property access in hooks\n *\n * Following generics best practices:\n * - generics-default-type-parameters: Default E to Error for common case\n * - generics-type-predicates: Type predicates for result state narrowing\n * - generics-constrain-type-parameters: K extends keyof T for property access\n */\n\n// =============================================================================\n// Base Result Types with Default Type Parameters\n// =============================================================================\n\n/**\n * Base result type for query hooks with default error type.\n * Uses default type parameter for E (generics-default-type-parameters rule).\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n *\n * @example\n * // Error type defaults to Error\n * type UsersResult = QueryResult<User[]>;\n *\n * // Can override when needed\n * type CustomResult = QueryResult<User[], ApiError>;\n */\nexport interface QueryResult<T, E = Error> {\n readonly data: T;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for hooks that may not have data yet.\n * Extends QueryResult with nullable data.\n *\n * @typeParam T - The data type\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface QueryResultNullable<T, E = Error> {\n readonly data: T | null | undefined;\n readonly isLoading: boolean;\n readonly isError: boolean;\n readonly error?: E | undefined;\n}\n\n/**\n * Result type for list/collection hooks with aggregates.\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ListQueryResult<T, E = Error> extends QueryResult<T[], E> {\n readonly totalCount: number;\n}\n\n/**\n * Result type for list hooks with value aggregation (e.g., deals with total value).\n *\n * @typeParam T - The item type in the array\n * @typeParam E - The error type (defaults to Error)\n */\nexport interface ValueListQueryResult<T, E = Error> extends ListQueryResult<\n T,\n E\n> {\n readonly totalValue: number;\n}\n\n// =============================================================================\n// Type Predicates for Query State Narrowing\n// =============================================================================\n\n/**\n * Type predicate to check if a query result has successfully loaded data.\n * Narrows the data type from T | null | undefined to T.\n *\n * @example\n * const result = useContact(id);\n * if (hasData(result)) {\n * // TypeScript knows result.data is Contact, not Contact | null\n * console.log(result.data.name);\n * }\n */\nexport function hasData<T, E = Error>(\n result: QueryResultNullable<T, E>,\n): result is QueryResultNullable<T, E> & { readonly data: T } {\n return result.data != null && !result.isLoading && !result.isError;\n}\n\n/**\n * Type predicate to check if a query result is in loading state.\n * Useful for conditional rendering.\n *\n * @example\n * if (isLoading(result)) {\n * return <Spinner />;\n * }\n */\nexport function isLoading<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return result.isLoading;\n}\n\n/**\n * Type predicate to check if a query result has an error.\n * Narrows to include the error property.\n *\n * @example\n * if (isErrorResult(result)) {\n * console.error(result.error); // error is E, not undefined\n * }\n */\nexport function isErrorResult<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): result is (QueryResult<T, E> | QueryResultNullable<T, E>) & {\n readonly isError: true;\n readonly error: E;\n} {\n return result.isError && result.error !== undefined;\n}\n\n/**\n * Type predicate to check if a query result is in idle state (not loading, no error, has data).\n *\n * @example\n * if (isIdle(result)) {\n * // Safe to access and render data\n * }\n */\nexport function isIdle<T, E = Error>(\n result: QueryResult<T, E> | QueryResultNullable<T, E>,\n): boolean {\n return !result.isLoading && !result.isError;\n}\n\n// =============================================================================\n// Generic Property Selector Pattern\n// =============================================================================\n\n/**\n * Type-safe property selector for hook results.\n * Uses K extends keyof T constraint (generics-function-constraints rule).\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n *\n * @example\n * const users = [{ name: \"Alice\", age: 30 }];\n * const names = selectProperty(users, \"name\"); // string[]\n * const ages = selectProperty(users, \"age\"); // number[]\n * selectProperty(users, \"invalid\"); // Error: \"invalid\" not in \"name\" | \"age\"\n */\nexport function selectProperty<T, K extends keyof T>(\n items: readonly T[],\n key: K,\n): T[K][] {\n return items.map((item) => item[key]);\n}\n\n/**\n * Type-safe property getter for a single item.\n * Returns undefined if item is null/undefined.\n *\n * @typeParam T - The data object type\n * @typeParam K - Key of T (constrained to actual keys)\n */\nexport function getProperty<T, K extends keyof T>(\n item: T | null | undefined,\n key: K,\n): T[K] | undefined {\n return item?.[key];\n}\n\n// =============================================================================\n// Hook Factory Types\n// =============================================================================\n\n/**\n * Generic type for hooks that fetch a single resource by ID.\n * Useful for creating consistent API across different resource types.\n *\n * @typeParam T - The resource type\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseSingleResourceHook<T, E = Error> = (\n id: string,\n) => QueryResultNullable<T, E>;\n\n/**\n * Generic type for hooks that fetch a list of resources with optional params.\n * Uses generics-default-type-parameters for the params type.\n *\n * @typeParam T - The item type\n * @typeParam P - The params type (defaults to empty object)\n * @typeParam E - The error type (defaults to Error)\n */\nexport type UseListResourceHook<\n T,\n P extends Record<string, unknown> = Record<string, never>,\n E = Error,\n> = (params?: P) => ListQueryResult<T, E>;\n\n/**\n * Transforms a nullable result to a non-nullable one if data exists.\n * Useful when you've already checked hasData().\n */\nexport type WithData<R extends QueryResultNullable<unknown>> =\n R extends QueryResultNullable<infer T, infer E>\n ? QueryResultNullable<T, E> & { readonly data: T }\n : never;\n\n// =============================================================================\n// Domain Types\n// Shared between hook files and demo-data files to avoid circular imports.\n// =============================================================================\n\n/**\n * Activity slug constants as a const object.\n * Derive the ActivitySlug type from this single source of truth.\n */\nexport const ACTIVITY_SLUGS = {\n abandonedCart: \"abandoned_cart\",\n announcements: \"announcements\",\n cartItemsAdded: \"cart_items_added\",\n commentReply: \"comment_reply\",\n directMessage: \"direct_message\",\n fantasyPoint: \"fantasy_point\",\n newLead: \"new_lead\",\n orderPlaced: \"order_placed\",\n pageViews: \"page_views\",\n pageViewsContact: \"page_views_contact\",\n tasks: \"tasks\",\n upcomingEvent: \"upcoming_event\",\n video: \"video\",\n videoComplete: \"video_complete\",\n videoCompleteContact: \"video_complete_contact\",\n videoContact: \"video_contact\",\n messageReceived: \"message_received\",\n messageSent: \"message_sent\",\n newCartItemsAdded: \"new_cart_items_added\",\n smartLinkClicked: \"smart_link_clicked\",\n reviewLeft: \"review_left\",\n} as const;\n\n/** Activity slug union type derived from ACTIVITY_SLUGS constant. */\nexport type ActivitySlug = (typeof ACTIVITY_SLUGS)[keyof typeof ACTIVITY_SLUGS];\n\n/** Type predicate to check if a string is a valid ActivitySlug. */\nexport function isActivitySlug(value: string): value is ActivitySlug {\n return Object.values(ACTIVITY_SLUGS).includes(value as ActivitySlug);\n}\n\n/** Transformed activity for display. */\nexport interface Activity {\n readonly id: number;\n readonly userName: string;\n readonly avatarUrl: string | null;\n readonly activityType: string;\n readonly targetName: string;\n readonly timestamp: string;\n readonly slug: ActivitySlug;\n}\n\n/** Description/rich text metadata for a calendar event. */\nexport interface CalendarEventDescription {\n readonly id?: number | null;\n readonly name?: string | null;\n readonly body?: string | null;\n readonly record_type?: string | null;\n readonly record_id?: number | null;\n readonly created_at?: string | null;\n readonly updated_at?: string | null;\n readonly locale?: string | null;\n}\n\n/** Calendar event data from the API. */\nexport interface CalendarEvent {\n readonly id: number;\n readonly title: string;\n readonly description?: CalendarEventDescription | null;\n readonly color?: string | null;\n readonly url?: string | null;\n readonly start: string;\n readonly end: string;\n readonly active?: boolean | null;\n readonly time_zone?: string | null;\n readonly status?: string | null;\n readonly image_url?: string | null;\n readonly images?: readonly unknown[] | null;\n readonly venue?: string | null;\n readonly countries?: readonly string[] | null;\n readonly hasTomorrow?: boolean | null;\n readonly hasYesterday?: boolean | null;\n readonly isAllDay?: boolean;\n}\n\n/** Catch up suggestion data from the API. */\nexport interface CatchUp {\n readonly id: number;\n readonly suggestion_title: string;\n}\n\n/** MySite data returned by the hook. */\nexport interface MySiteData {\n readonly url: string | null;\n readonly views: number;\n readonly leads: number;\n readonly userName: string;\n}\n\n/** Transformed todo for display. */\nexport interface Todo {\n readonly id: number;\n readonly body: string;\n readonly dueAt: string | null;\n readonly completedAt: string | null;\n readonly createdAt: string;\n readonly contactName: string | null;\n}\n","import type { Activity } from \"../hook-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\nexport const DEMO_ACTIVITIES: readonly Activity[] = [\n {\n id: 1,\n userName: \"Sarah Johnson\",\n avatarUrl: null,\n activityType: \"New Order\",\n targetName: \"Wellness Starter Kit\",\n timestamp: hoursAgo(1),\n slug: \"order_placed\",\n },\n {\n id: 2,\n userName: \"Mike Chen\",\n avatarUrl: null,\n activityType: \"New Lead\",\n targetName: \"Referral from LinkedIn campaign\",\n timestamp: hoursAgo(3),\n slug: \"new_lead\",\n },\n {\n id: 3,\n userName: \"Emily Rodriguez\",\n avatarUrl: null,\n activityType: \"Page View\",\n targetName: \"Product Catalog\",\n timestamp: hoursAgo(5),\n slug: \"page_views\",\n },\n {\n id: 4,\n userName: \"David Park\",\n avatarUrl: null,\n activityType: \"Cart Items Added\",\n targetName: \"Premium Bundle (3 items)\",\n timestamp: hoursAgo(8),\n slug: \"cart_items_added\",\n },\n {\n id: 5,\n userName: \"Lisa Thompson\",\n avatarUrl: null,\n activityType: \"Video Watched\",\n targetName: \"Getting Started Tutorial\",\n timestamp: hoursAgo(12),\n slug: \"video_complete\",\n },\n {\n id: 6,\n userName: \"James Wilson\",\n avatarUrl: null,\n activityType: \"Message Received\",\n targetName: \"Question about shipping\",\n timestamp: hoursAgo(18),\n slug: \"message_received\",\n },\n {\n id: 7,\n userName: \"Rachel Kim\",\n avatarUrl: null,\n activityType: \"Smart Link Clicked\",\n targetName: \"Holiday Promo Link\",\n timestamp: hoursAgo(24),\n slug: \"smart_link_clicked\",\n },\n {\n id: 8,\n userName: \"Tom Martinez\",\n avatarUrl: null,\n activityType: \"Review Left\",\n targetName: \"Essential Oil Set - 5 stars\",\n timestamp: hoursAgo(36),\n slug: \"review_left\",\n },\n];\n","/**\n * Activities hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport {\n ACTIVITY_SLUGS,\n isActivitySlug,\n type Activity,\n type ActivitySlug,\n} from \"./hook-types\";\nimport { DEMO_ACTIVITIES } from \"./demo-data/activities\";\n\n// Re-export types and constants so external consumers are unaffected\nexport { ACTIVITY_SLUGS, isActivitySlug };\nexport type { Activity, ActivitySlug };\n\n/**\n * Result type for useActivities hook.\n * Uses QueryResult generic with the activities + optional windowed totalCount.\n */\nexport type UseActivitiesData = {\n activities: Activity[];\n totalCount?: number;\n};\nexport type UseActivitiesResult = QueryResult<UseActivitiesData>;\n\n/**\n * Hook to fetch recent activities. The optional `daysAgo` arg windows the\n * result (e.g. `useActivities({ daysAgo: 7 })`) and surfaces the windowed\n * total via `data.totalCount` — used by the Recent Activity widget chip.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useActivities(_args?: {\n daysAgo?: number;\n}): UseActivitiesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: {\n activities: [...DEMO_ACTIVITIES],\n totalCount: DEMO_ACTIVITIES.length,\n },\n isLoading: false,\n isError: false,\n };\n}\n","import type { CatchUp } from \"../hook-types\";\n\nexport const DEMO_CATCHUPS: readonly CatchUp[] = [\n {\n id: 1,\n suggestion_title: \"Sarah Johnson hasn't ordered in 30 days\",\n },\n {\n id: 2,\n suggestion_title: \"Mike Chen's subscription is expiring soon\",\n },\n {\n id: 3,\n suggestion_title: \"Emily Rodriguez left a cart with 3 items\",\n },\n];\n","/**\n * Catch ups hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResult } from \"./hook-types\";\nimport type { CatchUp } from \"./hook-types\";\nimport { DEMO_CATCHUPS } from \"./demo-data/catchups\";\n\n// Re-export type so external consumers are unaffected\nexport type { CatchUp };\n\n/**\n * Result type for useCatchUps hook.\n * Uses QueryResult<CatchUp[]> with default Error type.\n */\nexport type UseCatchUpsResult = QueryResult<CatchUp[]>;\n\n/**\n * Hook to fetch catch up items.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useCatchUps(): UseCatchUpsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CATCHUPS],\n isLoading: false,\n isError: false,\n };\n}\n","import type { MySiteData } from \"../hook-types\";\n\nexport const DEMO_MYSITE: MySiteData = {\n url: \"https://my-portal.example.com\",\n views: 1243,\n leads: 37,\n userName: \"Demo User\",\n};\n","/**\n * MySite hook - stub implementation for SDK\n * In production, implement this hook to fetch from your API\n */\n\nimport type { QueryResultNullable } from \"./hook-types\";\nimport type { MySiteData } from \"./hook-types\";\nimport { DEMO_MYSITE } from \"./demo-data/mysite\";\n\n// Re-export type so external consumers are unaffected\nexport type { MySiteData };\n\n/**\n * Result type for useMySite hook.\n * Uses QueryResultNullable since MySite data may not be available.\n */\nexport type UseMySiteResult = QueryResultNullable<MySiteData>;\n\n/**\n * Hook to fetch MySite data.\n * This is a stub implementation - override with your own data fetching logic.\n */\nexport function useMySite(): UseMySiteResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: DEMO_MYSITE,\n isLoading: false,\n isError: false,\n };\n}\n","import type { Conversation, Message } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction hoursAgo(hours: number): string {\n const d = new Date(now);\n d.setHours(d.getHours() - hours);\n return d.toISOString();\n}\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONVERSATIONS: readonly Conversation[] = [\n {\n id: \"conv-1\",\n title: \"Sarah Johnson\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-2\",\n name: \"Sarah Johnson\",\n email: \"sarah.johnson@example.com\",\n isOnline: true,\n },\n ],\n lastMessage: {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n unreadCount: 1,\n status: \"active\",\n createdAt: daysAgo(30),\n updatedAt: hoursAgo(1),\n },\n {\n id: \"conv-2\",\n title: \"Mike Chen\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-3\",\n name: \"Mike Chen\",\n email: \"mike.chen@acmecorp.com\",\n isOnline: false,\n },\n ],\n lastMessage: {\n id: \"msg-2-2\",\n conversationId: \"conv-2\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"I've attached the updated pricing sheet. Let me know if you have any questions!\",\n timestamp: hoursAgo(5),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(14),\n updatedAt: hoursAgo(5),\n },\n {\n id: \"conv-3\",\n title: \"Team Updates\",\n participants: [\n { id: \"user-1\", name: \"You\", email: \"me@example.com\", isOnline: true },\n {\n id: \"user-4\",\n name: \"Emily Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n },\n { id: \"user-5\", name: \"David Park\", email: \"david.park@email.com\" },\n ],\n lastMessage: {\n id: \"msg-3-4\",\n conversationId: \"conv-3\",\n senderId: \"user-4\",\n senderName: \"Emily Rodriguez\",\n type: \"text\",\n content:\n \"The new product samples arrived today. Will distribute to the team tomorrow.\",\n timestamp: daysAgo(1),\n isRead: true,\n },\n unreadCount: 0,\n status: \"active\",\n createdAt: daysAgo(60),\n updatedAt: daysAgo(1),\n },\n];\n\nexport const DEMO_MESSAGES: readonly Message[] = [\n {\n id: \"msg-1-1\",\n conversationId: \"conv-1\",\n senderId: \"user-1\",\n senderName: \"You\",\n type: \"text\",\n content:\n \"Hi Sarah! I wanted to follow up on the Wellness Starter Kit. We have a special promotion running this month.\",\n timestamp: hoursAgo(3),\n isRead: true,\n },\n {\n id: \"msg-1-2\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content:\n \"That's perfect timing! I was just thinking about reordering. What's the promo?\",\n timestamp: hoursAgo(2),\n isRead: true,\n },\n {\n id: \"msg-1-3\",\n conversationId: \"conv-1\",\n senderId: \"user-2\",\n senderName: \"Sarah Johnson\",\n type: \"text\",\n content: \"Sounds great! I'll place the order by end of day.\",\n timestamp: hoursAgo(1),\n isRead: false,\n },\n];\n","/**\n * Conversations hooks - stub implementations for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Conversation, Message } from \"../types/screen-types\";\nimport type { QueryResult } from \"./hook-types\";\nimport { DEMO_CONVERSATIONS, DEMO_MESSAGES } from \"./demo-data/conversations\";\n\n// =============================================================================\n// useConversations Hook\n// =============================================================================\n\n/**\n * Result type for useConversations hook.\n * Uses QueryResult<Conversation[]> with default Error type.\n */\nexport type UseConversationsResult = QueryResult<Conversation[]>;\n\n/**\n * Hook to fetch all conversations.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @returns UseConversationsResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: conversations, isLoading, isError } = useConversations();\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return conversations.map(conv => <ConversationItem key={conv.id} {...conv} />);\n * ```\n */\nexport function useConversations(): UseConversationsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONVERSATIONS],\n isLoading: false,\n isError: false,\n };\n}\n\n// =============================================================================\n// useConversationMessages Hook\n// =============================================================================\n\n/**\n * Result type for useConversationMessages hook.\n * Uses QueryResult<Message[]> with default Error type.\n */\nexport type UseConversationMessagesResult = QueryResult<Message[]>;\n\n/**\n * Hook to fetch messages for a specific conversation.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param conversationId - The ID of the conversation to fetch messages for\n * @returns UseConversationMessagesResult with empty data array\n *\n * @example\n * ```tsx\n * const { data: messages, isLoading, isError } = useConversationMessages(conversationId);\n *\n * if (isLoading) return <Loading />;\n * if (isError) return <Error />;\n *\n * return messages.map(msg => <MessageBubble key={msg.id} {...msg} />);\n * ```\n */\nexport function useConversationMessages(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _conversationId: string,\n): UseConversationMessagesResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _conversationId\n return {\n data: [...DEMO_MESSAGES],\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Screen Types - Type definitions for core feature screens\n *\n * All status and type unions are derived from constants for single source of truth.\n * Use the constants (e.g., CONVERSATION_STATUSES.active) for type-safe comparisons.\n */\n\n// =============================================================================\n// Messaging Types\n// =============================================================================\n\n/**\n * Conversation status constant - single source of truth.\n */\nexport const CONVERSATION_STATUSES = {\n active: \"active\",\n archived: \"archived\",\n muted: \"muted\",\n} as const;\n\n/**\n * Union type derived from CONVERSATION_STATUSES constant.\n */\nexport type ConversationStatus =\n (typeof CONVERSATION_STATUSES)[keyof typeof CONVERSATION_STATUSES];\n\n/**\n * Message type constant - single source of truth.\n */\nexport const MESSAGE_TYPES = {\n text: \"text\",\n image: \"image\",\n file: \"file\",\n system: \"system\",\n} as const;\n\n/**\n * Union type derived from MESSAGE_TYPES constant.\n */\nexport type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];\n\nexport interface Participant {\n readonly id: string;\n readonly name: string;\n readonly email: string;\n readonly avatarUrl?: string;\n readonly isOnline?: boolean;\n}\n\n/**\n * Message attachment type - extracted for reusability and clarity.\n */\nexport interface MessageAttachment {\n readonly id: string;\n readonly name: string;\n readonly url: string;\n readonly type: string;\n readonly size?: number;\n}\n\nexport interface Message {\n readonly id: string;\n readonly conversationId: string;\n readonly senderId: string;\n readonly senderName: string;\n readonly senderAvatarUrl?: string;\n readonly type: MessageType;\n readonly content: string;\n readonly timestamp: string;\n readonly isRead: boolean;\n readonly attachments?: readonly MessageAttachment[];\n}\n\nexport interface Conversation {\n readonly id: string;\n readonly title: string;\n readonly participants: readonly Participant[];\n readonly lastMessage?: Message;\n readonly unreadCount: number;\n readonly status: ConversationStatus;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n\n// =============================================================================\n// Contacts Types\n// =============================================================================\n\n/**\n * Contact status constant - single source of truth.\n */\nexport const CONTACT_STATUSES = {\n active: \"active\",\n inactive: \"inactive\",\n lead: \"lead\",\n prospect: \"prospect\",\n} as const;\n\n/**\n * Union type derived from CONTACT_STATUSES constant.\n */\nexport type ContactStatus =\n (typeof CONTACT_STATUSES)[keyof typeof CONTACT_STATUSES];\n\n/**\n * Contact type constant - single source of truth.\n */\nexport const CONTACT_TYPES = {\n individual: \"individual\",\n company: \"company\",\n} as const;\n\n/**\n * Union type derived from CONTACT_TYPES constant.\n */\nexport type ContactType = (typeof CONTACT_TYPES)[keyof typeof CONTACT_TYPES];\n\nexport interface ContactAddress {\n readonly street?: string;\n readonly city?: string;\n readonly state?: string;\n readonly postalCode?: string;\n readonly country?: string;\n}\n\nexport interface Contact {\n readonly id: string;\n readonly firstName: string;\n readonly lastName: string;\n readonly email: string;\n readonly phone?: string;\n readonly company?: string;\n readonly jobTitle?: string;\n readonly avatarUrl?: string;\n readonly status: ContactStatus;\n readonly type: ContactType;\n readonly address?: ContactAddress;\n readonly tags?: readonly string[];\n readonly notes?: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n}\n","import type { Contact } from \"../../types/screen-types\";\n\nconst now = new Date();\n\nfunction daysAgo(days: number): string {\n const d = new Date(now);\n d.setDate(d.getDate() - days);\n return d.toISOString();\n}\n\nexport const DEMO_CONTACTS: readonly Contact[] = [\n {\n id: \"contact-1\",\n firstName: \"Sarah\",\n lastName: \"Johnson\",\n email: \"sarah.johnson@example.com\",\n phone: \"+1 (555) 123-4567\",\n company: \"Wellness Works Inc.\",\n jobTitle: \"Purchasing Manager\",\n status: \"active\",\n type: \"individual\",\n tags: [\"VIP\", \"Repeat Buyer\"],\n createdAt: daysAgo(90),\n updatedAt: daysAgo(2),\n },\n {\n id: \"contact-2\",\n firstName: \"Mike\",\n lastName: \"Chen\",\n email: \"mike.chen@acmecorp.com\",\n phone: \"+1 (555) 234-5678\",\n company: \"Acme Corp\",\n jobTitle: \"Director of Operations\",\n status: \"active\",\n type: \"individual\",\n tags: [\"Enterprise\"],\n createdAt: daysAgo(60),\n updatedAt: daysAgo(5),\n },\n {\n id: \"contact-3\",\n firstName: \"Emily\",\n lastName: \"Rodriguez\",\n email: \"emily.r@healthfirst.io\",\n company: \"HealthFirst\",\n status: \"lead\",\n type: \"individual\",\n notes: \"Interested in bulk pricing for team orders\",\n createdAt: daysAgo(7),\n updatedAt: daysAgo(1),\n },\n {\n id: \"contact-4\",\n firstName: \"David\",\n lastName: \"Park\",\n email: \"david.park@email.com\",\n phone: \"+1 (555) 345-6789\",\n status: \"prospect\",\n type: \"individual\",\n tags: [\"Referral\"],\n createdAt: daysAgo(14),\n updatedAt: daysAgo(10),\n },\n {\n id: \"contact-5\",\n firstName: \"Lisa\",\n lastName: \"Thompson\",\n email: \"lisa.t@globalfit.com\",\n company: \"Global Fitness\",\n jobTitle: \"CEO\",\n status: \"active\",\n type: \"individual\",\n address: {\n city: \"Los Angeles\",\n state: \"CA\",\n country: \"US\",\n },\n createdAt: daysAgo(120),\n updatedAt: daysAgo(15),\n },\n {\n id: \"contact-6\",\n firstName: \"James\",\n lastName: \"Wilson\",\n email: \"jwilson@retired.net\",\n status: \"inactive\",\n type: \"individual\",\n notes: \"Moved out of area, no longer purchasing\",\n createdAt: daysAgo(200),\n updatedAt: daysAgo(45),\n },\n];\n\nexport const DEMO_CONTACT: Contact = DEMO_CONTACTS[0]!;\n","/**\n * Contacts hooks - stub implementation for SDK\n * In production, implement these hooks to fetch from your API\n */\n\nimport type { Contact, ContactStatus } from \"../types/screen-types\";\nimport { CONTACT_STATUSES } from \"../types/screen-types\";\nimport type { ListQueryResult, QueryResultNullable } from \"./hook-types\";\nimport { DEMO_CONTACTS, DEMO_CONTACT } from \"./demo-data/contacts\";\n\n/**\n * Type predicate to check if a status string is a valid ContactStatus.\n * Enables runtime validation with type narrowing.\n */\nexport function isContactStatus(value: string): value is ContactStatus {\n return Object.values(CONTACT_STATUSES).includes(value as ContactStatus);\n}\n\n/**\n * Parameters for filtering and paginating contacts.\n * Uses readonly properties and proper ContactStatus type for status.\n */\nexport interface UseContactsParams {\n /** Search query to filter contacts by name, email, or company */\n readonly search?: string;\n /** Filter contacts by status - uses ContactStatus union type for type safety */\n readonly status?: ContactStatus;\n /** Maximum number of contacts to return */\n readonly limit?: number;\n}\n\n/**\n * Result type for the useContacts hook.\n * Uses ListQueryResult<Contact> with totalCount and default Error type.\n */\nexport type UseContactsResult = ListQueryResult<Contact>;\n\n/**\n * Result type for the useContact hook.\n * Uses QueryResultNullable since a specific contact may not exist.\n */\nexport type UseContactResult = QueryResultNullable<Contact>;\n\n/**\n * Hook to fetch a list of contacts with optional filtering and pagination.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param params - Optional parameters for filtering and pagination\n * @returns Object containing contacts data, loading state, error state, and total count\n *\n * @example\n * ```tsx\n * const { data: contacts, isLoading, totalCount } = useContacts({\n * search: 'john',\n * status: 'active',\n * limit: 20\n * });\n * ```\n */\nexport function useContacts(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _params?: UseContactsParams,\n): UseContactsResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching\n return {\n data: [...DEMO_CONTACTS],\n isLoading: false,\n isError: false,\n totalCount: DEMO_CONTACTS.length,\n };\n}\n\n/**\n * Hook to fetch a single contact by ID.\n * This is a stub implementation - override with your own data fetching logic.\n *\n * @param contactId - The unique identifier of the contact to fetch\n * @returns Object containing contact data, loading state, and error state\n *\n * @example\n * ```tsx\n * const { data: contact, isLoading, isError } = useContact('contact-123');\n * ```\n */\nexport function useContact(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _contactId: string,\n): UseContactResult {\n // Demo data for first-run experience\n // In production, replace with actual data fetching using _contactId\n return {\n data: DEMO_CONTACT,\n isLoading: false,\n isError: false,\n };\n}\n","/**\n * Auth Types\n *\n * These types define the JWT payload structure and authentication\n * configuration options.\n */\n\n// ============================================================================\n// User Types - Derive from constant for single source of truth\n// ============================================================================\n\n/**\n * User type constant - single source of truth for user role values.\n * Use USER_TYPES.admin instead of \"admin\" for type-safe comparisons.\n */\nexport const USER_TYPES = {\n admin: \"admin\",\n rep: \"rep\",\n root_admin: \"root_admin\",\n customer: \"customer\",\n} as const;\n\n/**\n * Union type of all user types, derived from USER_TYPES constant.\n * @see deriving-typeof-for-object-keys pattern\n */\nexport type UserType = (typeof USER_TYPES)[keyof typeof USER_TYPES];\n\n/**\n * Runtime validation for user types.\n * @param value - The value to check\n * @returns true if value is a valid UserType\n */\nexport function isUserType(value: string): value is UserType {\n return Object.values(USER_TYPES).includes(value as UserType);\n}\n\n/**\n * JWT payload structure from Fluid Commerce authentication.\n * Contains user identity and role information.\n */\nexport interface JWTPayload {\n /** User ID */\n id?: number | undefined;\n /** User email address */\n email?: string | undefined;\n /** Full name of the user */\n full_name?: string | undefined;\n /** User role type */\n user_type: UserType;\n /** Original user type (for impersonation scenarios) */\n og_user_type?: UserType | undefined;\n /** Company ID the user belongs to */\n company_id?: number | undefined;\n /** Token expiration timestamp (Unix seconds) */\n exp?: number | undefined;\n /** Authentication type (e.g., \"standard\", \"impersonation\") */\n auth_type?: string | undefined;\n}\n\n/**\n * Configuration options for FluidAuthProvider.\n * All options have sensible defaults.\n */\nexport interface FluidAuthConfig {\n /**\n * URL parameter name for the auth token.\n * @default \"fluidUserToken\"\n */\n tokenKey?: string;\n\n /**\n * Cookie name for storing the auth token.\n * @default \"auth_token\"\n */\n cookieKey?: string;\n\n /**\n * Cookie max age in seconds.\n * @default 777600 (9 days)\n */\n cookieMaxAge?: number;\n\n /**\n * Grace period in milliseconds to account for clock skew\n * when checking token expiration.\n * @default 30000 (30 seconds)\n */\n gracePeriodMs?: number;\n\n /**\n * Callback invoked when authentication fails (no valid token).\n * When omitted, the SDK redirects to `authUrl` with the current URL\n * as a redirect parameter so users can log in and return.\n */\n onAuthFailure?: () => void;\n\n /**\n * Base URL for the authentication page.\n * Used by the default auth failure redirect when `onAuthFailure` is not provided.\n * Ignored when a custom `onAuthFailure` callback is set.\n * @default \"https://auth.fluid.app\"\n */\n authUrl?: string;\n\n /**\n * Enable dev-mode auth bypass.\n * When true AND running in Vite dev mode (import.meta.env.DEV),\n * auth will use a synthetic mock user instead of requiring a real JWT.\n * The mock user allows UI rendering but API calls will fail (token is null).\n * @default false\n */\n devBypass?: boolean;\n\n /**\n * JWKS (JSON Web Key Set) URL for signature verification.\n * When provided, JWT signatures will be verified against keys\n * from this endpoint before accepting the token.\n *\n * This provides defense-in-depth client-side verification.\n * Without this, tokens are only decoded (not verified) client-side,\n * and real verification happens server-side on API calls.\n *\n * @example \"https://api.fluid.app/.well-known/jwks.json\"\n */\n jwksUrl?: string;\n}\n\n/**\n * Value provided by the FluidAuthContext.\n * All properties are readonly since context values should not be mutated by consumers.\n */\nexport interface FluidAuthContextValue {\n /** Whether the user is authenticated with a valid token */\n readonly isAuthenticated: boolean;\n /** Whether authentication is still being initialized */\n readonly isLoading: boolean;\n /** Decoded JWT payload if authenticated, null otherwise */\n readonly user: JWTPayload | null;\n /** Raw JWT token string if authenticated, null otherwise */\n readonly token: string | null;\n /** Clear authentication state and stored tokens */\n readonly clearAuth: () => void;\n /** Authentication error if any occurred during initialization */\n readonly error: Error | null;\n}\n\n/**\n * Result of token validation.\n * Uses a discriminated union for type-safe handling of valid/invalid states.\n */\nexport type TokenValidationResult =\n | {\n /** Token is valid */\n isValid: true;\n /** Decoded JWT payload */\n payload: JWTPayload;\n /** No error when valid */\n error?: undefined;\n }\n | {\n /** Token is invalid */\n isValid: false;\n /** Decoded JWT payload if parseable but expired */\n payload?: JWTPayload;\n /** Error message explaining why validation failed */\n error: string;\n };\n","import { z } from \"zod\";\nimport type { FetchClientInstance } from \"@fluid-app/api-client-core\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface Company {\n id: number;\n name: string;\n logo_url?: string | null;\n icon_url?: string | null;\n}\n\nexport interface SwitchCompanyResponseCompany {\n id: number;\n name: string;\n fluid_shop: string;\n logo_url?: string | null;\n jwt: string;\n}\n\nexport interface SwitchCompanyResponseMeta {\n request_id: string;\n timestamp: string;\n}\n\nexport interface SwitchCompanyResponse {\n company: SwitchCompanyResponseCompany;\n meta: SwitchCompanyResponseMeta;\n}\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nexport const companySchema: z.ZodType<Company> = z.object({\n id: z.number(),\n name: z.string(),\n logo_url: z.string().optional().nullable(),\n icon_url: z.string().nullable().optional(),\n});\n\nexport const switchCompanyResponseCompanySchema: z.ZodType<SwitchCompanyResponseCompany> =\n z.object({\n id: z.number().int().positive(),\n name: z.string(),\n fluid_shop: z.string(),\n logo_url: z.string().url().nullable().optional(),\n jwt: z.string(),\n });\n\nexport const switchCompanyResponseMetaSchema: z.ZodType<SwitchCompanyResponseMeta> =\n z.object({\n request_id: z.string(),\n timestamp: z.string(),\n });\n\nexport const switchCompanyResponseSchema: z.ZodType<SwitchCompanyResponse> =\n z.object({\n company: switchCompanyResponseCompanySchema,\n meta: switchCompanyResponseMetaSchema,\n });\n\n// ============================================================================\n// API function\n// ============================================================================\n\nexport function switchCompany(\n client: FetchClientInstance,\n companyId: number,\n): Promise<SwitchCompanyResponse> {\n return client\n .put<unknown>(`/authentication/company/${companyId}/switch`)\n .then((data) => switchCompanyResponseSchema.parse(data));\n}\n","/**\n * Core page template IDs\n */\nexport const CORE_PAGE_IDS = {\n PROFILE: \"core-profile\",\n MESSAGING: \"core-messaging\",\n CONTACTS: \"core-contacts\",\n ORDERS: \"core-orders\",\n SUBSCRIPTIONS: \"core-subscriptions\",\n CUSTOMERS: \"core-customers\",\n MY_SITE: \"core-my-site\",\n SHAREABLES: \"core-shareables\",\n SHOP: \"core-shop\",\n APP_DOWNLOAD: \"core-app-download\",\n} as const;\n","// Screen Components\nexport { ProfileScreen, profileScreenPropertySchema } from \"./ProfileScreen\";\nexport {\n MessagingScreen,\n messagingScreenPropertySchema,\n} from \"./MessagingScreen\";\nexport { ContactsScreen, contactsScreenPropertySchema } from \"./ContactsScreen\";\nexport { OrdersScreen, ordersScreenPropertySchema } from \"./OrdersScreen\";\nexport {\n SubscriptionsScreen,\n subscriptionsScreenPropertySchema,\n} from \"./SubscriptionsScreen\";\nexport {\n CustomersScreen,\n customersScreenPropertySchema,\n} from \"./CustomersScreen\";\nexport { MySiteScreen, mySiteScreenPropertySchema } from \"./MySiteScreen\";\nexport {\n ShareablesScreen,\n shareablesScreenPropertySchema,\n} from \"./ShareablesScreen\";\nexport { ShopScreen, shopScreenPropertySchema } from \"./ShopScreen\";\nexport {\n AppDownloadScreen,\n appDownloadScreenPropertySchema,\n} from \"./AppDownloadScreen\";\n\n// Re-export all property schemas as a lazy-loadable collection\nimport type { WidgetPropertySchema } from \"../registries/property-schema-types\";\n\nexport const screenPropertySchemas: Record<\n string,\n () => Promise<WidgetPropertySchema>\n> = {\n ProfileScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ProfileScreen\").then((m) => m.profileScreenPropertySchema),\n MessagingScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MessagingScreen\").then((m) => m.messagingScreenPropertySchema),\n ContactsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ContactsScreen\").then((m) => m.contactsScreenPropertySchema),\n OrdersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./OrdersScreen\").then((m) => m.ordersScreenPropertySchema),\n SubscriptionsScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./SubscriptionsScreen\").then(\n (m) => m.subscriptionsScreenPropertySchema,\n ),\n CustomersScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./CustomersScreen\").then((m) => m.customersScreenPropertySchema),\n MySiteScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./MySiteScreen\").then((m) => m.mySiteScreenPropertySchema),\n ShareablesScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShareablesScreen\").then((m) => m.shareablesScreenPropertySchema),\n ShopScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./ShopScreen\").then((m) => m.shopScreenPropertySchema),\n AppDownloadScreen: (): Promise<WidgetPropertySchema> =>\n import(\"./AppDownloadScreen\").then(\n (m) => m.appDownloadScreenPropertySchema,\n ),\n};\n\n// Page Template Registration\nimport { PageTemplateRegistry } from \"../registries/page-template-registry\";\nimport { PAGE_CATEGORIES } from \"../types/page-template\";\nimport { CORE_PAGE_IDS } from \"./core-page-ids\";\n\nexport { CORE_PAGE_IDS } from \"./core-page-ids\";\n\n/**\n * Register core page templates.\n * These are automatically registered when the SDK is imported.\n */\nfunction registerCorePageTemplates(): void {\n // Profile Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.PROFILE,\n slug: \"profile\",\n name: \"Profile\",\n description: \"User profile management\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"profile\", \"account\", \"user\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ProfileScreen\",\n id: \"profile-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Messaging Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MESSAGING,\n slug: \"messaging\",\n name: \"Messaging\",\n description: \"Messaging interface provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.COMMUNICATION,\n tags: [\"messaging\", \"chat\", \"conversations\", \"communication\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MessagingScreen\",\n id: \"messaging-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Contacts Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CONTACTS,\n slug: \"contacts\",\n name: \"Contacts\",\n description: \"Contact management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"contacts\", \"people\", \"address-book\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ContactsScreen\",\n id: \"contacts-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Orders Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.ORDERS,\n slug: \"orders\",\n name: \"Orders\",\n description: \"Order management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"orders\", \"commerce\", \"sales\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"OrdersScreen\",\n id: \"orders-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Subscriptions Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SUBSCRIPTIONS,\n slug: \"subscriptions\",\n name: \"Subscriptions\",\n description: \"Subscription management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"subscriptions\", \"recurring\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"SubscriptionsScreen\",\n id: \"subscriptions-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Customers Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.CUSTOMERS,\n slug: \"customers\",\n name: \"Customers\",\n description: \"Customer management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"customers\", \"people\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"CustomersScreen\",\n id: \"customers-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shop Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHOP,\n slug: \"shop\",\n name: \"Shop\",\n description: \"Product shop with catalog browsing and product details\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shop\", \"store\", \"products\", \"commerce\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShopScreen\",\n id: \"shop-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // MySite Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.MY_SITE,\n slug: \"my-site\",\n name: \"MySite\",\n description: \"Personal site management provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"my-site\", \"website\", \"portal\", \"personal\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"MySiteScreen\",\n id: \"my-site-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // App Download Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.APP_DOWNLOAD,\n slug: \"app-download\",\n name: \"App Download\",\n description: \"Mobile app download page with store links and QR code\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"app-download\", \"mobile\", \"qr-code\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"AppDownloadScreen\",\n id: \"app-download-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n\n // Shareables Screen\n PageTemplateRegistry.register({\n id: CORE_PAGE_IDS.SHAREABLES,\n slug: \"share\",\n name: \"Shareables\",\n description:\n \"Marketing assets and shareable content provided by Fluid Commerce\",\n category: PAGE_CATEGORIES.CORE,\n tags: [\"shareables\", \"marketing\", \"assets\", \"media\", \"products\"],\n version: \"1.0.0\",\n isCore: true,\n component_tree: [\n {\n type: \"ShareablesScreen\",\n id: \"shareables-screen-root\",\n props: {},\n },\n ],\n defaultProps: {},\n });\n}\n\n// Auto-register core templates on module load\nregisterCorePageTemplates();\n","import { useState, useMemo } from \"react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n Button,\n} from \"@fluid-app/ui-primitives\";\nimport { LayoutGrid } from \"lucide-react\";\nimport { RepIcon } from \"@fluid-app/portal-react/components/RepIcon\";\nimport { getSystemNavigationBySection } from \"@fluid-app/portal-core/navigation/system-navigation-items\";\nimport { useShellTranslation } from \"@fluid-app/portal-core/shell-translation-api-context\";\nimport {\n translateNavLabel,\n translateSectionLabel,\n} from \"@fluid-app/portal-core/navigation/translate-nav-label\";\nimport { filterRepOnlySections } from \"../navigation/filter-nav-items\";\nimport { useAccount } from \"../hooks/use-account\";\nimport { deniesRepSurfaces } from \"@fluid-app/portal-core/member-permissions\";\n\nexport interface QuickLinksDropdownProps {\n onNavigate: (slug: string) => void;\n}\n\nexport function QuickLinksDropdown({\n onNavigate,\n}: QuickLinksDropdownProps): React.JSX.Element {\n const [open, setOpen] = useState(false);\n const shellApi = useShellTranslation();\n const { t } = shellApi;\n\n const { data: account } = useAccount();\n const canRep = !deniesRepSurfaces(account);\n\n const sections = useMemo(() => {\n const all = getSystemNavigationBySection();\n return canRep ? all : filterRepOnlySections(all);\n }, [canRep]);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shrink-0\"\n aria-label={t(\"nav_quick_links\")}\n >\n <LayoutGrid className=\"size-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n align=\"end\"\n className=\"w-175 max-w-[90vw] overflow-hidden rounded-lg p-0 shadow-lg\"\n >\n <div className=\"flex flex-col\">\n {/* Header */}\n <div className=\"bg-background flex items-center gap-2 border-b p-4\">\n <LayoutGrid className=\"text-muted-foreground size-5\" />\n <h3 className=\"text-foreground text-sm font-semibold\">\n {t(\"nav_shortcuts\")}\n </h3>\n </div>\n\n {/* Sections */}\n <div className=\"bg-muted space-y-6 p-6\">\n {Object.entries(sections).map(([sectionName, items]) => (\n <div key={sectionName} className=\"space-y-3\">\n <h3 className=\"text-foreground text-sm font-semibold\">\n {translateSectionLabel(shellApi, sectionName)}\n </h3>\n <div className=\"grid grid-cols-4 gap-2\">\n {items.map((item) => (\n <Button\n key={item.slug}\n variant=\"ghost\"\n className=\"text-sidebar-foreground hover:bg-sidebar-primary hover:text-sidebar-primary-foreground h-auto justify-start gap-2 rounded-lg px-3 py-2 text-sm\"\n onClick={() => {\n if (item.slug) onNavigate(item.slug);\n setOpen(false);\n }}\n >\n {item.icon && (\n <RepIcon name={item.icon} className=\"size-4 shrink-0\" />\n )}\n <span className=\"truncate\">\n {translateNavLabel(shellApi, item.slug, item.label)}\n </span>\n </Button>\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n","import React, {\n forwardRef,\n type AnchorHTMLAttributes,\n type MouseEvent,\n} from \"react\";\nimport { useAppNavigation } from \"./AppNavigationContext\";\n\nexport interface AppLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /** Slug to navigate to (e.g. \"contacts/123\") */\n to: string;\n}\n\n/**\n * SPA-aware link that renders a real `<a href>` for accessibility\n * (right-click, ctrl+click, screen readers) but intercepts normal\n * clicks for client-side navigation.\n */\nexport const AppLink: React.ForwardRefExoticComponent<\n AppLinkProps & React.RefAttributes<HTMLAnchorElement>\n> = forwardRef<HTMLAnchorElement, AppLinkProps>(function AppLink(\n { to, onClick, children, ...rest },\n ref,\n) {\n const { navigate, buildHref } = useAppNavigation();\n\n const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {\n // Allow custom onClick handlers to run first\n onClick?.(e);\n if (e.defaultPrevented) return;\n\n // Skip SPA navigation for modifier keys, non-primary button, or external target\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n if (e.button !== 0) return;\n if (rest.target && rest.target !== \"_self\") return;\n\n e.preventDefault();\n navigate(to);\n };\n\n return (\n <a ref={ref} href={buildHref(to)} onClick={handleClick} {...rest}>\n {children}\n </a>\n );\n});\n","/**\n * Scaffold Manifest\n *\n * Tracks which files belong to the portal scaffold and their category.\n * Used by `fluid doctor` to detect drift between a scaffolded portal\n * and the canonical templates shipped with the SDK.\n *\n * Categories:\n * - \"entry\" — App bootstrap files (main.tsx, index.css)\n * - \"config\" — Developer-owned config (portal.config.ts, navigation.config.ts)\n * - \"infrastructure\" — Build/tooling config (vite, tsconfig, index.html, linting)\n */\n\nexport type FileCategory = \"entry\" | \"config\" | \"infrastructure\";\n\nexport interface ManifestEntry {\n readonly category: FileCategory;\n}\n\n/**\n * Canonical scaffold file manifest.\n *\n * Files marked as \"entry\" or \"infrastructure\" are checked by `fluid doctor`.\n * Files marked as \"config\" are developer-owned and intentionally excluded from drift checks.\n */\nexport const SCAFFOLD_MANIFEST = {\n version: 2,\n files: {\n \"src/main.tsx\": { category: \"entry\" },\n \"src/index.css\": { category: \"entry\" },\n \"index.html\": { category: \"infrastructure\" },\n \"tsconfig.json\": { category: \"infrastructure\" },\n \"vite.config.ts\": { category: \"infrastructure\" },\n \".oxlintrc.json\": { category: \"infrastructure\" },\n },\n /** Developer-owned files — never checked for drift */\n developerOwned: [\n \"src/portal.config.ts\",\n \"src/navigation.config.ts\",\n \"src/screens/*\",\n ],\n} as const;\n\nexport type ScaffoldFile = keyof typeof SCAFFOLD_MANIFEST.files;\n"],"mappings":"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"}