@fayz-ai/saas 0.1.6

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 (645) hide show
  1. package/dist/app/AdminShell.d.ts +31 -0
  2. package/dist/app/AdminShell.d.ts.map +1 -0
  3. package/dist/app/LoginPage.d.ts +17 -0
  4. package/dist/app/LoginPage.d.ts.map +1 -0
  5. package/dist/app/config.d.ts +83 -0
  6. package/dist/app/config.d.ts.map +1 -0
  7. package/dist/app/createFayzApp.d.ts +11 -0
  8. package/dist/app/createFayzApp.d.ts.map +1 -0
  9. package/dist/app/routing.d.ts +10 -0
  10. package/dist/app/routing.d.ts.map +1 -0
  11. package/dist/app/scaffold.d.ts +17 -0
  12. package/dist/app/scaffold.d.ts.map +1 -0
  13. package/dist/archetype-lookup.d.ts +30 -0
  14. package/dist/archetype-lookup.d.ts.map +1 -0
  15. package/dist/billing/index.cjs +12 -0
  16. package/dist/billing/index.cjs.map +1 -0
  17. package/dist/billing/index.d.ts +3 -0
  18. package/dist/billing/index.d.ts.map +1 -0
  19. package/dist/billing/index.js +3 -0
  20. package/dist/billing/index.js.map +1 -0
  21. package/dist/billing/store.d.ts +35 -0
  22. package/dist/billing/store.d.ts.map +1 -0
  23. package/dist/chunk-4XTQ4V5L.cjs +124 -0
  24. package/dist/chunk-4XTQ4V5L.cjs.map +1 -0
  25. package/dist/chunk-5GYQK5IW.js +21 -0
  26. package/dist/chunk-5GYQK5IW.js.map +1 -0
  27. package/dist/chunk-DJW67TCY.js +781 -0
  28. package/dist/chunk-DJW67TCY.js.map +1 -0
  29. package/dist/chunk-KYID2MR6.cjs +23 -0
  30. package/dist/chunk-KYID2MR6.cjs.map +1 -0
  31. package/dist/chunk-QGMDVLLW.cjs +816 -0
  32. package/dist/chunk-QGMDVLLW.cjs.map +1 -0
  33. package/dist/chunk-XNWCGJYH.js +97 -0
  34. package/dist/chunk-XNWCGJYH.js.map +1 -0
  35. package/dist/components/shared/PersonLink.d.ts +18 -0
  36. package/dist/components/shared/PersonLink.d.ts.map +1 -0
  37. package/dist/crud/CrudCardGrid.d.ts +13 -0
  38. package/dist/crud/CrudCardGrid.d.ts.map +1 -0
  39. package/dist/crud/CrudDetailPage.d.ts +18 -0
  40. package/dist/crud/CrudDetailPage.d.ts.map +1 -0
  41. package/dist/crud/CrudFormPage.d.ts +18 -0
  42. package/dist/crud/CrudFormPage.d.ts.map +1 -0
  43. package/dist/crud/CrudListView.d.ts +51 -0
  44. package/dist/crud/CrudListView.d.ts.map +1 -0
  45. package/dist/crud/CrudPage.d.ts +20 -0
  46. package/dist/crud/CrudPage.d.ts.map +1 -0
  47. package/dist/crud/DeleteConfirmDialog.d.ts +11 -0
  48. package/dist/crud/DeleteConfirmDialog.d.ts.map +1 -0
  49. package/dist/crud/EntityOverview.d.ts +14 -0
  50. package/dist/crud/EntityOverview.d.ts.map +1 -0
  51. package/dist/crud/ImportWizard.d.ts +18 -0
  52. package/dist/crud/ImportWizard.d.ts.map +1 -0
  53. package/dist/crud/archetypes/ActiveToggle.d.ts +8 -0
  54. package/dist/crud/archetypes/ActiveToggle.d.ts.map +1 -0
  55. package/dist/crud/archetypes/ArchetypeStatusBar.d.ts +11 -0
  56. package/dist/crud/archetypes/ArchetypeStatusBar.d.ts.map +1 -0
  57. package/dist/crud/archetypes/BlockSettingsPopover.d.ts +15 -0
  58. package/dist/crud/archetypes/BlockSettingsPopover.d.ts.map +1 -0
  59. package/dist/crud/archetypes/EntityLink.d.ts +32 -0
  60. package/dist/crud/archetypes/EntityLink.d.ts.map +1 -0
  61. package/dist/crud/archetypes/LocationDetailTabs.d.ts +11 -0
  62. package/dist/crud/archetypes/LocationDetailTabs.d.ts.map +1 -0
  63. package/dist/crud/archetypes/LocationFormLayout.d.ts +12 -0
  64. package/dist/crud/archetypes/LocationFormLayout.d.ts.map +1 -0
  65. package/dist/crud/archetypes/PersonDetailTabs.d.ts +22 -0
  66. package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -0
  67. package/dist/crud/archetypes/PersonFormLayout.d.ts +13 -0
  68. package/dist/crud/archetypes/PersonFormLayout.d.ts.map +1 -0
  69. package/dist/crud/archetypes/PersonPicker.d.ts +23 -0
  70. package/dist/crud/archetypes/PersonPicker.d.ts.map +1 -0
  71. package/dist/crud/archetypes/ProductDetailTabs.d.ts +11 -0
  72. package/dist/crud/archetypes/ProductDetailTabs.d.ts.map +1 -0
  73. package/dist/crud/archetypes/ProductFormLayout.d.ts +12 -0
  74. package/dist/crud/archetypes/ProductFormLayout.d.ts.map +1 -0
  75. package/dist/crud/archetypes/ScheduleEditor.d.ts +7 -0
  76. package/dist/crud/archetypes/ScheduleEditor.d.ts.map +1 -0
  77. package/dist/crud/archetypes/ServiceDetailTabs.d.ts +10 -0
  78. package/dist/crud/archetypes/ServiceDetailTabs.d.ts.map +1 -0
  79. package/dist/crud/archetypes/ServiceFormLayout.d.ts +12 -0
  80. package/dist/crud/archetypes/ServiceFormLayout.d.ts.map +1 -0
  81. package/dist/crud/archetypes/SubjectDetailTabs.d.ts +12 -0
  82. package/dist/crud/archetypes/SubjectDetailTabs.d.ts.map +1 -0
  83. package/dist/crud/archetypes/SubjectFormLayout.d.ts +14 -0
  84. package/dist/crud/archetypes/SubjectFormLayout.d.ts.map +1 -0
  85. package/dist/crud/createCrudPage.d.ts +20 -0
  86. package/dist/crud/createCrudPage.d.ts.map +1 -0
  87. package/dist/crud/csv-export.d.ts +3 -0
  88. package/dist/crud/csv-export.d.ts.map +1 -0
  89. package/dist/crud/fieldToColumn.d.ts +14 -0
  90. package/dist/crud/fieldToColumn.d.ts.map +1 -0
  91. package/dist/crud/index.d.ts +12 -0
  92. package/dist/crud/index.d.ts.map +1 -0
  93. package/dist/hooks/useFieldRules.d.ts +5 -0
  94. package/dist/hooks/useFieldRules.d.ts.map +1 -0
  95. package/dist/hooks/useModuleNavigation.d.ts +25 -0
  96. package/dist/hooks/useModuleNavigation.d.ts.map +1 -0
  97. package/dist/hooks/usePluginPrefs.d.ts +23 -0
  98. package/dist/hooks/usePluginPrefs.d.ts.map +1 -0
  99. package/dist/index.cjs +13880 -0
  100. package/dist/index.cjs.map +1 -0
  101. package/dist/index.d.ts +46 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +13730 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/lib/currency.d.ts +13 -0
  106. package/dist/lib/currency.d.ts.map +1 -0
  107. package/dist/lib/entity-routes.d.ts +24 -0
  108. package/dist/lib/entity-routes.d.ts.map +1 -0
  109. package/dist/lib/schedule-config.d.ts +32 -0
  110. package/dist/lib/schedule-config.d.ts.map +1 -0
  111. package/dist/lib/schedule-service.d.ts +76 -0
  112. package/dist/lib/schedule-service.d.ts.map +1 -0
  113. package/dist/org/adapters/mock.d.ts +4 -0
  114. package/dist/org/adapters/mock.d.ts.map +1 -0
  115. package/dist/org/adapters/supabase.d.ts +7 -0
  116. package/dist/org/adapters/supabase.d.ts.map +1 -0
  117. package/dist/org/context.d.ts +29 -0
  118. package/dist/org/context.d.ts.map +1 -0
  119. package/dist/org/index.cjs +44 -0
  120. package/dist/org/index.cjs.map +1 -0
  121. package/dist/org/index.d.ts +8 -0
  122. package/dist/org/index.d.ts.map +1 -0
  123. package/dist/org/index.js +3 -0
  124. package/dist/org/index.js.map +1 -0
  125. package/dist/org/store.d.ts +16 -0
  126. package/dist/org/store.d.ts.map +1 -0
  127. package/dist/permissions/PermissionGate.d.ts +12 -0
  128. package/dist/permissions/PermissionGate.d.ts.map +1 -0
  129. package/dist/permissions/context.d.ts +37 -0
  130. package/dist/permissions/context.d.ts.map +1 -0
  131. package/dist/permissions/index.cjs +32 -0
  132. package/dist/permissions/index.cjs.map +1 -0
  133. package/dist/permissions/index.d.ts +5 -0
  134. package/dist/permissions/index.d.ts.map +1 -0
  135. package/dist/permissions/index.js +3 -0
  136. package/dist/permissions/index.js.map +1 -0
  137. package/dist/permissions/store.d.ts +21 -0
  138. package/dist/permissions/store.d.ts.map +1 -0
  139. package/dist/placeholder.d.ts +7 -0
  140. package/dist/placeholder.d.ts.map +1 -0
  141. package/dist/plugins/ModuleActionBar.d.ts +27 -0
  142. package/dist/plugins/ModuleActionBar.d.ts.map +1 -0
  143. package/dist/plugins/PluginRegistryManager.d.ts +13 -0
  144. package/dist/plugins/PluginRegistryManager.d.ts.map +1 -0
  145. package/dist/plugins/QuickActionsButton.d.ts +12 -0
  146. package/dist/plugins/QuickActionsButton.d.ts.map +1 -0
  147. package/dist/plugins/SettingsGroup.d.ts +25 -0
  148. package/dist/plugins/SettingsGroup.d.ts.map +1 -0
  149. package/dist/plugins/WidgetSlot.d.ts +8 -0
  150. package/dist/plugins/WidgetSlot.d.ts.map +1 -0
  151. package/dist/plugins/createPluginContext.d.ts +28 -0
  152. package/dist/plugins/createPluginContext.d.ts.map +1 -0
  153. package/dist/plugins/createViewRouter.d.ts +38 -0
  154. package/dist/plugins/createViewRouter.d.ts.map +1 -0
  155. package/dist/shell/components/auth/CenteredLogin.d.ts +13 -0
  156. package/dist/shell/components/auth/CenteredLogin.d.ts.map +1 -0
  157. package/dist/shell/components/auth/LoginForm.d.ts +12 -0
  158. package/dist/shell/components/auth/LoginForm.d.ts.map +1 -0
  159. package/dist/shell/components/auth/LoginPage.d.ts +15 -0
  160. package/dist/shell/components/auth/LoginPage.d.ts.map +1 -0
  161. package/dist/shell/components/auth/OAuthButtons.d.ts +10 -0
  162. package/dist/shell/components/auth/OAuthButtons.d.ts.map +1 -0
  163. package/dist/shell/components/auth/ProtectedRoute.d.ts +11 -0
  164. package/dist/shell/components/auth/ProtectedRoute.d.ts.map +1 -0
  165. package/dist/shell/components/auth/RecoveryForm.d.ts +9 -0
  166. package/dist/shell/components/auth/RecoveryForm.d.ts.map +1 -0
  167. package/dist/shell/components/auth/SignupForm.d.ts +10 -0
  168. package/dist/shell/components/auth/SignupForm.d.ts.map +1 -0
  169. package/dist/shell/components/auth/SplitLogin.d.ts +14 -0
  170. package/dist/shell/components/auth/SplitLogin.d.ts.map +1 -0
  171. package/dist/shell/components/auth/index.d.ts +7 -0
  172. package/dist/shell/components/auth/index.d.ts.map +1 -0
  173. package/dist/shell/components/billing/BillingPage.d.ts +7 -0
  174. package/dist/shell/components/billing/BillingPage.d.ts.map +1 -0
  175. package/dist/shell/components/billing/InvoiceList.d.ts +9 -0
  176. package/dist/shell/components/billing/InvoiceList.d.ts.map +1 -0
  177. package/dist/shell/components/billing/PaywallGate.d.ts +11 -0
  178. package/dist/shell/components/billing/PaywallGate.d.ts.map +1 -0
  179. package/dist/shell/components/billing/PlanSelector.d.ts +13 -0
  180. package/dist/shell/components/billing/PlanSelector.d.ts.map +1 -0
  181. package/dist/shell/components/billing/SubscriptionCard.d.ts +12 -0
  182. package/dist/shell/components/billing/SubscriptionCard.d.ts.map +1 -0
  183. package/dist/shell/components/billing/index.d.ts +6 -0
  184. package/dist/shell/components/billing/index.d.ts.map +1 -0
  185. package/dist/shell/components/chat/ChatFab.d.ts +9 -0
  186. package/dist/shell/components/chat/ChatFab.d.ts.map +1 -0
  187. package/dist/shell/components/chat/ChatPanel.d.ts +10 -0
  188. package/dist/shell/components/chat/ChatPanel.d.ts.map +1 -0
  189. package/dist/shell/components/chat/ChatSuggestions.d.ts +14 -0
  190. package/dist/shell/components/chat/ChatSuggestions.d.ts.map +1 -0
  191. package/dist/shell/components/chat/index.d.ts +3 -0
  192. package/dist/shell/components/chat/index.d.ts.map +1 -0
  193. package/dist/shell/components/crud/archetypes/ClientOrdersTab.d.ts +16 -0
  194. package/dist/shell/components/crud/archetypes/ClientOrdersTab.d.ts.map +1 -0
  195. package/dist/shell/components/layout/BottomNav.d.ts +24 -0
  196. package/dist/shell/components/layout/BottomNav.d.ts.map +1 -0
  197. package/dist/shell/components/layout/CommandPalette.d.ts +29 -0
  198. package/dist/shell/components/layout/CommandPalette.d.ts.map +1 -0
  199. package/dist/shell/components/layout/LocationSwitchOverlay.d.ts +3 -0
  200. package/dist/shell/components/layout/LocationSwitchOverlay.d.ts.map +1 -0
  201. package/dist/shell/components/layout/MockModeBanner.d.ts +3 -0
  202. package/dist/shell/components/layout/MockModeBanner.d.ts.map +1 -0
  203. package/dist/shell/components/layout/PrintChrome.d.ts +10 -0
  204. package/dist/shell/components/layout/PrintChrome.d.ts.map +1 -0
  205. package/dist/shell/components/layout/index.d.ts +6 -0
  206. package/dist/shell/components/layout/index.d.ts.map +1 -0
  207. package/dist/shell/components/notifications/ChangelogFeed.d.ts +8 -0
  208. package/dist/shell/components/notifications/ChangelogFeed.d.ts.map +1 -0
  209. package/dist/shell/components/notifications/NotificationBell.d.ts +9 -0
  210. package/dist/shell/components/notifications/NotificationBell.d.ts.map +1 -0
  211. package/dist/shell/components/notifications/NotificationInbox.d.ts +13 -0
  212. package/dist/shell/components/notifications/NotificationInbox.d.ts.map +1 -0
  213. package/dist/shell/components/notifications/ToastProvider.d.ts +2 -0
  214. package/dist/shell/components/notifications/ToastProvider.d.ts.map +1 -0
  215. package/dist/shell/components/notifications/index.d.ts +5 -0
  216. package/dist/shell/components/notifications/index.d.ts.map +1 -0
  217. package/dist/shell/components/organization/ImpersonationBanner.d.ts +3 -0
  218. package/dist/shell/components/organization/ImpersonationBanner.d.ts.map +1 -0
  219. package/dist/shell/components/organization/InviteMemberDialog.d.ts +10 -0
  220. package/dist/shell/components/organization/InviteMemberDialog.d.ts.map +1 -0
  221. package/dist/shell/components/organization/OrgInitializer.d.ts +7 -0
  222. package/dist/shell/components/organization/OrgInitializer.d.ts.map +1 -0
  223. package/dist/shell/components/organization/OrgSwitcher.d.ts +7 -0
  224. package/dist/shell/components/organization/OrgSwitcher.d.ts.map +1 -0
  225. package/dist/shell/components/organization/PermissionGate.d.ts +11 -0
  226. package/dist/shell/components/organization/PermissionGate.d.ts.map +1 -0
  227. package/dist/shell/components/organization/PermissionMatrixEditor.d.ts +17 -0
  228. package/dist/shell/components/organization/PermissionMatrixEditor.d.ts.map +1 -0
  229. package/dist/shell/components/organization/PermissionProfilesTab.d.ts +3 -0
  230. package/dist/shell/components/organization/PermissionProfilesTab.d.ts.map +1 -0
  231. package/dist/shell/components/organization/TeamTab.d.ts +3 -0
  232. package/dist/shell/components/organization/TeamTab.d.ts.map +1 -0
  233. package/dist/shell/components/organization/TenantOnboarding.d.ts +9 -0
  234. package/dist/shell/components/organization/TenantOnboarding.d.ts.map +1 -0
  235. package/dist/shell/components/organization/index.d.ts +8 -0
  236. package/dist/shell/components/organization/index.d.ts.map +1 -0
  237. package/dist/shell/components/plugins/PluginSettingsPanel.d.ts +23 -0
  238. package/dist/shell/components/plugins/PluginSettingsPanel.d.ts.map +1 -0
  239. package/dist/shell/components/settings/BrandingSettings.d.ts +12 -0
  240. package/dist/shell/components/settings/BrandingSettings.d.ts.map +1 -0
  241. package/dist/shell/components/settings/CompanySettings.d.ts +13 -0
  242. package/dist/shell/components/settings/CompanySettings.d.ts.map +1 -0
  243. package/dist/shell/components/settings/ConnectedBrandingSettings.d.ts +2 -0
  244. package/dist/shell/components/settings/ConnectedBrandingSettings.d.ts.map +1 -0
  245. package/dist/shell/components/settings/ConnectedCompanySettings.d.ts +2 -0
  246. package/dist/shell/components/settings/ConnectedCompanySettings.d.ts.map +1 -0
  247. package/dist/shell/components/settings/ConnectedFieldRulesSettings.d.ts +2 -0
  248. package/dist/shell/components/settings/ConnectedFieldRulesSettings.d.ts.map +1 -0
  249. package/dist/shell/components/settings/ConnectedHolidaysSettings.d.ts +3 -0
  250. package/dist/shell/components/settings/ConnectedHolidaysSettings.d.ts.map +1 -0
  251. package/dist/shell/components/settings/ConnectedLocationsOverview.d.ts +3 -0
  252. package/dist/shell/components/settings/ConnectedLocationsOverview.d.ts.map +1 -0
  253. package/dist/shell/components/settings/ConnectedSecuritySettings.d.ts +2 -0
  254. package/dist/shell/components/settings/ConnectedSecuritySettings.d.ts.map +1 -0
  255. package/dist/shell/components/settings/ConnectedUserProfile.d.ts +2 -0
  256. package/dist/shell/components/settings/ConnectedUserProfile.d.ts.map +1 -0
  257. package/dist/shell/components/settings/FieldRulesSettings.d.ts +11 -0
  258. package/dist/shell/components/settings/FieldRulesSettings.d.ts.map +1 -0
  259. package/dist/shell/components/settings/HolidaysSettings.d.ts +21 -0
  260. package/dist/shell/components/settings/HolidaysSettings.d.ts.map +1 -0
  261. package/dist/shell/components/settings/LocationsCrudPage.d.ts +6 -0
  262. package/dist/shell/components/settings/LocationsCrudPage.d.ts.map +1 -0
  263. package/dist/shell/components/settings/LocationsOverview.d.ts +19 -0
  264. package/dist/shell/components/settings/LocationsOverview.d.ts.map +1 -0
  265. package/dist/shell/components/settings/SecuritySettings.d.ts +17 -0
  266. package/dist/shell/components/settings/SecuritySettings.d.ts.map +1 -0
  267. package/dist/shell/components/settings/SettingsPage.d.ts +18 -0
  268. package/dist/shell/components/settings/SettingsPage.d.ts.map +1 -0
  269. package/dist/shell/components/settings/UserProfile.d.ts +12 -0
  270. package/dist/shell/components/settings/UserProfile.d.ts.map +1 -0
  271. package/dist/shell/components/settings/index.d.ts +11 -0
  272. package/dist/shell/components/settings/index.d.ts.map +1 -0
  273. package/dist/shell/config/index.d.ts +7 -0
  274. package/dist/shell/config/index.d.ts.map +1 -0
  275. package/dist/shell/config/permissions.d.ts +7 -0
  276. package/dist/shell/config/permissions.d.ts.map +1 -0
  277. package/dist/shell/config/tailwind-preset.d.ts +4 -0
  278. package/dist/shell/config/tailwind-preset.d.ts.map +1 -0
  279. package/dist/shell/config/theme/dark.d.ts +3 -0
  280. package/dist/shell/config/theme/dark.d.ts.map +1 -0
  281. package/dist/shell/config/theme/index.d.ts +6 -0
  282. package/dist/shell/config/theme/index.d.ts.map +1 -0
  283. package/dist/shell/config/theme/light.d.ts +3 -0
  284. package/dist/shell/config/theme/light.d.ts.map +1 -0
  285. package/dist/shell/config/theme/tokens.d.ts +97 -0
  286. package/dist/shell/config/theme/tokens.d.ts.map +1 -0
  287. package/dist/shell/config/theme/utils.d.ts +23 -0
  288. package/dist/shell/config/theme/utils.d.ts.map +1 -0
  289. package/dist/shell/createSaasApp.d.ts +145 -0
  290. package/dist/shell/createSaasApp.d.ts.map +1 -0
  291. package/dist/shell/hooks/index.d.ts +19 -0
  292. package/dist/shell/hooks/index.d.ts.map +1 -0
  293. package/dist/shell/hooks/useAITools.d.ts +21 -0
  294. package/dist/shell/hooks/useAITools.d.ts.map +1 -0
  295. package/dist/shell/hooks/useAuth.d.ts +16 -0
  296. package/dist/shell/hooks/useAuth.d.ts.map +1 -0
  297. package/dist/shell/hooks/useBilling.d.ts +14 -0
  298. package/dist/shell/hooks/useBilling.d.ts.map +1 -0
  299. package/dist/shell/hooks/useChat.d.ts +16 -0
  300. package/dist/shell/hooks/useChat.d.ts.map +1 -0
  301. package/dist/shell/hooks/useFieldRules.d.ts +6 -0
  302. package/dist/shell/hooks/useFieldRules.d.ts.map +1 -0
  303. package/dist/shell/hooks/useFormatters.d.ts +7 -0
  304. package/dist/shell/hooks/useFormatters.d.ts.map +1 -0
  305. package/dist/shell/hooks/useLayout.d.ts +16 -0
  306. package/dist/shell/hooks/useLayout.d.ts.map +1 -0
  307. package/dist/shell/hooks/useModuleNavigation.d.ts +25 -0
  308. package/dist/shell/hooks/useModuleNavigation.d.ts.map +1 -0
  309. package/dist/shell/hooks/useNotifications.d.ts +11 -0
  310. package/dist/shell/hooks/useNotifications.d.ts.map +1 -0
  311. package/dist/shell/hooks/usePermission.d.ts +16 -0
  312. package/dist/shell/hooks/usePermission.d.ts.map +1 -0
  313. package/dist/shell/hooks/usePluginPrefs.d.ts +23 -0
  314. package/dist/shell/hooks/usePluginPrefs.d.ts.map +1 -0
  315. package/dist/shell/hooks/usePlugins.d.ts +5 -0
  316. package/dist/shell/hooks/usePlugins.d.ts.map +1 -0
  317. package/dist/shell/hooks/useTenant.d.ts +23 -0
  318. package/dist/shell/hooks/useTenant.d.ts.map +1 -0
  319. package/dist/shell/hooks/useTenantPlugins.d.ts +10 -0
  320. package/dist/shell/hooks/useTenantPlugins.d.ts.map +1 -0
  321. package/dist/shell/hooks/useTranslation.d.ts +5 -0
  322. package/dist/shell/hooks/useTranslation.d.ts.map +1 -0
  323. package/dist/shell/lib/api.d.ts +28 -0
  324. package/dist/shell/lib/api.d.ts.map +1 -0
  325. package/dist/shell/lib/apply-field-rules.d.ts +4 -0
  326. package/dist/shell/lib/apply-field-rules.d.ts.map +1 -0
  327. package/dist/shell/lib/auth-adapters/mock.d.ts +3 -0
  328. package/dist/shell/lib/auth-adapters/mock.d.ts.map +1 -0
  329. package/dist/shell/lib/auth-adapters/supabase.d.ts +3 -0
  330. package/dist/shell/lib/auth-adapters/supabase.d.ts.map +1 -0
  331. package/dist/shell/lib/auth-context.d.ts +5 -0
  332. package/dist/shell/lib/auth-context.d.ts.map +1 -0
  333. package/dist/shell/lib/cache.d.ts +18 -0
  334. package/dist/shell/lib/cache.d.ts.map +1 -0
  335. package/dist/shell/lib/cn.d.ts +2 -0
  336. package/dist/shell/lib/cn.d.ts.map +1 -0
  337. package/dist/shell/lib/core-ai-tools.d.ts +15 -0
  338. package/dist/shell/lib/core-ai-tools.d.ts.map +1 -0
  339. package/dist/shell/lib/create-client-orders-provider.d.ts +3 -0
  340. package/dist/shell/lib/create-client-orders-provider.d.ts.map +1 -0
  341. package/dist/shell/lib/csv.d.ts +14 -0
  342. package/dist/shell/lib/csv.d.ts.map +1 -0
  343. package/dist/shell/lib/dedup.d.ts +2 -0
  344. package/dist/shell/lib/dedup.d.ts.map +1 -0
  345. package/dist/shell/lib/entity-registry.d.ts +19 -0
  346. package/dist/shell/lib/entity-registry.d.ts.map +1 -0
  347. package/dist/shell/lib/entity-routes.d.ts +24 -0
  348. package/dist/shell/lib/entity-routes.d.ts.map +1 -0
  349. package/dist/shell/lib/format.d.ts +7 -0
  350. package/dist/shell/lib/format.d.ts.map +1 -0
  351. package/dist/shell/lib/i18n.d.ts +4 -0
  352. package/dist/shell/lib/i18n.d.ts.map +1 -0
  353. package/dist/shell/lib/locale-config.d.ts +8 -0
  354. package/dist/shell/lib/locale-config.d.ts.map +1 -0
  355. package/dist/shell/lib/org-adapters/mock.d.ts +4 -0
  356. package/dist/shell/lib/org-adapters/mock.d.ts.map +1 -0
  357. package/dist/shell/lib/org-adapters/supabase.d.ts +3 -0
  358. package/dist/shell/lib/org-adapters/supabase.d.ts.map +1 -0
  359. package/dist/shell/lib/org-context.d.ts +5 -0
  360. package/dist/shell/lib/org-context.d.ts.map +1 -0
  361. package/dist/shell/lib/plugins.d.ts +2 -0
  362. package/dist/shell/lib/plugins.d.ts.map +1 -0
  363. package/dist/shell/lib/router.d.ts +15 -0
  364. package/dist/shell/lib/router.d.ts.map +1 -0
  365. package/dist/shell/lib/schedule-config.d.ts +32 -0
  366. package/dist/shell/lib/schedule-config.d.ts.map +1 -0
  367. package/dist/shell/lib/schedule-service.d.ts +76 -0
  368. package/dist/shell/lib/schedule-service.d.ts.map +1 -0
  369. package/dist/shell/lib/supabase.d.ts +9 -0
  370. package/dist/shell/lib/supabase.d.ts.map +1 -0
  371. package/dist/shell/stores/auth.store.d.ts +12 -0
  372. package/dist/shell/stores/auth.store.d.ts.map +1 -0
  373. package/dist/shell/stores/billing.store.d.ts +2 -0
  374. package/dist/shell/stores/billing.store.d.ts.map +1 -0
  375. package/dist/shell/stores/chat.store.d.ts +20 -0
  376. package/dist/shell/stores/chat.store.d.ts.map +1 -0
  377. package/dist/shell/stores/index.d.ts +13 -0
  378. package/dist/shell/stores/index.d.ts.map +1 -0
  379. package/dist/shell/stores/invite.store.d.ts +11 -0
  380. package/dist/shell/stores/invite.store.d.ts.map +1 -0
  381. package/dist/shell/stores/layout.store.d.ts +2 -0
  382. package/dist/shell/stores/layout.store.d.ts.map +1 -0
  383. package/dist/shell/stores/locale.store.d.ts +7 -0
  384. package/dist/shell/stores/locale.store.d.ts.map +1 -0
  385. package/dist/shell/stores/location.store.d.ts +20 -0
  386. package/dist/shell/stores/location.store.d.ts.map +1 -0
  387. package/dist/shell/stores/notifications.store.d.ts +12 -0
  388. package/dist/shell/stores/notifications.store.d.ts.map +1 -0
  389. package/dist/shell/stores/organization.store.d.ts +2 -0
  390. package/dist/shell/stores/organization.store.d.ts.map +1 -0
  391. package/dist/shell/stores/permissions.store.d.ts +2 -0
  392. package/dist/shell/stores/permissions.store.d.ts.map +1 -0
  393. package/dist/shell/stores/plugin.store.d.ts +24 -0
  394. package/dist/shell/stores/plugin.store.d.ts.map +1 -0
  395. package/dist/shell/stores/tenant.store.d.ts +11 -0
  396. package/dist/shell/stores/tenant.store.d.ts.map +1 -0
  397. package/dist/shell/stores/theme.store.d.ts +17 -0
  398. package/dist/shell/stores/theme.store.d.ts.map +1 -0
  399. package/dist/shell/test-setup.d.ts +2 -0
  400. package/dist/shell/test-setup.d.ts.map +1 -0
  401. package/dist/shell/types/auth-adapter.d.ts +14 -0
  402. package/dist/shell/types/auth-adapter.d.ts.map +1 -0
  403. package/dist/shell/types/auth.d.ts +34 -0
  404. package/dist/shell/types/auth.d.ts.map +1 -0
  405. package/dist/shell/types/billing.d.ts +74 -0
  406. package/dist/shell/types/billing.d.ts.map +1 -0
  407. package/dist/shell/types/client-orders.d.ts +43 -0
  408. package/dist/shell/types/client-orders.d.ts.map +1 -0
  409. package/dist/shell/types/crud.d.ts +122 -0
  410. package/dist/shell/types/crud.d.ts.map +1 -0
  411. package/dist/shell/types/entities.d.ts +150 -0
  412. package/dist/shell/types/entities.d.ts.map +1 -0
  413. package/dist/shell/types/entity-lookup.d.ts +18 -0
  414. package/dist/shell/types/entity-lookup.d.ts.map +1 -0
  415. package/dist/shell/types/index.d.ts +17 -0
  416. package/dist/shell/types/index.d.ts.map +1 -0
  417. package/dist/shell/types/integrations.d.ts +48 -0
  418. package/dist/shell/types/integrations.d.ts.map +1 -0
  419. package/dist/shell/types/invite.d.ts +23 -0
  420. package/dist/shell/types/invite.d.ts.map +1 -0
  421. package/dist/shell/types/layout.d.ts +26 -0
  422. package/dist/shell/types/layout.d.ts.map +1 -0
  423. package/dist/shell/types/notifications.d.ts +31 -0
  424. package/dist/shell/types/notifications.d.ts.map +1 -0
  425. package/dist/shell/types/org-adapter.d.ts +72 -0
  426. package/dist/shell/types/org-adapter.d.ts.map +1 -0
  427. package/dist/shell/types/permissions.d.ts +39 -0
  428. package/dist/shell/types/permissions.d.ts.map +1 -0
  429. package/dist/shell/types/platform.d.ts +25 -0
  430. package/dist/shell/types/platform.d.ts.map +1 -0
  431. package/dist/shell/types/plugins.d.ts +308 -0
  432. package/dist/shell/types/plugins.d.ts.map +1 -0
  433. package/dist/shell/types/tenant.d.ts +74 -0
  434. package/dist/shell/types/tenant.d.ts.map +1 -0
  435. package/dist/stores/createCrudStore.d.ts +32 -0
  436. package/dist/stores/createCrudStore.d.ts.map +1 -0
  437. package/dist/supabase/client.d.ts +18 -0
  438. package/dist/supabase/client.d.ts.map +1 -0
  439. package/package.json +72 -0
  440. package/src/app/AdminShell.tsx +449 -0
  441. package/src/app/LoginPage.tsx +264 -0
  442. package/src/app/config.ts +146 -0
  443. package/src/app/createFayzApp.tsx +254 -0
  444. package/src/app/routing.tsx +65 -0
  445. package/src/app/scaffold.tsx +175 -0
  446. package/src/archetype-lookup.ts +160 -0
  447. package/src/billing/index.ts +7 -0
  448. package/src/billing/store.ts +62 -0
  449. package/src/components/shared/PersonLink.tsx +199 -0
  450. package/src/crud/CrudCardGrid.tsx +92 -0
  451. package/src/crud/CrudDetailPage.tsx +366 -0
  452. package/src/crud/CrudFormPage.tsx +467 -0
  453. package/src/crud/CrudListView.tsx +223 -0
  454. package/src/crud/CrudPage.tsx +491 -0
  455. package/src/crud/DeleteConfirmDialog.tsx +41 -0
  456. package/src/crud/EntityOverview.tsx +230 -0
  457. package/src/crud/ImportWizard.tsx +690 -0
  458. package/src/crud/archetypes/ActiveToggle.tsx +23 -0
  459. package/src/crud/archetypes/ArchetypeStatusBar.tsx +58 -0
  460. package/src/crud/archetypes/BlockSettingsPopover.tsx +322 -0
  461. package/src/crud/archetypes/EntityLink.tsx +74 -0
  462. package/src/crud/archetypes/LocationDetailTabs.tsx +72 -0
  463. package/src/crud/archetypes/LocationFormLayout.tsx +173 -0
  464. package/src/crud/archetypes/PersonDetailTabs.tsx +87 -0
  465. package/src/crud/archetypes/PersonFormLayout.tsx +184 -0
  466. package/src/crud/archetypes/PersonPicker.tsx +299 -0
  467. package/src/crud/archetypes/ProductDetailTabs.tsx +72 -0
  468. package/src/crud/archetypes/ProductFormLayout.tsx +197 -0
  469. package/src/crud/archetypes/ScheduleEditor.tsx +796 -0
  470. package/src/crud/archetypes/ServiceDetailTabs.tsx +60 -0
  471. package/src/crud/archetypes/ServiceFormLayout.tsx +155 -0
  472. package/src/crud/archetypes/SubjectDetailTabs.tsx +142 -0
  473. package/src/crud/archetypes/SubjectFormLayout.tsx +164 -0
  474. package/src/crud/createCrudPage.tsx +65 -0
  475. package/src/crud/csv-export.ts +17 -0
  476. package/src/crud/fieldToColumn.tsx +109 -0
  477. package/src/crud/index.ts +11 -0
  478. package/src/hooks/useFieldRules.ts +10 -0
  479. package/src/hooks/useModuleNavigation.ts +124 -0
  480. package/src/hooks/usePluginPrefs.ts +113 -0
  481. package/src/index.ts +168 -0
  482. package/src/lib/currency.ts +19 -0
  483. package/src/lib/entity-routes.ts +53 -0
  484. package/src/lib/schedule-config.ts +42 -0
  485. package/src/lib/schedule-service.ts +307 -0
  486. package/src/org/adapters/mock.ts +353 -0
  487. package/src/org/adapters/supabase.ts +509 -0
  488. package/src/org/context.tsx +174 -0
  489. package/src/org/index.ts +19 -0
  490. package/src/org/store.ts +70 -0
  491. package/src/permissions/PermissionGate.tsx +17 -0
  492. package/src/permissions/context.tsx +126 -0
  493. package/src/permissions/index.ts +13 -0
  494. package/src/permissions/store.ts +68 -0
  495. package/src/placeholder.ts +32 -0
  496. package/src/plugins/ModuleActionBar.tsx +57 -0
  497. package/src/plugins/PluginRegistryManager.tsx +110 -0
  498. package/src/plugins/QuickActionsButton.tsx +73 -0
  499. package/src/plugins/SettingsGroup.tsx +100 -0
  500. package/src/plugins/WidgetSlot.tsx +38 -0
  501. package/src/plugins/createPluginContext.tsx +68 -0
  502. package/src/plugins/createViewRouter.tsx +45 -0
  503. package/src/shell/components/auth/CenteredLogin.tsx +86 -0
  504. package/src/shell/components/auth/LoginForm.tsx +129 -0
  505. package/src/shell/components/auth/LoginPage.tsx +22 -0
  506. package/src/shell/components/auth/OAuthButtons.tsx +66 -0
  507. package/src/shell/components/auth/ProtectedRoute.tsx +52 -0
  508. package/src/shell/components/auth/RecoveryForm.tsx +110 -0
  509. package/src/shell/components/auth/SignupForm.tsx +170 -0
  510. package/src/shell/components/auth/SplitLogin.tsx +124 -0
  511. package/src/shell/components/auth/index.ts +6 -0
  512. package/src/shell/components/billing/BillingPage.tsx +56 -0
  513. package/src/shell/components/billing/InvoiceList.tsx +123 -0
  514. package/src/shell/components/billing/PaywallGate.tsx +80 -0
  515. package/src/shell/components/billing/PlanSelector.tsx +125 -0
  516. package/src/shell/components/billing/SubscriptionCard.tsx +108 -0
  517. package/src/shell/components/billing/index.ts +5 -0
  518. package/src/shell/components/chat/ChatFab.tsx +195 -0
  519. package/src/shell/components/chat/ChatPanel.tsx +170 -0
  520. package/src/shell/components/chat/ChatSuggestions.tsx +102 -0
  521. package/src/shell/components/chat/index.ts +2 -0
  522. package/src/shell/components/crud/archetypes/ClientOrdersTab.tsx +212 -0
  523. package/src/shell/components/layout/BottomNav.tsx +131 -0
  524. package/src/shell/components/layout/CommandPalette.tsx +257 -0
  525. package/src/shell/components/layout/LocationSwitchOverlay.tsx +64 -0
  526. package/src/shell/components/layout/MockModeBanner.tsx +17 -0
  527. package/src/shell/components/layout/PrintChrome.tsx +200 -0
  528. package/src/shell/components/layout/index.ts +5 -0
  529. package/src/shell/components/notifications/ChangelogFeed.tsx +85 -0
  530. package/src/shell/components/notifications/NotificationBell.tsx +44 -0
  531. package/src/shell/components/notifications/NotificationInbox.tsx +175 -0
  532. package/src/shell/components/notifications/ToastProvider.tsx +2 -0
  533. package/src/shell/components/notifications/index.ts +4 -0
  534. package/src/shell/components/organization/ImpersonationBanner.tsx +29 -0
  535. package/src/shell/components/organization/InviteMemberDialog.tsx +161 -0
  536. package/src/shell/components/organization/OrgInitializer.tsx +179 -0
  537. package/src/shell/components/organization/OrgSwitcher.tsx +177 -0
  538. package/src/shell/components/organization/PermissionGate.tsx +16 -0
  539. package/src/shell/components/organization/PermissionMatrixEditor.tsx +206 -0
  540. package/src/shell/components/organization/PermissionProfilesTab.tsx +148 -0
  541. package/src/shell/components/organization/TeamTab.tsx +243 -0
  542. package/src/shell/components/organization/TenantOnboarding.tsx +179 -0
  543. package/src/shell/components/organization/index.ts +7 -0
  544. package/src/shell/components/plugins/PluginSettingsPanel.tsx +189 -0
  545. package/src/shell/components/settings/BrandingSettings.tsx +255 -0
  546. package/src/shell/components/settings/CompanySettings.tsx +188 -0
  547. package/src/shell/components/settings/ConnectedBrandingSettings.tsx +82 -0
  548. package/src/shell/components/settings/ConnectedCompanySettings.tsx +63 -0
  549. package/src/shell/components/settings/ConnectedFieldRulesSettings.tsx +47 -0
  550. package/src/shell/components/settings/ConnectedHolidaysSettings.tsx +99 -0
  551. package/src/shell/components/settings/ConnectedLocationsOverview.tsx +57 -0
  552. package/src/shell/components/settings/ConnectedSecuritySettings.tsx +59 -0
  553. package/src/shell/components/settings/ConnectedUserProfile.tsx +59 -0
  554. package/src/shell/components/settings/FieldRulesSettings.tsx +511 -0
  555. package/src/shell/components/settings/HolidaysSettings.tsx +141 -0
  556. package/src/shell/components/settings/LocationsCrudPage.ts +36 -0
  557. package/src/shell/components/settings/LocationsOverview.tsx +171 -0
  558. package/src/shell/components/settings/SecuritySettings.tsx +208 -0
  559. package/src/shell/components/settings/SettingsPage.tsx +162 -0
  560. package/src/shell/components/settings/UserProfile.tsx +151 -0
  561. package/src/shell/components/settings/index.ts +10 -0
  562. package/src/shell/config/index.ts +6 -0
  563. package/src/shell/config/permissions.ts +31 -0
  564. package/src/shell/config/tailwind-preset.ts +164 -0
  565. package/src/shell/config/theme/dark.ts +63 -0
  566. package/src/shell/config/theme/index.ts +5 -0
  567. package/src/shell/config/theme/light.ts +63 -0
  568. package/src/shell/config/theme/tokens.ts +105 -0
  569. package/src/shell/config/theme/utils.ts +249 -0
  570. package/src/shell/createSaasApp.tsx +922 -0
  571. package/src/shell/hooks/index.ts +18 -0
  572. package/src/shell/hooks/useAITools.ts +139 -0
  573. package/src/shell/hooks/useAuth.ts +109 -0
  574. package/src/shell/hooks/useBilling.ts +151 -0
  575. package/src/shell/hooks/useChat.ts +84 -0
  576. package/src/shell/hooks/useFieldRules.ts +19 -0
  577. package/src/shell/hooks/useFormatters.ts +56 -0
  578. package/src/shell/hooks/useLayout.ts +76 -0
  579. package/src/shell/hooks/useModuleNavigation.ts +124 -0
  580. package/src/shell/hooks/useNotifications.ts +114 -0
  581. package/src/shell/hooks/usePermission.ts +101 -0
  582. package/src/shell/hooks/usePluginPrefs.ts +113 -0
  583. package/src/shell/hooks/usePlugins.ts +18 -0
  584. package/src/shell/hooks/useTenant.ts +106 -0
  585. package/src/shell/hooks/useTenantPlugins.ts +103 -0
  586. package/src/shell/hooks/useTranslation.ts +10 -0
  587. package/src/shell/lib/api.ts +67 -0
  588. package/src/shell/lib/apply-field-rules.ts +14 -0
  589. package/src/shell/lib/auth-adapters/mock.ts +97 -0
  590. package/src/shell/lib/auth-adapters/supabase.ts +96 -0
  591. package/src/shell/lib/auth-context.ts +18 -0
  592. package/src/shell/lib/cache.ts +124 -0
  593. package/src/shell/lib/cn.ts +1 -0
  594. package/src/shell/lib/core-ai-tools.ts +90 -0
  595. package/src/shell/lib/create-client-orders-provider.ts +105 -0
  596. package/src/shell/lib/csv.ts +54 -0
  597. package/src/shell/lib/dedup.ts +17 -0
  598. package/src/shell/lib/entity-registry.ts +39 -0
  599. package/src/shell/lib/entity-routes.ts +53 -0
  600. package/src/shell/lib/format.ts +58 -0
  601. package/src/shell/lib/i18n.ts +3723 -0
  602. package/src/shell/lib/locale-config.ts +14 -0
  603. package/src/shell/lib/org-adapters/mock.ts +318 -0
  604. package/src/shell/lib/org-adapters/supabase.ts +533 -0
  605. package/src/shell/lib/org-context.ts +18 -0
  606. package/src/shell/lib/plugins.ts +11 -0
  607. package/src/shell/lib/router.ts +86 -0
  608. package/src/shell/lib/schedule-config.ts +42 -0
  609. package/src/shell/lib/schedule-service.ts +307 -0
  610. package/src/shell/lib/supabase.ts +19 -0
  611. package/src/shell/stores/auth.store.ts +29 -0
  612. package/src/shell/stores/billing.store.ts +1 -0
  613. package/src/shell/stores/chat.store.ts +45 -0
  614. package/src/shell/stores/index.ts +12 -0
  615. package/src/shell/stores/invite.store.ts +18 -0
  616. package/src/shell/stores/layout.store.ts +1 -0
  617. package/src/shell/stores/locale.store.ts +24 -0
  618. package/src/shell/stores/location.store.ts +89 -0
  619. package/src/shell/stores/notifications.store.ts +48 -0
  620. package/src/shell/stores/organization.store.ts +3 -0
  621. package/src/shell/stores/permissions.store.ts +1 -0
  622. package/src/shell/stores/plugin.store.ts +50 -0
  623. package/src/shell/stores/tenant.store.ts +26 -0
  624. package/src/shell/stores/theme.store.ts +162 -0
  625. package/src/shell/styles.css +433 -0
  626. package/src/shell/test-setup.ts +1 -0
  627. package/src/shell/types/auth-adapter.ts +11 -0
  628. package/src/shell/types/auth.ts +38 -0
  629. package/src/shell/types/billing.ts +90 -0
  630. package/src/shell/types/client-orders.ts +58 -0
  631. package/src/shell/types/crud.ts +134 -0
  632. package/src/shell/types/entities.ts +169 -0
  633. package/src/shell/types/entity-lookup.ts +25 -0
  634. package/src/shell/types/index.ts +16 -0
  635. package/src/shell/types/integrations.ts +51 -0
  636. package/src/shell/types/invite.ts +23 -0
  637. package/src/shell/types/layout.ts +29 -0
  638. package/src/shell/types/notifications.ts +33 -0
  639. package/src/shell/types/org-adapter.ts +72 -0
  640. package/src/shell/types/permissions.ts +64 -0
  641. package/src/shell/types/platform.ts +25 -0
  642. package/src/shell/types/plugins.ts +359 -0
  643. package/src/shell/types/tenant.ts +79 -0
  644. package/src/stores/createCrudStore.ts +138 -0
  645. package/src/supabase/client.ts +54 -0
