@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,65 @@
1
+ import * as React from 'react'
2
+ import { hashRouterAdapter } from '@fayz-ai/core'
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Admin hash router — the same lightweight approach the storefront uses. The
6
+ // admin shell collects routes from the plugin runtime + manifest pages and
7
+ // switches on the current hash path. Deliberately not react-router: an SDK
8
+ // shell shipping a router creates singleton hazards under source-alias dev.
9
+ // ---------------------------------------------------------------------------
10
+
11
+ const adapter = hashRouterAdapter()
12
+
13
+ export function normalizeAdminPath(path: string): string {
14
+ const clean = `/${path}`.replace(/\/+/g, '/')
15
+ return clean.length > 1 ? clean.replace(/\/+$/, '') : clean
16
+ }
17
+
18
+ export function navigateTo(to: string): void {
19
+ adapter.navigate(normalizeAdminPath(to))
20
+ }
21
+
22
+ export function useAdminPath(): string {
23
+ const [path, setPath] = React.useState(() => normalizeAdminPath(adapter.getCurrentPath() || '/'))
24
+ React.useEffect(() => {
25
+ return adapter.onPathChange((p) => {
26
+ setPath(normalizeAdminPath(p || '/'))
27
+ if (typeof window !== 'undefined') window.scrollTo(0, 0)
28
+ })
29
+ }, [])
30
+ return path
31
+ }
32
+
33
+ /** Match '/courses/:id' against '/courses/abc' → { id: 'abc' }; supports a
34
+ * trailing '/*' wildcard (matches the base and any deeper path). */
35
+ export function matchRoute(pattern: string, path: string): Record<string, string> | null {
36
+ const cleanPath = (path.split('?')[0] ?? '').replace(/\/+$/, '') || '/'
37
+ if (pattern.endsWith('/*')) {
38
+ const base = pattern.slice(0, -2)
39
+ if (cleanPath === base || cleanPath.startsWith(`${base}/`)) return {}
40
+ return null
41
+ }
42
+ const patternParts = pattern.split('/').filter(Boolean)
43
+ const pathParts = cleanPath.split('/').filter(Boolean)
44
+ if (patternParts.length !== pathParts.length) return null
45
+ const params: Record<string, string> = {}
46
+ for (let i = 0; i < patternParts.length; i++) {
47
+ const pat = patternParts[i]!
48
+ const part = pathParts[i]!
49
+ if (pat.startsWith(':')) params[pat.slice(1)] = decodeURIComponent(part)
50
+ else if (pat !== part) return null
51
+ }
52
+ return params
53
+ }
54
+
55
+ /** Score a route pattern for specificity so the most specific match wins
56
+ * (static segments beat params; longer beats shorter; wildcard loses). */
57
+ export function routeScore(pattern: string): number {
58
+ const isWildcard = pattern.endsWith('/*')
59
+ const routePattern = isWildcard ? pattern.slice(0, -2) : pattern
60
+ const parts = routePattern.split('/').filter(Boolean)
61
+ let score = parts.length * 10
62
+ for (const p of parts) if (!p.startsWith(':')) score += 5
63
+ if (isWildcard) score -= 1
64
+ return score
65
+ }
@@ -0,0 +1,175 @@
1
+ import * as React from 'react'
2
+ import {
3
+ registerScaffold,
4
+ defineApp,
5
+ getPluginFactory,
6
+ getComponent,
7
+ } from '@fayz-ai/core'
8
+ import type {
9
+ AppManifest,
10
+ PluginManifest,
11
+ LocaleConfig,
12
+ PermissionsConfig,
13
+ BillingConfig as ManifestBillingConfig,
14
+ } from '@fayz-ai/core'
15
+ import { AdminProviders } from './createFayzApp'
16
+ import { AdminShell } from './AdminShell'
17
+ import type { FayzAppConfig, CustomPage } from './config'
18
+ import { createSaasApp, type SaasAppConfig } from '../shell/createSaasApp'
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // Admin scaffold — renders an admin app from a pure-data AppManifest. Mirrors
22
+ // the storefront scaffold's dual path: defineSaas turns a FayzAppConfig into a
23
+ // manifest (the sugar→data direction), and AdminScaffold turns a manifest back
24
+ // into the provider stack + shell (the data→render direction). Plugins live in
25
+ // the manifest as PluginRefs (id + JSON config) and are resolved to live
26
+ // PluginManifests through the plugin-factory registry, exactly the contract a
27
+ // generated app's plugins.generated.ts populates.
28
+ // ---------------------------------------------------------------------------
29
+
30
+ function slug(name: string): string {
31
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'app'
32
+ }
33
+
34
+ const liveConfigOption = '__fayzLiveConfigRef'
35
+ const liveSaasConfigs = new Map<string, FayzAppConfig | SaasAppConfig>()
36
+
37
+ function hasLegacyShellConfig(config: FayzAppConfig | SaasAppConfig): config is SaasAppConfig {
38
+ return 'organization' in config || 'settingsTabs' in config || 'bottomNav' in config || 'pluginRuntime' in config
39
+ }
40
+
41
+ function getAuthRequireAuth(config: FayzAppConfig | SaasAppConfig): boolean {
42
+ return config.auth?.requireAuth ?? true
43
+ }
44
+
45
+ function getLiveConfigRef(id: string): string {
46
+ return `code:${id}`
47
+ }
48
+
49
+ /** Fayz/Saas config → AppManifest (sugar → data). During the Beauty/Resto
50
+ * migration, config-authored apps keep a live config ref so renderApp can
51
+ * preserve code-backed plugins and custom pages while the JSON manifest path
52
+ * matures. Serialized manifests still resolve through plugin/page registries. */
53
+ export function defineSaas(config: FayzAppConfig | SaasAppConfig): AppManifest {
54
+ const id = slug(config.name)
55
+ const configRef = getLiveConfigRef(id)
56
+ liveSaasConfigs.set(configRef, config)
57
+ return defineApp({
58
+ id,
59
+ name: config.name,
60
+ backend: config.supabaseUrl
61
+ ? { provider: 'supabase', url: config.supabaseUrl }
62
+ : { provider: 'mock' },
63
+ locale: config.locale as LocaleConfig | undefined,
64
+ theme: config.theme as Record<string, unknown> | undefined,
65
+ permissions: config.permissions,
66
+ billing: config.billing as ManifestBillingConfig | undefined,
67
+ surfaces: {
68
+ admin: {
69
+ scaffold: 'admin',
70
+ plugins: (config.plugins ?? []).map((p) => ({ id: p.id })),
71
+ options: {
72
+ [liveConfigOption]: configRef,
73
+ layout: config.layout ?? 'sidebar',
74
+ requireAuth: getAuthRequireAuth(config),
75
+ },
76
+ },
77
+ },
78
+ })
79
+ }
80
+
81
+ /** Resolve a manifest's PluginRefs into live PluginManifests via the
82
+ * plugin-factory registry. Unknown ids are skipped with a console warning so a
83
+ * partial install degrades instead of crashing. */
84
+ function resolvePlugins(manifest: AppManifest, surfaceName: string): PluginManifest[] {
85
+ const refs = manifest.surfaces[surfaceName]?.plugins ?? []
86
+ const plugins: PluginManifest[] = []
87
+ for (const ref of refs) {
88
+ if (ref.enabled === false) continue
89
+ const factory = getPluginFactory(ref.id)
90
+ if (!factory) {
91
+ // eslint-disable-next-line no-console
92
+ console.warn(`[@fayz-ai/saas] No plugin factory registered for "${ref.id}". Did plugins.generated.ts run?`)
93
+ continue
94
+ }
95
+ plugins.push(factory(ref.config) as PluginManifest)
96
+ }
97
+ return plugins
98
+ }
99
+
100
+ /** Resolve manifest pages that reference a registered component id into the
101
+ * CustomPage shape the shell renders. (entity/blocks pages are handled by the
102
+ * plugins that own them; component pages are the app-level escape hatch.) */
103
+ function resolvePages(manifest: AppManifest, surfaceName: string): CustomPage[] {
104
+ const pages = manifest.surfaces[surfaceName]?.pages ?? []
105
+ const out: CustomPage[] = []
106
+ for (const p of pages) {
107
+ if (!p.component) continue
108
+ const Component = getComponent(p.component) as React.ComponentType | undefined
109
+ if (!Component) {
110
+ // eslint-disable-next-line no-console
111
+ console.warn(`[@fayz-ai/saas] Manifest page "${p.path}" references unregistered component "${p.component}".`)
112
+ continue
113
+ }
114
+ out.push({ path: p.path, label: p.label, icon: p.icon, section: p.section, component: Component })
115
+ }
116
+ return out
117
+ }
118
+
119
+ /** AppManifest → FayzAppConfig (data → render). */
120
+ function manifestToFayzConfig(manifest: AppManifest, surfaceName: string): FayzAppConfig {
121
+ const options = (manifest.surfaces[surfaceName]?.options ?? {}) as Record<string, unknown>
122
+ const liveConfigRef = options[liveConfigOption]
123
+ const liveConfig = typeof liveConfigRef === 'string' ? liveSaasConfigs.get(liveConfigRef) : undefined
124
+ if (liveConfig && !hasLegacyShellConfig(liveConfig)) return liveConfig
125
+
126
+ return {
127
+ name: manifest.name,
128
+ supabaseUrl: manifest.backend?.provider === 'supabase' ? manifest.backend.url : undefined,
129
+ plugins: resolvePlugins(manifest, surfaceName),
130
+ pages: resolvePages(manifest, surfaceName),
131
+ theme: manifest.theme as FayzAppConfig['theme'],
132
+ layout: (options.layout as FayzAppConfig['layout']) ?? 'sidebar',
133
+ locale: manifest.locale as LocaleConfig | undefined,
134
+ permissions: manifest.permissions as PermissionsConfig | undefined,
135
+ billing: manifest.billing as FayzAppConfig['billing'],
136
+ auth: { requireAuth: (options.requireAuth as boolean) ?? true },
137
+ }
138
+ }
139
+
140
+ export function AdminScaffold({ manifest, surface }: { manifest: AppManifest; surface: string }) {
141
+ const liveConfigRef = manifest.surfaces[surface]?.options?.[liveConfigOption]
142
+ const liveConfig = typeof liveConfigRef === 'string' ? liveSaasConfigs.get(liveConfigRef) : undefined
143
+ const LegacyApp = React.useMemo(() => {
144
+ if (!liveConfig || !hasLegacyShellConfig(liveConfig)) return null
145
+ return createSaasApp(liveConfig)
146
+ }, [liveConfig])
147
+ const config = React.useMemo(() => manifestToFayzConfig(manifest, surface), [manifest, surface])
148
+
149
+ if (LegacyApp) return <LegacyApp />
150
+
151
+ return (
152
+ <AdminProviders config={config}>
153
+ <AdminShell
154
+ appName={config.name}
155
+ logo={config.logo}
156
+ layout={config.layout}
157
+ pages={config.pages}
158
+ requireAuth={config.auth?.requireAuth}
159
+ loginTagline={config.auth?.loginTagline}
160
+ loginDescription={config.auth?.loginDescription}
161
+ loginLogo={config.auth?.loginLogo}
162
+ loginLayout={config.auth?.loginLayout}
163
+ showOAuth={config.auth?.showOAuth}
164
+ oauthProviders={config.auth?.oauthProviders}
165
+ showSettings
166
+ showOrgSettings={Boolean(config.org)}
167
+ />
168
+ </AdminProviders>
169
+ )
170
+ }
171
+ AdminScaffold.displayName = 'AdminScaffold'
172
+
173
+ // Self-register so renderApp(manifest) can resolve the 'admin' scaffold once
174
+ // this package is imported.
175
+ registerScaffold('admin', AdminScaffold, { source: 'sdk', label: 'Admin' })
@@ -0,0 +1,160 @@
1
+ import { getSupabaseClientOptional } from '@fayz-ai/core'
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Types
5
+ // ---------------------------------------------------------------------------
6
+
7
+ export interface EntityLookupResult {
8
+ id: string
9
+ label: string
10
+ subtitle?: string
11
+ group?: string
12
+ icon?: string
13
+ price?: number
14
+ data?: Record<string, unknown>
15
+ }
16
+
17
+ export type EntityLookupMap = Record<string, EntityLookup>
18
+
19
+ export interface EntityLookup {
20
+ search(query: string): Promise<EntityLookupResult[]>
21
+ getById(id: string): Promise<EntityLookupResult | null>
22
+ list?(): Promise<EntityLookupResult[]>
23
+ }
24
+
25
+ export type ArchetypeType = 'person' | 'product' | 'service' | 'location'
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Archetype display config
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const ARCHETYPE_DISPLAY: Record<ArchetypeType, {
32
+ table: string
33
+ labelField: string
34
+ subtitleFields: string[]
35
+ priceField?: string
36
+ groupField?: string
37
+ }> = {
38
+ person: { table: 'persons', labelField: 'name', subtitleFields: ['phone'], groupField: 'kind' },
39
+ product: { table: 'products', labelField: 'name', subtitleFields: ['sku', 'description'], priceField: 'price' },
40
+ service: { table: 'services', labelField: 'name', subtitleFields: ['duration_minutes', 'description'], priceField: 'price' },
41
+ location: { table: 'locations', labelField: 'name', subtitleFields: ['city', 'address'], groupField: 'kind' },
42
+ }
43
+
44
+ interface ArchetypeLookupConfig {
45
+ archetype: ArchetypeType
46
+ kind?: string | string[]
47
+ kindLabels?: Record<string, string>
48
+ limit?: number
49
+ subtitleFields?: string[]
50
+ }
51
+
52
+ function toResult(
53
+ row: Record<string, unknown>,
54
+ display: typeof ARCHETYPE_DISPLAY[ArchetypeType],
55
+ kindLabels?: Record<string, string>,
56
+ ): EntityLookupResult {
57
+ const label = String(row[display.labelField] ?? row.name ?? '')
58
+ const subtitleParts = display.subtitleFields
59
+ .map((f) => row[f])
60
+ .filter(Boolean)
61
+ .map((v) => {
62
+ if (typeof v === 'number') return `${v}min`
63
+ return String(v)
64
+ })
65
+
66
+ let group: string | undefined
67
+ if (display.groupField && row[display.groupField]) {
68
+ const rawGroup = String(row[display.groupField])
69
+ group = kindLabels?.[rawGroup] ?? rawGroup
70
+ }
71
+
72
+ return {
73
+ id: String(row.id),
74
+ label,
75
+ subtitle: subtitleParts.length > 0 ? subtitleParts.join(' · ') : undefined,
76
+ group,
77
+ price: display.priceField ? Number(row[display.priceField]) || undefined : undefined,
78
+ data: row,
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Create an EntityLookup that queries a saas_core archetype table via Supabase.
84
+ * Returns empty results if Supabase is not configured.
85
+ */
86
+ export function createArchetypeLookup(config: ArchetypeLookupConfig): EntityLookup {
87
+ const baseDisplay = ARCHETYPE_DISPLAY[config.archetype]
88
+ const display = config.subtitleFields
89
+ ? { ...baseDisplay, subtitleFields: config.subtitleFields }
90
+ : baseDisplay
91
+ const limit = config.limit ?? 20
92
+ const kinds = config.kind
93
+ ? Array.isArray(config.kind) ? config.kind : [config.kind]
94
+ : undefined
95
+
96
+ function applyKindFilter(qb: any) {
97
+ if (kinds && kinds.length === 1) return qb.eq('kind', kinds[0])
98
+ if (kinds && kinds.length > 1) return qb.in('kind', kinds)
99
+ return qb
100
+ }
101
+
102
+ return {
103
+ async search(query: string) {
104
+ const supabase = getSupabaseClientOptional() as any
105
+ if (!supabase) return []
106
+
107
+ let qb = supabase
108
+ .schema('saas_core')
109
+ .from(display.table)
110
+ .select('*')
111
+ .eq('is_active', true)
112
+ .order(display.labelField, { ascending: true })
113
+ .limit(limit)
114
+
115
+ if (query) qb = qb.ilike(display.labelField, `%${query}%`)
116
+ qb = applyKindFilter(qb)
117
+
118
+ const { data } = await qb
119
+ return (data ?? []).map((row: Record<string, unknown>) =>
120
+ toResult(row, display, config.kindLabels),
121
+ )
122
+ },
123
+
124
+ async list() {
125
+ const supabase = getSupabaseClientOptional() as any
126
+ if (!supabase) return []
127
+
128
+ let qb = supabase
129
+ .schema('saas_core')
130
+ .from(display.table)
131
+ .select('*')
132
+ .eq('is_active', true)
133
+ .order(display.labelField, { ascending: true })
134
+ .limit(limit)
135
+
136
+ qb = applyKindFilter(qb)
137
+
138
+ const { data } = await qb
139
+ return (data ?? []).map((row: Record<string, unknown>) =>
140
+ toResult(row, display, config.kindLabels),
141
+ )
142
+ },
143
+
144
+ async getById(id: string) {
145
+ const supabase = getSupabaseClientOptional() as any
146
+ if (!supabase) return null
147
+
148
+ const { data } = await supabase
149
+ .schema('saas_core')
150
+ .from(display.table)
151
+ .select('*')
152
+ .eq('id', id)
153
+ .single()
154
+
155
+ return data
156
+ ? toResult(data as Record<string, unknown>, display, config.kindLabels)
157
+ : null
158
+ },
159
+ }
160
+ }
@@ -0,0 +1,7 @@
1
+ export { useBillingStore } from './store'
2
+ export type {
3
+ BillingStore,
4
+ BillingState,
5
+ Subscription,
6
+ Invoice,
7
+ } from './store'
@@ -0,0 +1,62 @@
1
+ import { create } from 'zustand'
2
+ import type { Plan } from '@fayz-ai/core'
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Types
6
+ // ---------------------------------------------------------------------------
7
+
8
+ export interface Subscription {
9
+ id: string
10
+ orgId: string
11
+ planId: string
12
+ status: 'active' | 'trialing' | 'past_due' | 'canceled' | 'incomplete'
13
+ currentPeriodStart: string
14
+ currentPeriodEnd: string
15
+ cancelAtPeriodEnd?: boolean
16
+ stripeSubscriptionId?: string
17
+ }
18
+
19
+ export interface Invoice {
20
+ id: string
21
+ orgId: string
22
+ amount: number
23
+ currency: string
24
+ status: 'paid' | 'open' | 'void' | 'uncollectible'
25
+ pdfUrl?: string
26
+ createdAt: string
27
+ }
28
+
29
+ export interface BillingState {
30
+ subscription: Subscription | null
31
+ invoices: Invoice[]
32
+ plans: Plan[]
33
+ loading: boolean
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Store
38
+ // ---------------------------------------------------------------------------
39
+
40
+ export interface BillingStore extends BillingState {
41
+ setSubscription: (subscription: Subscription | null) => void
42
+ setInvoices: (invoices: Invoice[]) => void
43
+ setPlans: (plans: Plan[]) => void
44
+ setLoading: (loading: boolean) => void
45
+ reset: () => void
46
+ }
47
+
48
+ const initialState: BillingState = {
49
+ subscription: null,
50
+ invoices: [],
51
+ plans: [],
52
+ loading: false,
53
+ }
54
+
55
+ export const useBillingStore = create<BillingStore>((set) => ({
56
+ ...initialState,
57
+ setSubscription: (subscription) => set({ subscription }),
58
+ setInvoices: (invoices) => set({ invoices }),
59
+ setPlans: (plans) => set({ plans }),
60
+ setLoading: (loading) => set({ loading }),
61
+ reset: () => set(initialState),
62
+ }))
@@ -0,0 +1,199 @@
1
+ import React, { useState, useCallback, useRef } from 'react'
2
+ import { Mail, Phone, ExternalLink, Cake, Calendar } from 'lucide-react'
3
+ import { Popover, PopoverTrigger, PopoverContent } from '@fayz-ai/ui'
4
+ import { createArchetypeLookup } from '../../archetype-lookup'
5
+ import type { EntityLookupResult } from '../../archetype-lookup'
6
+ import { cn } from '@fayz-ai/ui'
7
+ import { resolveEntityHref } from '../../lib/entity-routes'
8
+
9
+ // Singleton person lookup — shared across all PersonLink instances
10
+ const personLookup = createArchetypeLookup({ archetype: 'person' })
11
+
12
+ interface PersonLinkProps {
13
+ /** Person UUID — required for fetching details */
14
+ personId?: string | null
15
+ /** Display name (shown as the clickable text) */
16
+ name: string
17
+ /** Optional href to navigate to the person's profile */
18
+ profileHref?: string
19
+ /** Optional tab to open on the detail page (e.g. "schedule") */
20
+ tab?: string
21
+ /** Size variant */
22
+ size?: 'sm' | 'default'
23
+ /** Additional className for the trigger */
24
+ className?: string
25
+ }
26
+
27
+ function formatAge(dob: string): string | null {
28
+ try {
29
+ const birth = new Date(dob)
30
+ const now = new Date()
31
+ let age = now.getFullYear() - birth.getFullYear()
32
+ if (now.getMonth() < birth.getMonth() || (now.getMonth() === birth.getMonth() && now.getDate() < birth.getDate())) age--
33
+ return `${age}y`
34
+ } catch { return null }
35
+ }
36
+
37
+ export function PersonLink({ personId, name, profileHref, tab, size = 'default', className }: PersonLinkProps) {
38
+ const [person, setPerson] = useState<EntityLookupResult | null>(null)
39
+ const [loading, setLoading] = useState(false)
40
+ const [fetched, setFetched] = useState(false)
41
+ const [open, setOpen] = useState(false)
42
+ const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
43
+
44
+ const fetchPerson = useCallback(() => {
45
+ if (!fetched && personId) {
46
+ setLoading(true)
47
+ personLookup.getById(personId).then((result) => {
48
+ setPerson(result)
49
+ setFetched(true)
50
+ setLoading(false)
51
+ }).catch(() => {
52
+ setFetched(true)
53
+ setLoading(false)
54
+ })
55
+ }
56
+ }, [personId, fetched])
57
+
58
+ const handleOpenChange = useCallback((v: boolean) => {
59
+ setOpen(v)
60
+ if (v) fetchPerson()
61
+ }, [fetchPerson])
62
+
63
+ function handleMouseEnter() {
64
+ hoverTimer.current = setTimeout(() => { setOpen(true); fetchPerson() }, 800)
65
+ }
66
+
67
+ function handleMouseLeave() {
68
+ if (hoverTimer.current) { clearTimeout(hoverTimer.current); hoverTimer.current = null }
69
+ }
70
+
71
+ // If no personId, just render the name as plain text
72
+ if (!personId) {
73
+ return <span className={cn(size === 'sm' ? 'text-xs' : 'text-sm', className)}>{name}</span>
74
+ }
75
+
76
+ const data = person?.data as Record<string, unknown> | undefined
77
+ const email = data?.email as string | undefined
78
+ const phone = data?.phone as string | undefined
79
+ const kind = data?.kind as string | undefined ?? person?.group
80
+ const isActive = data?.is_active as boolean | undefined
81
+ const dob = data?.date_of_birth as string | undefined
82
+ const createdAt = data?.created_at as string | undefined
83
+ const age = dob ? formatAge(dob) : null
84
+ const initial = name.charAt(0).toUpperCase()
85
+
86
+ // Auto-derive profile href from person kind if not explicitly provided
87
+ const resolvedHref = profileHref ?? (personId ? '#' + resolveEntityHref(personId, 'person', kind ?? undefined, tab) : undefined)
88
+
89
+ return (
90
+ <Popover open={open} onOpenChange={handleOpenChange}>
91
+ <PopoverTrigger asChild>
92
+ <button
93
+ type="button"
94
+ onClick={(e) => e.stopPropagation()}
95
+ onMouseEnter={handleMouseEnter}
96
+ onMouseLeave={handleMouseLeave}
97
+ className={cn(
98
+ 'font-medium text-foreground underline decoration-dashed decoration-muted-foreground/40 underline-offset-2 hover:decoration-primary hover:text-primary transition-colors truncate text-left',
99
+ size === 'sm' ? 'text-xs' : 'text-sm',
100
+ className
101
+ )}
102
+ >
103
+ {name}
104
+ </button>
105
+ </PopoverTrigger>
106
+ <PopoverContent side="bottom" align="start" sideOffset={6}>
107
+ {loading ? (
108
+ <div className="p-4 space-y-3">
109
+ <div className="flex items-center gap-3">
110
+ <div className="h-10 w-10 rounded-full bg-muted/40 animate-pulse" />
111
+ <div className="space-y-1.5 flex-1">
112
+ <div className="h-3.5 w-24 rounded bg-muted/40 animate-pulse" />
113
+ <div className="h-2.5 w-16 rounded bg-muted/30 animate-pulse" />
114
+ </div>
115
+ </div>
116
+ <div className="space-y-2 pt-1">
117
+ <div className="h-3 w-36 rounded bg-muted/30 animate-pulse" />
118
+ <div className="h-3 w-28 rounded bg-muted/30 animate-pulse" />
119
+ </div>
120
+ </div>
121
+ ) : (
122
+ <div>
123
+ {/* Header with profile link */}
124
+ <div className="flex items-start gap-3 p-4 pb-3">
125
+ <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary text-sm font-bold">
126
+ {initial}
127
+ </div>
128
+ <div className="min-w-0 flex-1">
129
+ <p className="text-sm font-semibold truncate">{name}</p>
130
+ <div className="flex items-center gap-1.5 mt-0.5">
131
+ {kind && (
132
+ <span className="inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground capitalize">
133
+ {kind}
134
+ </span>
135
+ )}
136
+ {isActive !== undefined && (
137
+ <span className={`inline-flex items-center gap-1 text-[10px] ${isActive ? 'text-success' : 'text-muted-foreground/50'}`}>
138
+ <span className={`h-1.5 w-1.5 rounded-full ${isActive ? 'bg-success' : 'bg-muted-foreground/30'}`} />
139
+ {isActive ? 'Active' : 'Inactive'}
140
+ </span>
141
+ )}
142
+ </div>
143
+ </div>
144
+ {resolvedHref && (
145
+ <a
146
+ href={resolvedHref}
147
+ onClick={(e) => { e.preventDefault(); window.location.hash = resolvedHref.replace(/^#/, '') }}
148
+ className="flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground transition-colors shrink-0"
149
+ title="View profile"
150
+ >
151
+ <ExternalLink className="h-3.5 w-3.5" />
152
+ </a>
153
+ )}
154
+ </div>
155
+
156
+ {/* Contact details */}
157
+ {(email || phone || age) && (
158
+ <div className="px-4 pb-3 space-y-1.5">
159
+ {email && (
160
+ <a href={`mailto:${email}`} className="flex items-center gap-2 text-xs text-muted-foreground hover:text-primary transition-colors">
161
+ <Mail className="h-3 w-3 shrink-0" />
162
+ <span className="truncate">{email}</span>
163
+ </a>
164
+ )}
165
+ {phone && (
166
+ <a href={`tel:${phone}`} className="flex items-center gap-2 text-xs text-muted-foreground hover:text-primary transition-colors">
167
+ <Phone className="h-3 w-3 shrink-0" />
168
+ <span>{phone}</span>
169
+ </a>
170
+ )}
171
+ {dob && (
172
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
173
+ <Cake className="h-3 w-3 shrink-0" />
174
+ <span>{new Date(dob).toLocaleDateString()}{age ? ` (${age})` : ''}</span>
175
+ </div>
176
+ )}
177
+ </div>
178
+ )}
179
+
180
+ {/* No details found */}
181
+ {!person && fetched && (
182
+ <div className="px-4 pb-3">
183
+ <p className="text-xs text-muted-foreground">No additional details available</p>
184
+ </div>
185
+ )}
186
+
187
+ {/* Footer — created at */}
188
+ {createdAt && (
189
+ <div className="border-t px-4 py-2 flex items-center gap-1.5 text-[10px] text-muted-foreground/50">
190
+ <Calendar className="h-2.5 w-2.5" />
191
+ <span>Added {new Date(createdAt).toLocaleDateString()}</span>
192
+ </div>
193
+ )}
194
+ </div>
195
+ )}
196
+ </PopoverContent>
197
+ </Popover>
198
+ )
199
+ }