@@ -0,0 +1,922 @@
1
+ import React from 'react'
2
+ import { useThemeStore } from './stores/theme.store'
3
+ import { useBillingStore } from './stores/billing.store'
4
+ import { useAuthStore } from './stores/auth.store'
5
+ import { usePermissionsStore } from './stores/permissions.store'
6
+ import { createSupabaseClient, getSupabaseClientOptional } from './lib/supabase'
7
+ import { setEntityRouteMap, resolveEntityHref } from './lib/entity-routes'
8
+ import { ToastProvider } from './components/notifications/ToastProvider'
9
+ import { RouterProvider, setGlobalRouter, hashRouterAdapter, type RouterAdapter } from './lib/router'
10
+ import { AppShell } from '@fayz-ai/ui'
11
+ import type { NavigationItem as UiNavigationItem } from '@fayz-ai/ui'
12
+ import { MockModeBanner } from './components/layout/MockModeBanner'
13
+ import { WidgetSlot } from '../plugins/WidgetSlot'
14
+ import { SettingsPage } from './components/settings/SettingsPage'
15
+ import { BillingPage } from './components/billing/BillingPage'
16
+ import { ChatFab } from './components/chat/ChatFab'
17
+ import { ChatPanel } from './components/chat/ChatPanel'
18
+ import { CommandPalette, type CommandItem, type EntitySearchResult, type EntitySearchFn } from './components/layout/CommandPalette'
19
+ import { useLayoutStore } from './stores/layout.store'
20
+ import { LoginPage } from './components/auth/LoginPage'
21
+ import { ProtectedRoute } from './components/auth/ProtectedRoute'
22
+ import { AuthAdapterProvider } from './lib/auth-context'
23
+ import { OrgAdapterProvider } from './lib/org-context'
24
+ import { createMockAuthAdapter } from './lib/auth-adapters/mock'
25
+ import { createSupabaseAuthAdapter } from './lib/auth-adapters/supabase'
26
+ import { createMockOrgAdapter } from './lib/org-adapters/mock'
27
+ import { createSupabaseOrgAdapter } from './lib/org-adapters/supabase'
28
+ import { OrgSwitcher } from './components/organization/OrgSwitcher'
29
+ import { OrgInitializer } from './components/organization/OrgInitializer'
30
+ import { PermissionGate } from '../permissions/PermissionGate'
31
+ import { ImpersonationBanner } from './components/organization/ImpersonationBanner'
32
+ import { TeamTab } from './components/organization/TeamTab'
33
+ import { PermissionProfilesTab } from './components/organization/PermissionProfilesTab'
34
+ import { ConnectedLocationsOverview } from './components/settings/ConnectedLocationsOverview'
35
+ import { LocationsCrudPage } from './components/settings/LocationsCrudPage'
36
+ import { LocationSwitchOverlay } from './components/layout/LocationSwitchOverlay'
37
+ import { ConnectedHolidaysSettings } from './components/settings/ConnectedHolidaysSettings'
38
+ import { ConnectedFieldRulesSettings } from './components/settings/ConnectedFieldRulesSettings'
39
+ import { Users, ShieldCheck, MapPin, CalendarOff, SlidersHorizontal, Puzzle } from 'lucide-react'
40
+ import * as LucideIcons from 'lucide-react'
41
+ import { resolvePluginRuntime, PluginRuntimeProvider } from './lib/plugins'
42
+ import type { AuthAdapter } from './types/auth-adapter'
43
+ import type { OrgAdapter } from './types/org-adapter'
44
+ import type { NavigationItem } from './types/layout'
45
+ import type { Plan, PlanConfig } from './types/billing'
46
+ import { normalizePlanConfig } from './types/billing'
47
+ import type { PluginManifest, TenantPluginBinding } from '@fayz-ai/core'
48
+ import type {
49
+ PluginPermissionRequirement,
50
+ ResolvedPluginManifest,
51
+ PluginSettingsTab,
52
+ } from './types/plugins'
53
+ import type { CreateThemeOptions } from './config/theme/utils'
54
+ import { resolveTheme } from './config/theme/utils'
55
+ import type { SaasTheme } from './config/theme/tokens'
56
+ import type { AuthProvider } from './types/auth'
57
+ import type { PermissionsConfig, PermissionAction } from './types/permissions'
58
+ import { useOrganizationStore } from './stores/organization.store'
59
+ import { usePermission } from './hooks/usePermission'
60
+ import { useTenantPlugins } from './hooks/useTenantPlugins'
61
+ import { I18nProvider, builtInLocales } from './lib/i18n'
62
+ import { useLocaleStore } from './stores/locale.store'
63
+ import { setDefaultCurrency, setCurrentLocale, registerTranslations } from '@fayz-ai/core'
64
+ import { useTranslation } from './hooks/useTranslation'
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Page config — unifies navigation + routing in one declaration
68
+ // ---------------------------------------------------------------------------
69
+ export interface PageConfig {
70
+ /** URL path, e.g. '/' or '/clients' */
71
+ path: string
72
+ /** Sidebar/nav label */
73
+ label: string
74
+ /** Lucide icon name */
75
+ icon: string
76
+ /** The page component to render */
77
+ component: React.ComponentType
78
+ /** Navigation section (default: 'main') */
79
+ section?: 'main' | 'secondary' | 'settings'
80
+ /** Sort order within section (auto-incremented if omitted) */
81
+ position?: number
82
+ /** Optional badge */
83
+ badge?: string | number
84
+ /** Permission required to access this page */
85
+ permission?: { feature: string; action: PermissionAction }
86
+ /** Child pages — renders as a dropdown in topbar, collapsible in sidebar.
87
+ * Children can omit `component` to be navigation-only links (pointing to routes the parent handles). */
88
+ children?: Array<Omit<PageConfig, 'component'> & { component?: React.ComponentType }>
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Config
93
+ // ---------------------------------------------------------------------------
94
+ export interface SaasAppConfig {
95
+ /** App display name (used in sidebar header) */
96
+ name: string
97
+ /** Logo — string renders as initial badge, ReactNode renders as-is */
98
+ logo?: string | React.ReactNode
99
+ /** Supabase project URL — one project per SaaS (core in saas_core schema, data in public) */
100
+ supabaseUrl?: string
101
+ /** Supabase anon key */
102
+ supabaseAnonKey?: string
103
+ /** Theme overrides — use SaasTheme (friendly) or CreateThemeOptions (granular) */
104
+ theme?: CreateThemeOptions | SaasTheme
105
+ /** Default theme mode: 'light' or 'dark'. User can toggle. Default: 'light' */
106
+ defaultThemeMode?: 'light' | 'dark'
107
+ /** Layout variant (default: 'sidebar') */
108
+ layout?: 'sidebar' | 'topbar' | 'minimal'
109
+ /** Content floats in a rounded frame over the sidebar background (default: true). Set false to disable. */
110
+ sidebarFrame?: boolean
111
+ /** Vertical-specific pages */
112
+ pages: PageConfig[]
113
+ /** Plugins to register */
114
+ plugins?: PluginManifest[]
115
+ /** Runtime plugin activation for tenant-aware apps */
116
+ pluginRuntime?: {
117
+ /** Opt into loading tenant plugin bindings from the backend RPC. Static apps should leave this off. */
118
+ hydrateTenantPlugins?: boolean
119
+ tenantPlugins?: TenantPluginBinding[]
120
+ resolveTenantPlugins?: (context: {
121
+ tenant: { id: string; slug: string; verticalId?: string; plan?: string } | null
122
+ user: { id: string } | null
123
+ }) => TenantPluginBinding[] | undefined
124
+ }
125
+ /** Router adapter (default: hashRouterAdapter) */
126
+ router?: RouterAdapter
127
+ /** Billing configuration */
128
+ billing?: {
129
+ plans: PlanConfig[]
130
+ stripePublishableKey?: string
131
+ }
132
+ /** AI chat configuration */
133
+ chat?: {
134
+ enabled?: boolean
135
+ systemPrompt?: string
136
+ apiEndpoint?: string
137
+ title?: string
138
+ }
139
+ /** Notification configuration */
140
+ notifications?: {
141
+ changelogUrl?: string
142
+ }
143
+ /** Locale / i18n configuration */
144
+ locale?: {
145
+ default?: string
146
+ supported?: string[]
147
+ translations?: Record<string, Record<string, string>>
148
+ /** ISO 4217 currency code for all currency fields (e.g. 'BRL'). Inferred from locale when omitted. */
149
+ currency?: string
150
+ }
151
+ /** Mock user for demo/dev (will be replaced by auth in production) */
152
+ user?: {
153
+ fullName: string
154
+ email: string
155
+ avatarUrl?: string
156
+ }
157
+ /** Auth configuration */
158
+ auth?: {
159
+ adapter?: AuthAdapter | 'mock' | 'supabase'
160
+ requireAuth?: boolean
161
+ loginLogo?: React.ReactNode
162
+ loginLayout?: 'split' | 'centered'
163
+ loginTagline?: string
164
+ loginDescription?: string
165
+ showOAuth?: boolean
166
+ oauthProviders?: Exclude<AuthProvider, 'email'>[]
167
+ }
168
+ /** Organization / multi-tenant configuration */
169
+ organization?: {
170
+ adapter?: OrgAdapter | 'mock' | 'supabase'
171
+ multiOrg?: boolean
172
+ }
173
+ /** Vertical/niche ID — auto-set on tenant creation, skips niche selection in onboarding */
174
+ verticalId?: string
175
+ /** Permission profiles configuration */
176
+ permissions?: PermissionsConfig
177
+ /** Additional settings tabs to merge with built-in ones */
178
+ settingsTabs?: { id: string; label: string; component: React.ReactNode }[]
179
+ /** Set to false to hide built-in Settings page (default: true) */
180
+ showSettings?: boolean
181
+ /** Set to false to hide built-in Billing page (default: true when plans provided) */
182
+ showBilling?: boolean
183
+ /** Custom mobile bottom nav items (max 5). If omitted, auto-picks first main items. */
184
+ bottomNav?: Array<{ label: string; icon: string; route: string }>
185
+ }
186
+
187
+ // ---------------------------------------------------------------------------
188
+ // Internal: build NavigationItem[] from PageConfig[] + built-ins
189
+ // ---------------------------------------------------------------------------
190
+ type RouteEntry = {
191
+ component: React.ComponentType<any>
192
+ permission?: PluginPermissionRequirement
193
+ plugin?: ResolvedPluginManifest
194
+ fullBleed?: boolean
195
+ }
196
+
197
+ function sortNavigation(
198
+ navigation: (NavigationItem & { permission?: PluginPermissionRequirement })[],
199
+ ): (NavigationItem & { permission?: PluginPermissionRequirement })[] {
200
+ return [...navigation].sort((a, b) => {
201
+ const sectionOrder = { main: 0, secondary: 1, settings: 2 }
202
+ const sa = sectionOrder[a.section] ?? 1
203
+ const sb = sectionOrder[b.section] ?? 1
204
+ if (sa !== sb) return sa - sb
205
+ return a.position - b.position
206
+ })
207
+ }
208
+
209
+ function buildNavigation(
210
+ pages: PageConfig[],
211
+ config: SaasAppConfig
212
+ ): { navigation: (NavigationItem & { permission?: PluginPermissionRequirement })[]; routes: Map<string, RouteEntry>; entityRouteMap: Map<string, string> } {
213
+ const navigation: (NavigationItem & { permission?: PluginPermissionRequirement })[] = []
214
+ const routes = new Map<string, RouteEntry>()
215
+ // Maps "archetype:kind" (e.g. "person:staff") or "archetype" to route path
216
+ const entityRouteMap = new Map<string, string>()
217
+
218
+ // Vertical pages
219
+ let mainPos = 0
220
+ let secondaryPos = 0
221
+ for (const page of pages) {
222
+ const section = page.section ?? 'main'
223
+ const position = page.position ?? (section === 'main' ? mainPos++ : secondaryPos++)
224
+
225
+ // Build child navigation items + routes
226
+ const childNavItems: NavigationItem[] = []
227
+ if (page.children) {
228
+ for (const child of page.children) {
229
+ childNavItems.push({
230
+ id: child.path.slice(1).replace(/\//g, '-'),
231
+ label: child.label,
232
+ icon: child.icon,
233
+ route: child.path,
234
+ section,
235
+ position: 0,
236
+ badge: child.badge,
237
+ })
238
+ // Only register route if child has its own component and path isn't already handled
239
+ if (child.component && !routes.has(child.path)) {
240
+ if ((child.component as any)?.__isCrudPage) {
241
+ ;(child.component as any).__crudBasePath = child.path
242
+ const eDef = (child.component as any).__entityDef
243
+ if (eDef?.data?.archetypeKind) {
244
+ entityRouteMap.set(`${eDef.data.archetype ?? ''}:${eDef.data.archetypeKind}`, child.path)
245
+ } else if (eDef?.data?.archetype) {
246
+ entityRouteMap.set(eDef.data.archetype, child.path)
247
+ }
248
+ }
249
+ routes.set(child.path, { component: child.component, permission: child.permission })
250
+ }
251
+ }
252
+ }
253
+
254
+ navigation.push({
255
+ id: page.path === '/' ? 'home' : page.path.slice(1).replace(/\//g, '-'),
256
+ label: page.label,
257
+ icon: page.icon,
258
+ route: page.path,
259
+ section,
260
+ position,
261
+ badge: page.badge,
262
+ permission: page.permission,
263
+ ...(childNavItems.length > 0 ? { children: childNavItems } : {}),
264
+ })
265
+ // Set basePath on CRUD pages so they know their mount point
266
+ if ((page.component as any)?.__isCrudPage) {
267
+ ;(page.component as any).__crudBasePath = page.path
268
+ const eDef = (page.component as any).__entityDef
269
+ if (eDef?.data?.archetypeKind) {
270
+ entityRouteMap.set(`${eDef.data.archetype ?? ''}:${eDef.data.archetypeKind}`, page.path)
271
+ } else if (eDef?.data?.archetype) {
272
+ entityRouteMap.set(eDef.data.archetype, page.path)
273
+ }
274
+ }
275
+ routes.set(page.path, { component: page.component, permission: page.permission })
276
+ }
277
+
278
+ // Built-in Billing page (route exists but no sidebar nav item — accessed via user dropdown)
279
+ const showBilling = config.showBilling ?? (config.billing?.plans && config.billing.plans.length > 0)
280
+ if (showBilling) {
281
+ routes.set('/billing', { component: BillingPage })
282
+ }
283
+
284
+ // Built-in Settings page (route exists but no sidebar nav item — accessed via user dropdown)
285
+ const showSettings = config.showSettings !== false
286
+ if (showSettings) {
287
+ routes.set('/settings', { component: SettingsPage })
288
+ }
289
+
290
+ return { navigation: sortNavigation(navigation), routes, entityRouteMap }
291
+ }
292
+
293
+ // ---------------------------------------------------------------------------
294
+ // Internal: render logo from string or ReactNode
295
+ // ---------------------------------------------------------------------------
296
+ // ---------------------------------------------------------------------------
297
+ // Global entity search — queries persons, products, services in parallel
298
+ // ---------------------------------------------------------------------------
299
+
300
+ function createGlobalEntitySearch(): EntitySearchFn {
301
+ return async (query: string): Promise<EntitySearchResult[]> => {
302
+ const supabase = getSupabaseClientOptional()
303
+ if (!supabase) return []
304
+
305
+ const term = `%${query}%`
306
+ const limit = 8
307
+
308
+ const [persons, products, services] = await Promise.all([
309
+ supabase.schema('saas_core').from('persons')
310
+ .select('id, name, email, phone, kind')
311
+ .eq('is_active', true)
312
+ .ilike('name', term)
313
+ .limit(limit)
314
+ .then((r: any) => r.data ?? []),
315
+ supabase.schema('saas_core').from('products')
316
+ .select('id, name, sku, price')
317
+ .eq('is_active', true)
318
+ .ilike('name', term)
319
+ .limit(limit)
320
+ .then((r: any) => r.data ?? []),
321
+ supabase.schema('saas_core').from('services')
322
+ .select('id, name, price, duration_minutes')
323
+ .eq('is_active', true)
324
+ .ilike('name', term)
325
+ .limit(limit)
326
+ .then((r: any) => r.data ?? []),
327
+ ])
328
+
329
+ const results: EntitySearchResult[] = []
330
+
331
+ for (const p of persons) {
332
+ const kind = p.kind ? String(p.kind) : 'person'
333
+ const kindLabel = kind.charAt(0).toUpperCase() + kind.slice(1) + 's'
334
+ results.push({
335
+ id: p.id,
336
+ label: p.name,
337
+ subtitle: [p.email, p.phone].filter(Boolean).join(' · ') || undefined,
338
+ group: kindLabel,
339
+ icon: 'User',
340
+ data: { ...p, archetype: 'person' },
341
+ })
342
+ }
343
+ for (const p of products) {
344
+ results.push({
345
+ id: p.id,
346
+ label: p.name,
347
+ subtitle: [p.sku, p.price != null ? `$${p.price}` : null].filter(Boolean).join(' · ') || undefined,
348
+ group: 'Products',
349
+ icon: 'Box',
350
+ data: { ...p, archetype: 'product' },
351
+ })
352
+ }
353
+ for (const s of services) {
354
+ results.push({
355
+ id: s.id,
356
+ label: s.name,
357
+ subtitle: [
358
+ s.duration_minutes ? `${s.duration_minutes}min` : null,
359
+ s.price != null ? `$${s.price}` : null,
360
+ ].filter(Boolean).join(' · ') || undefined,
361
+ group: 'Services',
362
+ icon: 'Sparkles',
363
+ data: { ...s, archetype: 'service' },
364
+ })
365
+ }
366
+
367
+ return results
368
+ }
369
+ }
370
+
371
+ function CommandPaletteWrapper({ navigation, routerAdapter }: { navigation: NavigationItem[]; routerAdapter: RouterAdapter }) {
372
+ const { commandPaletteOpen, setCommandPaletteOpen } = useLayoutStore()
373
+
374
+ const entitySearch = React.useMemo(() => createGlobalEntitySearch(), [])
375
+
376
+ const commands: CommandItem[] = React.useMemo(() => {
377
+ const items: CommandItem[] = []
378
+ for (const nav of navigation) {
379
+ items.push({
380
+ id: nav.id,
381
+ label: nav.label,
382
+ icon: nav.icon,
383
+ group: 'Pages',
384
+ action: () => routerAdapter.navigate(nav.route),
385
+ })
386
+ if ((nav as any).children) {
387
+ for (const child of (nav as any).children) {
388
+ items.push({
389
+ id: child.id,
390
+ label: child.label,
391
+ icon: child.icon,
392
+ group: nav.label,
393
+ action: () => routerAdapter.navigate(child.route),
394
+ })
395
+ }
396
+ }
397
+ }
398
+ // System actions
399
+ items.push(
400
+ { id: 'settings', label: 'Settings', icon: 'Settings', group: 'System', action: () => routerAdapter.navigate('/settings') },
401
+ { id: 'billing', label: 'Billing', icon: 'CreditCard', group: 'System', action: () => routerAdapter.navigate('/billing') },
402
+ )
403
+ return items
404
+ }, [navigation, routerAdapter])
405
+
406
+ const handleEntitySelect = React.useCallback((result: EntitySearchResult) => {
407
+ const archetype = result.data?.archetype ? String(result.data.archetype) : undefined
408
+ const kind = result.data?.kind ? String(result.data.kind) : undefined
409
+ const href = resolveEntityHref(result.id, archetype, kind)
410
+ routerAdapter.navigate(href)
411
+ }, [routerAdapter])
412
+
413
+ return React.createElement(CommandPalette, {
414
+ commands,
415
+ open: commandPaletteOpen,
416
+ onOpenChange: setCommandPaletteOpen,
417
+ onEntitySearch: entitySearch,
418
+ onEntitySelect: handleEntitySelect,
419
+ })
420
+ }
421
+
422
+ function renderLogo(name: string, logo?: string | React.ReactNode): React.ReactNode {
423
+ if (logo && typeof logo !== 'string') return logo
424
+ const initial = typeof logo === 'string' ? logo : name.charAt(0).toUpperCase()
425
+ return React.createElement('div', { className: 'flex items-center gap-3' },
426
+ React.createElement('div', {
427
+ className: 'w-8 h-8 rounded-lg bg-primary flex items-center justify-center text-primary-foreground font-bold text-sm',
428
+ }, initial),
429
+ React.createElement('span', { className: 'font-bold text-lg' }, name),
430
+ )
431
+ }
432
+
433
+ // ---------------------------------------------------------------------------
434
+ // Internal: resolve auth adapter from config
435
+ // ---------------------------------------------------------------------------
436
+ function resolveAuthAdapter(config: SaasAppConfig): AuthAdapter | null {
437
+ if (!config.auth?.adapter) return null
438
+ if (typeof config.auth.adapter === 'object') return config.auth.adapter
439
+ if (config.auth.adapter === 'mock') return createMockAuthAdapter()
440
+ if (config.auth.adapter === 'supabase') {
441
+ if (!getSupabaseClientOptional()) return createMockAuthAdapter()
442
+ return createSupabaseAuthAdapter()
443
+ }
444
+ return null
445
+ }
446
+
447
+ // ---------------------------------------------------------------------------
448
+ // Internal: resolve org adapter from config
449
+ // ---------------------------------------------------------------------------
450
+ function resolveOrgAdapter(config: SaasAppConfig): OrgAdapter | null {
451
+ if (!config.organization?.adapter) return null
452
+ if (typeof config.organization.adapter === 'object') return config.organization.adapter
453
+ if (config.organization.adapter === 'mock') {
454
+ return createMockOrgAdapter(config.permissions?.defaultProfiles)
455
+ }
456
+ if (config.organization.adapter === 'supabase') {
457
+ if (!getSupabaseClientOptional()) return createMockOrgAdapter(config.permissions?.defaultProfiles)
458
+ return createSupabaseOrgAdapter()
459
+ }
460
+ return null
461
+ }
462
+
463
+ function buildSettingsTabs(
464
+ config: SaasAppConfig,
465
+ pluginTabs: PluginSettingsTab[],
466
+ can: (feature: string, action: PermissionAction) => boolean,
467
+ t: (key: string, params?: Record<string, string | number>) => string,
468
+ ): { id: string; label: string; icon?: React.ReactNode; component: React.ReactNode }[] {
469
+ const settingsTabs: { id: string; label: string; icon?: React.ReactNode; component: React.ReactNode }[] = []
470
+
471
+ if (config.organization) {
472
+ settingsTabs.push(
473
+ { id: 'team', label: t('settings.team'), icon: React.createElement(Users, { className: 'h-4 w-4' }), component: React.createElement(TeamTab) },
474
+ { id: 'permissions', label: t('settings.permissions'), icon: React.createElement(ShieldCheck, { className: 'h-4 w-4' }), component: React.createElement(PermissionProfilesTab) },
475
+ { id: 'locations', label: t('settings.locations'), icon: React.createElement(MapPin, { className: 'h-4 w-4' }), component: React.createElement(LocationsCrudPage) },
476
+ { id: 'field-rules', label: t('settings.fieldRules'), icon: React.createElement(SlidersHorizontal, { className: 'h-4 w-4' }), component: React.createElement(ConnectedFieldRulesSettings) },
477
+ )
478
+ }
479
+
480
+ if (config.settingsTabs) {
481
+ settingsTabs.push(...config.settingsTabs)
482
+ }
483
+
484
+ for (const tab of pluginTabs) {
485
+ if (tab.permission && !can(tab.permission.feature, tab.permission.action)) {
486
+ continue
487
+ }
488
+
489
+ const IconComp = tab.icon ? (LucideIcons as any)[tab.icon] ?? Puzzle : Puzzle
490
+ // Try to translate plugin settings tab label via t('settings.plugin.{id}'), fallback to raw label
491
+ const pluginLabelKey = `settings.plugin.${tab.id}`
492
+ const pluginLabel = t(pluginLabelKey)
493
+ settingsTabs.push({
494
+ id: tab.id,
495
+ label: pluginLabel === pluginLabelKey ? tab.label : pluginLabel,
496
+ icon: React.createElement(IconComp, { className: 'h-4 w-4' }),
497
+ component: React.createElement(tab.component),
498
+ isPlugin: true,
499
+ } as any)
500
+ }
501
+
502
+ return settingsTabs
503
+ }
504
+
505
+ // ---------------------------------------------------------------------------
506
+ // createSaasApp — returns a ready-to-render App component
507
+ // ---------------------------------------------------------------------------
508
+ export function createSaasApp(config: SaasAppConfig): React.FC {
509
+ // Initialize Supabase — single project, core tables in saas_core schema
510
+ if (config.supabaseUrl && config.supabaseAnonKey) {
511
+ createSupabaseClient(config.supabaseUrl, config.supabaseAnonKey)
512
+ }
513
+
514
+ const routerAdapter = config.router ?? hashRouterAdapter()
515
+ setGlobalRouter(routerAdapter)
516
+
517
+ const { navigation: baseNavigation, routes: baseRoutes, entityRouteMap } = buildNavigation(config.pages, config)
518
+ setEntityRouteMap(entityRouteMap)
519
+ const layout = config.layout ?? 'sidebar'
520
+ const logoNode = renderLogo(config.name, config.logo)
521
+
522
+ // Resolve adapters at creation time
523
+ const authAdapter = resolveAuthAdapter(config)
524
+ const orgAdapter = resolveOrgAdapter(config)
525
+ const requireAuth = config.auth?.requireAuth ?? !!authAdapter
526
+
527
+ // Login page component
528
+ const LoginPageWrapper: React.FC = () => {
529
+ return React.createElement(LoginPage, {
530
+ appName: config.name,
531
+ logo: config.auth?.loginLogo ?? logoNode,
532
+ layout: config.auth?.loginLayout ?? 'split',
533
+ tagline: config.auth?.loginTagline,
534
+ description: config.auth?.loginDescription,
535
+ showOAuth: config.auth?.showOAuth,
536
+ oauthProviders: config.auth?.oauthProviders,
537
+ onSuccess: () => { routerAdapter.navigate('/') },
538
+ })
539
+ }
540
+
541
+ // The inner app (authenticated content)
542
+ const AppContent: React.FC = () => {
543
+ const route = routerAdapter.usePathname()
544
+ const authUser = useAuthStore((s) => s.user)
545
+ const currentOrg = useOrganizationStore((s) => s.currentOrg)
546
+ const currentProfile = usePermissionsStore((s) => s.currentProfile)
547
+ const { can } = usePermission()
548
+ const shouldHydrateTenantPlugins =
549
+ config.pluginRuntime?.hydrateTenantPlugins === true &&
550
+ !config.pluginRuntime?.resolveTenantPlugins &&
551
+ !config.pluginRuntime?.tenantPlugins
552
+ const { tenantPlugins: hydratedTenantPlugins } = useTenantPlugins({ enabled: shouldHydrateTenantPlugins })
553
+ const { t } = useTranslation()
554
+
555
+ // Derive display user from auth or config fallback
556
+ const user = authUser
557
+ ? { fullName: authUser.fullName, email: authUser.email, avatarUrl: authUser.avatarUrl }
558
+ : config.user
559
+
560
+ // Merge plugin-declared features into permission store (must be before any early return)
561
+ React.useEffect(() => {
562
+ const pf = config.plugins?.flatMap((p) => p.declaredFeatures ?? []) ?? []
563
+ if (pf.length === 0) return
564
+ const store = usePermissionsStore.getState()
565
+ const existing = new Set(store.features.map((f) => f.id))
566
+ const toAdd = pf.filter((f) => !existing.has(f.id))
567
+ if (toAdd.length > 0) store.setFeatures([...store.features, ...toAdd])
568
+ }, [])
569
+
570
+ // If we're on the login route, render login page
571
+ if (route === '/login') {
572
+ return React.createElement(LoginPageWrapper)
573
+ }
574
+
575
+ const tenantPluginBindings = config.pluginRuntime?.resolveTenantPlugins?.({
576
+ tenant: currentOrg
577
+ ? {
578
+ id: currentOrg.id,
579
+ slug: currentOrg.slug,
580
+ verticalId: currentOrg.verticalId,
581
+ plan: currentOrg.plan,
582
+ }
583
+ : null,
584
+ user: authUser ? { id: authUser.id } : null,
585
+ }) ?? config.pluginRuntime?.tenantPlugins ?? hydratedTenantPlugins
586
+ const usesHydratedTenantBindings = shouldHydrateTenantPlugins && !!currentOrg
587
+
588
+ const initialPluginRuntime = resolvePluginRuntime({
589
+ plugins: config.plugins as any,
590
+ tenantPlugins: tenantPluginBindings as any,
591
+ hasTenantBindings: usesHydratedTenantBindings ? true : undefined,
592
+ context: {
593
+ tenant: currentOrg
594
+ ? {
595
+ id: currentOrg.id,
596
+ slug: currentOrg.slug,
597
+ verticalId: currentOrg.verticalId,
598
+ plan: currentOrg.plan,
599
+ }
600
+ : null,
601
+ user: authUser
602
+ ? {
603
+ id: authUser.id,
604
+ role: currentProfile?.id,
605
+ }
606
+ : null,
607
+ currentPath: route,
608
+ matchedPath: route,
609
+ layout,
610
+ hasPermission: (requirement) => can(requirement.feature, requirement.action),
611
+ },
612
+ })
613
+
614
+ const navigation = sortNavigation([
615
+ ...baseNavigation,
616
+ ...initialPluginRuntime.navigation.map((item) => ({
617
+ id: item.id ?? item.route,
618
+ label: item.label,
619
+ icon: item.icon ?? 'Package',
620
+ route: item.route,
621
+ section: item.section,
622
+ position: item.position,
623
+ badge: item.badge,
624
+ permission: item.permission,
625
+ })),
626
+ ])
627
+ const routes = new Map(baseRoutes)
628
+ for (const pluginRoute of initialPluginRuntime.routes) {
629
+ routes.set(pluginRoute.path, {
630
+ component: pluginRoute.component as any,
631
+ permission: pluginRoute.permission,
632
+ plugin: pluginRoute.plugin as any,
633
+ fullBleed: pluginRoute.fullBleed,
634
+ })
635
+ }
636
+
637
+ // Resolve page: exact match first, then prefix match for CRUD sub-routes and plugin routes
638
+ let matchedPath = route
639
+ let routeEntry = routes.get(route)
640
+ if (!routeEntry) {
641
+ // Try prefix match (e.g. /services/new → /services, /financial/settings/... → /financial)
642
+ for (const [path, entry] of routes) {
643
+ if (path !== '/' && route.startsWith(path + '/')) {
644
+ routeEntry = entry
645
+ matchedPath = path
646
+ break
647
+ }
648
+ }
649
+ }
650
+ const pluginRuntime = {
651
+ ...initialPluginRuntime,
652
+ context: {
653
+ ...initialPluginRuntime.context,
654
+ matchedPath,
655
+ },
656
+ }
657
+ const NotFoundPage: React.FC = () => React.createElement('div', { className: 'flex flex-col items-center justify-center py-24 text-center' },
658
+ React.createElement('h1', { className: 'text-6xl font-bold text-muted-foreground/20 mb-4' }, t('app.notFound.title')),
659
+ React.createElement('h2', { className: 'text-xl font-semibold mb-2' }, t('app.notFound.heading')),
660
+ React.createElement('p', { className: 'text-sm text-muted-foreground mb-6' }, t('app.notFound.message')),
661
+ React.createElement('button', {
662
+ className: 'rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors',
663
+ onClick: () => routerAdapter.navigate('/'),
664
+ }, t('app.notFound.goToDashboard')),
665
+ )
666
+ const PageComponent = routeEntry?.component ?? NotFoundPage
667
+ const pagePermission = routeEntry?.permission
668
+ const pageTitle = navigation.find((n) => n.route === matchedPath)?.label ?? navigation.find((n) => n.route === route)?.label ?? navigation[0]?.label ?? ''
669
+ const settingsTabs = buildSettingsTabs(config, pluginRuntime.settingsTabs as any, can, t)
670
+
671
+ const handleSignOut = async () => {
672
+ if (authAdapter) {
673
+ try {
674
+ await authAdapter.signOut()
675
+ useAuthStore.getState().reset()
676
+ useAuthStore.getState().setInitialized(true)
677
+ } catch {
678
+ // ignore sign out errors
679
+ }
680
+ routerAdapter.navigate('/login')
681
+ }
682
+ }
683
+
684
+ const renderedPage = matchedPath === '/settings' && config.showSettings !== false
685
+ ? React.createElement(SettingsPage, {
686
+ extraTabs: settingsTabs,
687
+ beforeContent: React.createElement(WidgetSlot, {
688
+ zone: 'settings.before',
689
+ className: 'space-y-4',
690
+ contextOverrides: { matchedPath },
691
+ }),
692
+ afterContent: React.createElement(WidgetSlot, {
693
+ zone: 'settings.after',
694
+ className: 'space-y-4',
695
+ contextOverrides: { matchedPath },
696
+ }),
697
+ })
698
+ : React.createElement(PageComponent, routeEntry?.plugin ? {
699
+ plugin: routeEntry.plugin,
700
+ runtime: pluginRuntime.context,
701
+ config: routeEntry.plugin.config,
702
+ } : undefined)
703
+
704
+ // Build page element — wrap in PermissionGate if page has permission config.
705
+ // Full-bleed routes (chat, kanban, canvas) opt out of the p-6 padding +
706
+ // animation wrapper and fill the content region edge-to-edge.
707
+ const fullBleed = Boolean(routeEntry?.fullBleed)
708
+ const pageContent = fullBleed
709
+ ? React.createElement('div', { key: matchedPath, className: 'h-full min-h-0 overflow-hidden' }, renderedPage)
710
+ : React.createElement('div', { key: matchedPath, className: 'saas-page-enter space-y-6 p-6' },
711
+ React.createElement(WidgetSlot, {
712
+ zone: 'page.before',
713
+ className: 'space-y-4',
714
+ contextOverrides: { matchedPath },
715
+ }),
716
+ renderedPage,
717
+ React.createElement(WidgetSlot, {
718
+ zone: 'page.after',
719
+ className: 'space-y-4',
720
+ contextOverrides: { matchedPath },
721
+ }),
722
+ )
723
+ const pageElement: React.ReactNode = pagePermission
724
+ ? React.createElement(
725
+ PermissionGate,
726
+ {
727
+ feature: pagePermission.feature,
728
+ action: pagePermission.action,
729
+ fallback: React.createElement('div', { className: 'flex items-center justify-center py-24' },
730
+ React.createElement('div', { className: 'text-center' },
731
+ React.createElement('h2', { className: 'text-xl font-semibold mb-2' }, t('app.accessDenied.heading')),
732
+ React.createElement('p', { className: 'text-muted-foreground' }, t('app.accessDenied.message'))
733
+ )
734
+ ),
735
+ children: pageContent,
736
+ },
737
+ )
738
+ : pageContent
739
+
740
+ // Build orgSwitcher if configured
741
+ const orgSwitcherElement = orgAdapter
742
+ ? React.createElement(OrgSwitcher)
743
+ : undefined
744
+
745
+ const content = React.createElement(
746
+ PluginRuntimeProvider,
747
+ { value: pluginRuntime },
748
+ React.createElement(
749
+ React.Fragment,
750
+ null,
751
+ React.createElement(MockModeBanner),
752
+ React.createElement(ImpersonationBanner),
753
+ React.createElement(
754
+ AppShell,
755
+ {
756
+ variant: layout,
757
+ contentFrame: config.sidebarFrame !== false,
758
+ navigation: navigation as unknown as UiNavigationItem[],
759
+ user,
760
+ pageTitle: undefined,
761
+ currentPath: matchedPath,
762
+ onNavigate: (path: string) => { routerAdapter.navigate(path) },
763
+ onSignOut: authAdapter ? handleSignOut : () => console.log('sign out'),
764
+ onSettings: () => { routerAdapter.navigate('/settings') },
765
+ onBilling: routes.has('/billing') ? () => { routerAdapter.navigate('/billing') } : undefined,
766
+ logo: logoNode,
767
+ orgSwitcher: orgSwitcherElement,
768
+ topbarStart: React.createElement(WidgetSlot, {
769
+ zone: 'shell.topbar.start',
770
+ className: 'flex items-center gap-2',
771
+ contextOverrides: { matchedPath },
772
+ }),
773
+ topbarEnd: React.createElement(WidgetSlot, {
774
+ zone: 'shell.topbar.end',
775
+ className: 'flex items-center gap-2',
776
+ contextOverrides: { matchedPath },
777
+ }),
778
+ sidebarTopContent: React.createElement(WidgetSlot, {
779
+ zone: 'shell.sidebar.before-nav',
780
+ className: 'space-y-2',
781
+ contextOverrides: { matchedPath },
782
+ }),
783
+ sidebarFooterContent: React.createElement(WidgetSlot, {
784
+ zone: 'shell.sidebar.footer',
785
+ className: 'space-y-2',
786
+ contextOverrides: { matchedPath },
787
+ }),
788
+ bottomNav: config.bottomNav,
789
+ },
790
+ pageElement,
791
+ ),
792
+ // Org initializer
793
+ orgAdapter ? React.createElement(OrgInitializer, { verticalId: config.verticalId }) : null,
794
+ // Location switch overlay
795
+ React.createElement(LocationSwitchOverlay),
796
+ // Chat
797
+ config.chat?.enabled !== false && config.chat
798
+ ? React.createElement(React.Fragment, null,
799
+ React.createElement(ChatFab, { apiEndpoint: config.chat.apiEndpoint, systemPrompt: config.chat.systemPrompt }),
800
+ React.createElement(ChatPanel, { title: config.chat.title, apiEndpoint: config.chat.apiEndpoint, systemPrompt: config.chat.systemPrompt }),
801
+ )
802
+ : null,
803
+ React.createElement(WidgetSlot, {
804
+ zone: 'shell.floating',
805
+ contextOverrides: { matchedPath },
806
+ }),
807
+ // Command palette
808
+ React.createElement(CommandPaletteWrapper, { navigation, routerAdapter }),
809
+ ),
810
+ )
811
+
812
+ // Wrap in ProtectedRoute if auth is required
813
+ if (requireAuth && authAdapter) {
814
+ return React.createElement(
815
+ ProtectedRoute,
816
+ { onUnauthenticated: () => { routerAdapter.navigate('/login') } },
817
+ content,
818
+ )
819
+ }
820
+
821
+ return content
822
+ }
823
+
824
+ // The full App component
825
+ const SaasApp: React.FC = () => {
826
+ const initialize = useThemeStore((s) => s.initialize)
827
+ const setOverrides = useThemeStore((s) => s.setOverrides)
828
+ const setPlans = useBillingStore((s) => s.setPlans)
829
+ const setFeatures = usePermissionsStore((s) => s.setFeatures)
830
+
831
+ const setMode = useThemeStore((s) => s.setMode)
832
+
833
+ React.useEffect(() => {
834
+ if (config.theme) {
835
+ const isSaasTheme = 'brand' in config.theme && ('radius' in config.theme || 'sidebar' in config.theme || 'font' in config.theme)
836
+ const resolved = isSaasTheme ? resolveTheme(config.theme as SaasTheme) : config.theme as CreateThemeOptions
837
+ setOverrides(resolved)
838
+ }
839
+ // Set default mode if no user preference saved
840
+ if (config.defaultThemeMode && !localStorage.getItem('saas-core:theme-mode')) {
841
+ setMode(config.defaultThemeMode)
842
+ }
843
+ initialize()
844
+ }, [initialize, setOverrides, setMode])
845
+
846
+ React.useEffect(() => {
847
+ if (config.billing?.plans) {
848
+ setPlans(config.billing.plans.map(normalizePlanConfig) as any)
849
+ }
850
+ }, [setPlans])
851
+
852
+ // Initialize permissions config (consumer-declared only; plugin features merged in AppContent)
853
+ React.useEffect(() => {
854
+ if (config.permissions?.features) {
855
+ setFeatures(config.permissions.features)
856
+ }
857
+ }, [setFeatures])
858
+
859
+ // Initialize locale on the unified @fayz-ai/core i18n.
860
+ React.useEffect(() => {
861
+ setCurrentLocale(config.locale?.default ?? 'en')
862
+ setDefaultCurrency(config.locale?.currency)
863
+ }, [])
864
+
865
+ let inner: React.ReactNode = React.createElement(
866
+ RouterProvider,
867
+ { value: routerAdapter },
868
+ React.createElement(ToastProvider, null),
869
+ React.createElement(AppContent),
870
+ )
871
+
872
+ // Wrap in OrgAdapterProvider if adapter is configured
873
+ if (orgAdapter) {
874
+ inner = React.createElement(
875
+ OrgAdapterProvider,
876
+ { value: orgAdapter, children: inner },
877
+ )
878
+ }
879
+
880
+ // Wrap in AuthAdapterProvider if adapter is configured
881
+ if (authAdapter) {
882
+ inner = React.createElement(
883
+ AuthAdapterProvider,
884
+ { value: authAdapter, children: inner },
885
+ )
886
+ }
887
+
888
+ // Merge built-in locale packs + plugin locales + consumer overrides
889
+ const mergedTranslations: Record<string, Record<string, string>> = {}
890
+ const supportedLocales = config.locale?.supported ?? ['en', 'pt-BR']
891
+ for (const loc of supportedLocales) {
892
+ // Layer 1: built-in core translations
893
+ const layer: Record<string, string> = { ...(builtInLocales[loc] ?? {}) }
894
+ // Layer 2: plugin translations (each plugin can ship its own locales)
895
+ if (config.plugins) {
896
+ for (const plugin of config.plugins) {
897
+ if (plugin.locales?.[loc]) {
898
+ Object.assign(layer, plugin.locales[loc])
899
+ }
900
+ }
901
+ }
902
+ // Layer 3: consumer app overrides (highest priority)
903
+ Object.assign(layer, config.locale?.translations?.[loc] ?? {})
904
+ mergedTranslations[loc] = layer
905
+ }
906
+ // Register into @fayz-ai/core so the shimmed useTranslation resolves these keys.
907
+ registerTranslations(mergedTranslations)
908
+
909
+ // Wrap in I18nProvider (outermost)
910
+ inner = React.createElement(I18nProvider, {
911
+ value: {
912
+ defaultLocale: config.locale?.default ?? 'en',
913
+ supported: supportedLocales,
914
+ translations: mergedTranslations,
915
+ }
916
+ }, inner)
917
+
918
+ return inner as React.JSX.Element
919
+ }
920
+
921
+ return SaasApp
922
+ }