@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.
- package/dist/app/AdminShell.d.ts +31 -0
- package/dist/app/AdminShell.d.ts.map +1 -0
- package/dist/app/LoginPage.d.ts +17 -0
- package/dist/app/LoginPage.d.ts.map +1 -0
- package/dist/app/config.d.ts +83 -0
- package/dist/app/config.d.ts.map +1 -0
- package/dist/app/createFayzApp.d.ts +11 -0
- package/dist/app/createFayzApp.d.ts.map +1 -0
- package/dist/app/routing.d.ts +10 -0
- package/dist/app/routing.d.ts.map +1 -0
- package/dist/app/scaffold.d.ts +17 -0
- package/dist/app/scaffold.d.ts.map +1 -0
- package/dist/archetype-lookup.d.ts +30 -0
- package/dist/archetype-lookup.d.ts.map +1 -0
- package/dist/billing/index.cjs +12 -0
- package/dist/billing/index.cjs.map +1 -0
- package/dist/billing/index.d.ts +3 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +3 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/store.d.ts +35 -0
- package/dist/billing/store.d.ts.map +1 -0
- package/dist/chunk-4XTQ4V5L.cjs +124 -0
- package/dist/chunk-4XTQ4V5L.cjs.map +1 -0
- package/dist/chunk-5GYQK5IW.js +21 -0
- package/dist/chunk-5GYQK5IW.js.map +1 -0
- package/dist/chunk-DJW67TCY.js +781 -0
- package/dist/chunk-DJW67TCY.js.map +1 -0
- package/dist/chunk-KYID2MR6.cjs +23 -0
- package/dist/chunk-KYID2MR6.cjs.map +1 -0
- package/dist/chunk-QGMDVLLW.cjs +816 -0
- package/dist/chunk-QGMDVLLW.cjs.map +1 -0
- package/dist/chunk-XNWCGJYH.js +97 -0
- package/dist/chunk-XNWCGJYH.js.map +1 -0
- package/dist/components/shared/PersonLink.d.ts +18 -0
- package/dist/components/shared/PersonLink.d.ts.map +1 -0
- package/dist/crud/CrudCardGrid.d.ts +13 -0
- package/dist/crud/CrudCardGrid.d.ts.map +1 -0
- package/dist/crud/CrudDetailPage.d.ts +18 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -0
- package/dist/crud/CrudFormPage.d.ts +18 -0
- package/dist/crud/CrudFormPage.d.ts.map +1 -0
- package/dist/crud/CrudListView.d.ts +51 -0
- package/dist/crud/CrudListView.d.ts.map +1 -0
- package/dist/crud/CrudPage.d.ts +20 -0
- package/dist/crud/CrudPage.d.ts.map +1 -0
- package/dist/crud/DeleteConfirmDialog.d.ts +11 -0
- package/dist/crud/DeleteConfirmDialog.d.ts.map +1 -0
- package/dist/crud/EntityOverview.d.ts +14 -0
- package/dist/crud/EntityOverview.d.ts.map +1 -0
- package/dist/crud/ImportWizard.d.ts +18 -0
- package/dist/crud/ImportWizard.d.ts.map +1 -0
- package/dist/crud/archetypes/ActiveToggle.d.ts +8 -0
- package/dist/crud/archetypes/ActiveToggle.d.ts.map +1 -0
- package/dist/crud/archetypes/ArchetypeStatusBar.d.ts +11 -0
- package/dist/crud/archetypes/ArchetypeStatusBar.d.ts.map +1 -0
- package/dist/crud/archetypes/BlockSettingsPopover.d.ts +15 -0
- package/dist/crud/archetypes/BlockSettingsPopover.d.ts.map +1 -0
- package/dist/crud/archetypes/EntityLink.d.ts +32 -0
- package/dist/crud/archetypes/EntityLink.d.ts.map +1 -0
- package/dist/crud/archetypes/LocationDetailTabs.d.ts +11 -0
- package/dist/crud/archetypes/LocationDetailTabs.d.ts.map +1 -0
- package/dist/crud/archetypes/LocationFormLayout.d.ts +12 -0
- package/dist/crud/archetypes/LocationFormLayout.d.ts.map +1 -0
- package/dist/crud/archetypes/PersonDetailTabs.d.ts +22 -0
- package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -0
- package/dist/crud/archetypes/PersonFormLayout.d.ts +13 -0
- package/dist/crud/archetypes/PersonFormLayout.d.ts.map +1 -0
- package/dist/crud/archetypes/PersonPicker.d.ts +23 -0
- package/dist/crud/archetypes/PersonPicker.d.ts.map +1 -0
- package/dist/crud/archetypes/ProductDetailTabs.d.ts +11 -0
- package/dist/crud/archetypes/ProductDetailTabs.d.ts.map +1 -0
- package/dist/crud/archetypes/ProductFormLayout.d.ts +12 -0
- package/dist/crud/archetypes/ProductFormLayout.d.ts.map +1 -0
- package/dist/crud/archetypes/ScheduleEditor.d.ts +7 -0
- package/dist/crud/archetypes/ScheduleEditor.d.ts.map +1 -0
- package/dist/crud/archetypes/ServiceDetailTabs.d.ts +10 -0
- package/dist/crud/archetypes/ServiceDetailTabs.d.ts.map +1 -0
- package/dist/crud/archetypes/ServiceFormLayout.d.ts +12 -0
- package/dist/crud/archetypes/ServiceFormLayout.d.ts.map +1 -0
- package/dist/crud/archetypes/SubjectDetailTabs.d.ts +12 -0
- package/dist/crud/archetypes/SubjectDetailTabs.d.ts.map +1 -0
- package/dist/crud/archetypes/SubjectFormLayout.d.ts +14 -0
- package/dist/crud/archetypes/SubjectFormLayout.d.ts.map +1 -0
- package/dist/crud/createCrudPage.d.ts +20 -0
- package/dist/crud/createCrudPage.d.ts.map +1 -0
- package/dist/crud/csv-export.d.ts +3 -0
- package/dist/crud/csv-export.d.ts.map +1 -0
- package/dist/crud/fieldToColumn.d.ts +14 -0
- package/dist/crud/fieldToColumn.d.ts.map +1 -0
- package/dist/crud/index.d.ts +12 -0
- package/dist/crud/index.d.ts.map +1 -0
- package/dist/hooks/useFieldRules.d.ts +5 -0
- package/dist/hooks/useFieldRules.d.ts.map +1 -0
- package/dist/hooks/useModuleNavigation.d.ts +25 -0
- package/dist/hooks/useModuleNavigation.d.ts.map +1 -0
- package/dist/hooks/usePluginPrefs.d.ts +23 -0
- package/dist/hooks/usePluginPrefs.d.ts.map +1 -0
- package/dist/index.cjs +13880 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13730 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/currency.d.ts +13 -0
- package/dist/lib/currency.d.ts.map +1 -0
- package/dist/lib/entity-routes.d.ts +24 -0
- package/dist/lib/entity-routes.d.ts.map +1 -0
- package/dist/lib/schedule-config.d.ts +32 -0
- package/dist/lib/schedule-config.d.ts.map +1 -0
- package/dist/lib/schedule-service.d.ts +76 -0
- package/dist/lib/schedule-service.d.ts.map +1 -0
- package/dist/org/adapters/mock.d.ts +4 -0
- package/dist/org/adapters/mock.d.ts.map +1 -0
- package/dist/org/adapters/supabase.d.ts +7 -0
- package/dist/org/adapters/supabase.d.ts.map +1 -0
- package/dist/org/context.d.ts +29 -0
- package/dist/org/context.d.ts.map +1 -0
- package/dist/org/index.cjs +44 -0
- package/dist/org/index.cjs.map +1 -0
- package/dist/org/index.d.ts +8 -0
- package/dist/org/index.d.ts.map +1 -0
- package/dist/org/index.js +3 -0
- package/dist/org/index.js.map +1 -0
- package/dist/org/store.d.ts +16 -0
- package/dist/org/store.d.ts.map +1 -0
- package/dist/permissions/PermissionGate.d.ts +12 -0
- package/dist/permissions/PermissionGate.d.ts.map +1 -0
- package/dist/permissions/context.d.ts +37 -0
- package/dist/permissions/context.d.ts.map +1 -0
- package/dist/permissions/index.cjs +32 -0
- package/dist/permissions/index.cjs.map +1 -0
- package/dist/permissions/index.d.ts +5 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +3 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/store.d.ts +21 -0
- package/dist/permissions/store.d.ts.map +1 -0
- package/dist/placeholder.d.ts +7 -0
- package/dist/placeholder.d.ts.map +1 -0
- package/dist/plugins/ModuleActionBar.d.ts +27 -0
- package/dist/plugins/ModuleActionBar.d.ts.map +1 -0
- package/dist/plugins/PluginRegistryManager.d.ts +13 -0
- package/dist/plugins/PluginRegistryManager.d.ts.map +1 -0
- package/dist/plugins/QuickActionsButton.d.ts +12 -0
- package/dist/plugins/QuickActionsButton.d.ts.map +1 -0
- package/dist/plugins/SettingsGroup.d.ts +25 -0
- package/dist/plugins/SettingsGroup.d.ts.map +1 -0
- package/dist/plugins/WidgetSlot.d.ts +8 -0
- package/dist/plugins/WidgetSlot.d.ts.map +1 -0
- package/dist/plugins/createPluginContext.d.ts +28 -0
- package/dist/plugins/createPluginContext.d.ts.map +1 -0
- package/dist/plugins/createViewRouter.d.ts +38 -0
- package/dist/plugins/createViewRouter.d.ts.map +1 -0
- package/dist/shell/components/auth/CenteredLogin.d.ts +13 -0
- package/dist/shell/components/auth/CenteredLogin.d.ts.map +1 -0
- package/dist/shell/components/auth/LoginForm.d.ts +12 -0
- package/dist/shell/components/auth/LoginForm.d.ts.map +1 -0
- package/dist/shell/components/auth/LoginPage.d.ts +15 -0
- package/dist/shell/components/auth/LoginPage.d.ts.map +1 -0
- package/dist/shell/components/auth/OAuthButtons.d.ts +10 -0
- package/dist/shell/components/auth/OAuthButtons.d.ts.map +1 -0
- package/dist/shell/components/auth/ProtectedRoute.d.ts +11 -0
- package/dist/shell/components/auth/ProtectedRoute.d.ts.map +1 -0
- package/dist/shell/components/auth/RecoveryForm.d.ts +9 -0
- package/dist/shell/components/auth/RecoveryForm.d.ts.map +1 -0
- package/dist/shell/components/auth/SignupForm.d.ts +10 -0
- package/dist/shell/components/auth/SignupForm.d.ts.map +1 -0
- package/dist/shell/components/auth/SplitLogin.d.ts +14 -0
- package/dist/shell/components/auth/SplitLogin.d.ts.map +1 -0
- package/dist/shell/components/auth/index.d.ts +7 -0
- package/dist/shell/components/auth/index.d.ts.map +1 -0
- package/dist/shell/components/billing/BillingPage.d.ts +7 -0
- package/dist/shell/components/billing/BillingPage.d.ts.map +1 -0
- package/dist/shell/components/billing/InvoiceList.d.ts +9 -0
- package/dist/shell/components/billing/InvoiceList.d.ts.map +1 -0
- package/dist/shell/components/billing/PaywallGate.d.ts +11 -0
- package/dist/shell/components/billing/PaywallGate.d.ts.map +1 -0
- package/dist/shell/components/billing/PlanSelector.d.ts +13 -0
- package/dist/shell/components/billing/PlanSelector.d.ts.map +1 -0
- package/dist/shell/components/billing/SubscriptionCard.d.ts +12 -0
- package/dist/shell/components/billing/SubscriptionCard.d.ts.map +1 -0
- package/dist/shell/components/billing/index.d.ts +6 -0
- package/dist/shell/components/billing/index.d.ts.map +1 -0
- package/dist/shell/components/chat/ChatFab.d.ts +9 -0
- package/dist/shell/components/chat/ChatFab.d.ts.map +1 -0
- package/dist/shell/components/chat/ChatPanel.d.ts +10 -0
- package/dist/shell/components/chat/ChatPanel.d.ts.map +1 -0
- package/dist/shell/components/chat/ChatSuggestions.d.ts +14 -0
- package/dist/shell/components/chat/ChatSuggestions.d.ts.map +1 -0
- package/dist/shell/components/chat/index.d.ts +3 -0
- package/dist/shell/components/chat/index.d.ts.map +1 -0
- package/dist/shell/components/crud/archetypes/ClientOrdersTab.d.ts +16 -0
- package/dist/shell/components/crud/archetypes/ClientOrdersTab.d.ts.map +1 -0
- package/dist/shell/components/layout/BottomNav.d.ts +24 -0
- package/dist/shell/components/layout/BottomNav.d.ts.map +1 -0
- package/dist/shell/components/layout/CommandPalette.d.ts +29 -0
- package/dist/shell/components/layout/CommandPalette.d.ts.map +1 -0
- package/dist/shell/components/layout/LocationSwitchOverlay.d.ts +3 -0
- package/dist/shell/components/layout/LocationSwitchOverlay.d.ts.map +1 -0
- package/dist/shell/components/layout/MockModeBanner.d.ts +3 -0
- package/dist/shell/components/layout/MockModeBanner.d.ts.map +1 -0
- package/dist/shell/components/layout/PrintChrome.d.ts +10 -0
- package/dist/shell/components/layout/PrintChrome.d.ts.map +1 -0
- package/dist/shell/components/layout/index.d.ts +6 -0
- package/dist/shell/components/layout/index.d.ts.map +1 -0
- package/dist/shell/components/notifications/ChangelogFeed.d.ts +8 -0
- package/dist/shell/components/notifications/ChangelogFeed.d.ts.map +1 -0
- package/dist/shell/components/notifications/NotificationBell.d.ts +9 -0
- package/dist/shell/components/notifications/NotificationBell.d.ts.map +1 -0
- package/dist/shell/components/notifications/NotificationInbox.d.ts +13 -0
- package/dist/shell/components/notifications/NotificationInbox.d.ts.map +1 -0
- package/dist/shell/components/notifications/ToastProvider.d.ts +2 -0
- package/dist/shell/components/notifications/ToastProvider.d.ts.map +1 -0
- package/dist/shell/components/notifications/index.d.ts +5 -0
- package/dist/shell/components/notifications/index.d.ts.map +1 -0
- package/dist/shell/components/organization/ImpersonationBanner.d.ts +3 -0
- package/dist/shell/components/organization/ImpersonationBanner.d.ts.map +1 -0
- package/dist/shell/components/organization/InviteMemberDialog.d.ts +10 -0
- package/dist/shell/components/organization/InviteMemberDialog.d.ts.map +1 -0
- package/dist/shell/components/organization/OrgInitializer.d.ts +7 -0
- package/dist/shell/components/organization/OrgInitializer.d.ts.map +1 -0
- package/dist/shell/components/organization/OrgSwitcher.d.ts +7 -0
- package/dist/shell/components/organization/OrgSwitcher.d.ts.map +1 -0
- package/dist/shell/components/organization/PermissionGate.d.ts +11 -0
- package/dist/shell/components/organization/PermissionGate.d.ts.map +1 -0
- package/dist/shell/components/organization/PermissionMatrixEditor.d.ts +17 -0
- package/dist/shell/components/organization/PermissionMatrixEditor.d.ts.map +1 -0
- package/dist/shell/components/organization/PermissionProfilesTab.d.ts +3 -0
- package/dist/shell/components/organization/PermissionProfilesTab.d.ts.map +1 -0
- package/dist/shell/components/organization/TeamTab.d.ts +3 -0
- package/dist/shell/components/organization/TeamTab.d.ts.map +1 -0
- package/dist/shell/components/organization/TenantOnboarding.d.ts +9 -0
- package/dist/shell/components/organization/TenantOnboarding.d.ts.map +1 -0
- package/dist/shell/components/organization/index.d.ts +8 -0
- package/dist/shell/components/organization/index.d.ts.map +1 -0
- package/dist/shell/components/plugins/PluginSettingsPanel.d.ts +23 -0
- package/dist/shell/components/plugins/PluginSettingsPanel.d.ts.map +1 -0
- package/dist/shell/components/settings/BrandingSettings.d.ts +12 -0
- package/dist/shell/components/settings/BrandingSettings.d.ts.map +1 -0
- package/dist/shell/components/settings/CompanySettings.d.ts +13 -0
- package/dist/shell/components/settings/CompanySettings.d.ts.map +1 -0
- package/dist/shell/components/settings/ConnectedBrandingSettings.d.ts +2 -0
- package/dist/shell/components/settings/ConnectedBrandingSettings.d.ts.map +1 -0
- package/dist/shell/components/settings/ConnectedCompanySettings.d.ts +2 -0
- package/dist/shell/components/settings/ConnectedCompanySettings.d.ts.map +1 -0
- package/dist/shell/components/settings/ConnectedFieldRulesSettings.d.ts +2 -0
- package/dist/shell/components/settings/ConnectedFieldRulesSettings.d.ts.map +1 -0
- package/dist/shell/components/settings/ConnectedHolidaysSettings.d.ts +3 -0
- package/dist/shell/components/settings/ConnectedHolidaysSettings.d.ts.map +1 -0
- package/dist/shell/components/settings/ConnectedLocationsOverview.d.ts +3 -0
- package/dist/shell/components/settings/ConnectedLocationsOverview.d.ts.map +1 -0
- package/dist/shell/components/settings/ConnectedSecuritySettings.d.ts +2 -0
- package/dist/shell/components/settings/ConnectedSecuritySettings.d.ts.map +1 -0
- package/dist/shell/components/settings/ConnectedUserProfile.d.ts +2 -0
- package/dist/shell/components/settings/ConnectedUserProfile.d.ts.map +1 -0
- package/dist/shell/components/settings/FieldRulesSettings.d.ts +11 -0
- package/dist/shell/components/settings/FieldRulesSettings.d.ts.map +1 -0
- package/dist/shell/components/settings/HolidaysSettings.d.ts +21 -0
- package/dist/shell/components/settings/HolidaysSettings.d.ts.map +1 -0
- package/dist/shell/components/settings/LocationsCrudPage.d.ts +6 -0
- package/dist/shell/components/settings/LocationsCrudPage.d.ts.map +1 -0
- package/dist/shell/components/settings/LocationsOverview.d.ts +19 -0
- package/dist/shell/components/settings/LocationsOverview.d.ts.map +1 -0
- package/dist/shell/components/settings/SecuritySettings.d.ts +17 -0
- package/dist/shell/components/settings/SecuritySettings.d.ts.map +1 -0
- package/dist/shell/components/settings/SettingsPage.d.ts +18 -0
- package/dist/shell/components/settings/SettingsPage.d.ts.map +1 -0
- package/dist/shell/components/settings/UserProfile.d.ts +12 -0
- package/dist/shell/components/settings/UserProfile.d.ts.map +1 -0
- package/dist/shell/components/settings/index.d.ts +11 -0
- package/dist/shell/components/settings/index.d.ts.map +1 -0
- package/dist/shell/config/index.d.ts +7 -0
- package/dist/shell/config/index.d.ts.map +1 -0
- package/dist/shell/config/permissions.d.ts +7 -0
- package/dist/shell/config/permissions.d.ts.map +1 -0
- package/dist/shell/config/tailwind-preset.d.ts +4 -0
- package/dist/shell/config/tailwind-preset.d.ts.map +1 -0
- package/dist/shell/config/theme/dark.d.ts +3 -0
- package/dist/shell/config/theme/dark.d.ts.map +1 -0
- package/dist/shell/config/theme/index.d.ts +6 -0
- package/dist/shell/config/theme/index.d.ts.map +1 -0
- package/dist/shell/config/theme/light.d.ts +3 -0
- package/dist/shell/config/theme/light.d.ts.map +1 -0
- package/dist/shell/config/theme/tokens.d.ts +97 -0
- package/dist/shell/config/theme/tokens.d.ts.map +1 -0
- package/dist/shell/config/theme/utils.d.ts +23 -0
- package/dist/shell/config/theme/utils.d.ts.map +1 -0
- package/dist/shell/createSaasApp.d.ts +145 -0
- package/dist/shell/createSaasApp.d.ts.map +1 -0
- package/dist/shell/hooks/index.d.ts +19 -0
- package/dist/shell/hooks/index.d.ts.map +1 -0
- package/dist/shell/hooks/useAITools.d.ts +21 -0
- package/dist/shell/hooks/useAITools.d.ts.map +1 -0
- package/dist/shell/hooks/useAuth.d.ts +16 -0
- package/dist/shell/hooks/useAuth.d.ts.map +1 -0
- package/dist/shell/hooks/useBilling.d.ts +14 -0
- package/dist/shell/hooks/useBilling.d.ts.map +1 -0
- package/dist/shell/hooks/useChat.d.ts +16 -0
- package/dist/shell/hooks/useChat.d.ts.map +1 -0
- package/dist/shell/hooks/useFieldRules.d.ts +6 -0
- package/dist/shell/hooks/useFieldRules.d.ts.map +1 -0
- package/dist/shell/hooks/useFormatters.d.ts +7 -0
- package/dist/shell/hooks/useFormatters.d.ts.map +1 -0
- package/dist/shell/hooks/useLayout.d.ts +16 -0
- package/dist/shell/hooks/useLayout.d.ts.map +1 -0
- package/dist/shell/hooks/useModuleNavigation.d.ts +25 -0
- package/dist/shell/hooks/useModuleNavigation.d.ts.map +1 -0
- package/dist/shell/hooks/useNotifications.d.ts +11 -0
- package/dist/shell/hooks/useNotifications.d.ts.map +1 -0
- package/dist/shell/hooks/usePermission.d.ts +16 -0
- package/dist/shell/hooks/usePermission.d.ts.map +1 -0
- package/dist/shell/hooks/usePluginPrefs.d.ts +23 -0
- package/dist/shell/hooks/usePluginPrefs.d.ts.map +1 -0
- package/dist/shell/hooks/usePlugins.d.ts +5 -0
- package/dist/shell/hooks/usePlugins.d.ts.map +1 -0
- package/dist/shell/hooks/useTenant.d.ts +23 -0
- package/dist/shell/hooks/useTenant.d.ts.map +1 -0
- package/dist/shell/hooks/useTenantPlugins.d.ts +10 -0
- package/dist/shell/hooks/useTenantPlugins.d.ts.map +1 -0
- package/dist/shell/hooks/useTranslation.d.ts +5 -0
- package/dist/shell/hooks/useTranslation.d.ts.map +1 -0
- package/dist/shell/lib/api.d.ts +28 -0
- package/dist/shell/lib/api.d.ts.map +1 -0
- package/dist/shell/lib/apply-field-rules.d.ts +4 -0
- package/dist/shell/lib/apply-field-rules.d.ts.map +1 -0
- package/dist/shell/lib/auth-adapters/mock.d.ts +3 -0
- package/dist/shell/lib/auth-adapters/mock.d.ts.map +1 -0
- package/dist/shell/lib/auth-adapters/supabase.d.ts +3 -0
- package/dist/shell/lib/auth-adapters/supabase.d.ts.map +1 -0
- package/dist/shell/lib/auth-context.d.ts +5 -0
- package/dist/shell/lib/auth-context.d.ts.map +1 -0
- package/dist/shell/lib/cache.d.ts +18 -0
- package/dist/shell/lib/cache.d.ts.map +1 -0
- package/dist/shell/lib/cn.d.ts +2 -0
- package/dist/shell/lib/cn.d.ts.map +1 -0
- package/dist/shell/lib/core-ai-tools.d.ts +15 -0
- package/dist/shell/lib/core-ai-tools.d.ts.map +1 -0
- package/dist/shell/lib/create-client-orders-provider.d.ts +3 -0
- package/dist/shell/lib/create-client-orders-provider.d.ts.map +1 -0
- package/dist/shell/lib/csv.d.ts +14 -0
- package/dist/shell/lib/csv.d.ts.map +1 -0
- package/dist/shell/lib/dedup.d.ts +2 -0
- package/dist/shell/lib/dedup.d.ts.map +1 -0
- package/dist/shell/lib/entity-registry.d.ts +19 -0
- package/dist/shell/lib/entity-registry.d.ts.map +1 -0
- package/dist/shell/lib/entity-routes.d.ts +24 -0
- package/dist/shell/lib/entity-routes.d.ts.map +1 -0
- package/dist/shell/lib/format.d.ts +7 -0
- package/dist/shell/lib/format.d.ts.map +1 -0
- package/dist/shell/lib/i18n.d.ts +4 -0
- package/dist/shell/lib/i18n.d.ts.map +1 -0
- package/dist/shell/lib/locale-config.d.ts +8 -0
- package/dist/shell/lib/locale-config.d.ts.map +1 -0
- package/dist/shell/lib/org-adapters/mock.d.ts +4 -0
- package/dist/shell/lib/org-adapters/mock.d.ts.map +1 -0
- package/dist/shell/lib/org-adapters/supabase.d.ts +3 -0
- package/dist/shell/lib/org-adapters/supabase.d.ts.map +1 -0
- package/dist/shell/lib/org-context.d.ts +5 -0
- package/dist/shell/lib/org-context.d.ts.map +1 -0
- package/dist/shell/lib/plugins.d.ts +2 -0
- package/dist/shell/lib/plugins.d.ts.map +1 -0
- package/dist/shell/lib/router.d.ts +15 -0
- package/dist/shell/lib/router.d.ts.map +1 -0
- package/dist/shell/lib/schedule-config.d.ts +32 -0
- package/dist/shell/lib/schedule-config.d.ts.map +1 -0
- package/dist/shell/lib/schedule-service.d.ts +76 -0
- package/dist/shell/lib/schedule-service.d.ts.map +1 -0
- package/dist/shell/lib/supabase.d.ts +9 -0
- package/dist/shell/lib/supabase.d.ts.map +1 -0
- package/dist/shell/stores/auth.store.d.ts +12 -0
- package/dist/shell/stores/auth.store.d.ts.map +1 -0
- package/dist/shell/stores/billing.store.d.ts +2 -0
- package/dist/shell/stores/billing.store.d.ts.map +1 -0
- package/dist/shell/stores/chat.store.d.ts +20 -0
- package/dist/shell/stores/chat.store.d.ts.map +1 -0
- package/dist/shell/stores/index.d.ts +13 -0
- package/dist/shell/stores/index.d.ts.map +1 -0
- package/dist/shell/stores/invite.store.d.ts +11 -0
- package/dist/shell/stores/invite.store.d.ts.map +1 -0
- package/dist/shell/stores/layout.store.d.ts +2 -0
- package/dist/shell/stores/layout.store.d.ts.map +1 -0
- package/dist/shell/stores/locale.store.d.ts +7 -0
- package/dist/shell/stores/locale.store.d.ts.map +1 -0
- package/dist/shell/stores/location.store.d.ts +20 -0
- package/dist/shell/stores/location.store.d.ts.map +1 -0
- package/dist/shell/stores/notifications.store.d.ts +12 -0
- package/dist/shell/stores/notifications.store.d.ts.map +1 -0
- package/dist/shell/stores/organization.store.d.ts +2 -0
- package/dist/shell/stores/organization.store.d.ts.map +1 -0
- package/dist/shell/stores/permissions.store.d.ts +2 -0
- package/dist/shell/stores/permissions.store.d.ts.map +1 -0
- package/dist/shell/stores/plugin.store.d.ts +24 -0
- package/dist/shell/stores/plugin.store.d.ts.map +1 -0
- package/dist/shell/stores/tenant.store.d.ts +11 -0
- package/dist/shell/stores/tenant.store.d.ts.map +1 -0
- package/dist/shell/stores/theme.store.d.ts +17 -0
- package/dist/shell/stores/theme.store.d.ts.map +1 -0
- package/dist/shell/test-setup.d.ts +2 -0
- package/dist/shell/test-setup.d.ts.map +1 -0
- package/dist/shell/types/auth-adapter.d.ts +14 -0
- package/dist/shell/types/auth-adapter.d.ts.map +1 -0
- package/dist/shell/types/auth.d.ts +34 -0
- package/dist/shell/types/auth.d.ts.map +1 -0
- package/dist/shell/types/billing.d.ts +74 -0
- package/dist/shell/types/billing.d.ts.map +1 -0
- package/dist/shell/types/client-orders.d.ts +43 -0
- package/dist/shell/types/client-orders.d.ts.map +1 -0
- package/dist/shell/types/crud.d.ts +122 -0
- package/dist/shell/types/crud.d.ts.map +1 -0
- package/dist/shell/types/entities.d.ts +150 -0
- package/dist/shell/types/entities.d.ts.map +1 -0
- package/dist/shell/types/entity-lookup.d.ts +18 -0
- package/dist/shell/types/entity-lookup.d.ts.map +1 -0
- package/dist/shell/types/index.d.ts +17 -0
- package/dist/shell/types/index.d.ts.map +1 -0
- package/dist/shell/types/integrations.d.ts +48 -0
- package/dist/shell/types/integrations.d.ts.map +1 -0
- package/dist/shell/types/invite.d.ts +23 -0
- package/dist/shell/types/invite.d.ts.map +1 -0
- package/dist/shell/types/layout.d.ts +26 -0
- package/dist/shell/types/layout.d.ts.map +1 -0
- package/dist/shell/types/notifications.d.ts +31 -0
- package/dist/shell/types/notifications.d.ts.map +1 -0
- package/dist/shell/types/org-adapter.d.ts +72 -0
- package/dist/shell/types/org-adapter.d.ts.map +1 -0
- package/dist/shell/types/permissions.d.ts +39 -0
- package/dist/shell/types/permissions.d.ts.map +1 -0
- package/dist/shell/types/platform.d.ts +25 -0
- package/dist/shell/types/platform.d.ts.map +1 -0
- package/dist/shell/types/plugins.d.ts +308 -0
- package/dist/shell/types/plugins.d.ts.map +1 -0
- package/dist/shell/types/tenant.d.ts +74 -0
- package/dist/shell/types/tenant.d.ts.map +1 -0
- package/dist/stores/createCrudStore.d.ts +32 -0
- package/dist/stores/createCrudStore.d.ts.map +1 -0
- package/dist/supabase/client.d.ts +18 -0
- package/dist/supabase/client.d.ts.map +1 -0
- package/package.json +72 -0
- package/src/app/AdminShell.tsx +449 -0
- package/src/app/LoginPage.tsx +264 -0
- package/src/app/config.ts +146 -0
- package/src/app/createFayzApp.tsx +254 -0
- package/src/app/routing.tsx +65 -0
- package/src/app/scaffold.tsx +175 -0
- package/src/archetype-lookup.ts +160 -0
- package/src/billing/index.ts +7 -0
- package/src/billing/store.ts +62 -0
- package/src/components/shared/PersonLink.tsx +199 -0
- package/src/crud/CrudCardGrid.tsx +92 -0
- package/src/crud/CrudDetailPage.tsx +366 -0
- package/src/crud/CrudFormPage.tsx +467 -0
- package/src/crud/CrudListView.tsx +223 -0
- package/src/crud/CrudPage.tsx +491 -0
- package/src/crud/DeleteConfirmDialog.tsx +41 -0
- package/src/crud/EntityOverview.tsx +230 -0
- package/src/crud/ImportWizard.tsx +690 -0
- package/src/crud/archetypes/ActiveToggle.tsx +23 -0
- package/src/crud/archetypes/ArchetypeStatusBar.tsx +58 -0
- package/src/crud/archetypes/BlockSettingsPopover.tsx +322 -0
- package/src/crud/archetypes/EntityLink.tsx +74 -0
- package/src/crud/archetypes/LocationDetailTabs.tsx +72 -0
- package/src/crud/archetypes/LocationFormLayout.tsx +173 -0
- package/src/crud/archetypes/PersonDetailTabs.tsx +87 -0
- package/src/crud/archetypes/PersonFormLayout.tsx +184 -0
- package/src/crud/archetypes/PersonPicker.tsx +299 -0
- package/src/crud/archetypes/ProductDetailTabs.tsx +72 -0
- package/src/crud/archetypes/ProductFormLayout.tsx +197 -0
- package/src/crud/archetypes/ScheduleEditor.tsx +796 -0
- package/src/crud/archetypes/ServiceDetailTabs.tsx +60 -0
- package/src/crud/archetypes/ServiceFormLayout.tsx +155 -0
- package/src/crud/archetypes/SubjectDetailTabs.tsx +142 -0
- package/src/crud/archetypes/SubjectFormLayout.tsx +164 -0
- package/src/crud/createCrudPage.tsx +65 -0
- package/src/crud/csv-export.ts +17 -0
- package/src/crud/fieldToColumn.tsx +109 -0
- package/src/crud/index.ts +11 -0
- package/src/hooks/useFieldRules.ts +10 -0
- package/src/hooks/useModuleNavigation.ts +124 -0
- package/src/hooks/usePluginPrefs.ts +113 -0
- package/src/index.ts +168 -0
- package/src/lib/currency.ts +19 -0
- package/src/lib/entity-routes.ts +53 -0
- package/src/lib/schedule-config.ts +42 -0
- package/src/lib/schedule-service.ts +307 -0
- package/src/org/adapters/mock.ts +353 -0
- package/src/org/adapters/supabase.ts +509 -0
- package/src/org/context.tsx +174 -0
- package/src/org/index.ts +19 -0
- package/src/org/store.ts +70 -0
- package/src/permissions/PermissionGate.tsx +17 -0
- package/src/permissions/context.tsx +126 -0
- package/src/permissions/index.ts +13 -0
- package/src/permissions/store.ts +68 -0
- package/src/placeholder.ts +32 -0
- package/src/plugins/ModuleActionBar.tsx +57 -0
- package/src/plugins/PluginRegistryManager.tsx +110 -0
- package/src/plugins/QuickActionsButton.tsx +73 -0
- package/src/plugins/SettingsGroup.tsx +100 -0
- package/src/plugins/WidgetSlot.tsx +38 -0
- package/src/plugins/createPluginContext.tsx +68 -0
- package/src/plugins/createViewRouter.tsx +45 -0
- package/src/shell/components/auth/CenteredLogin.tsx +86 -0
- package/src/shell/components/auth/LoginForm.tsx +129 -0
- package/src/shell/components/auth/LoginPage.tsx +22 -0
- package/src/shell/components/auth/OAuthButtons.tsx +66 -0
- package/src/shell/components/auth/ProtectedRoute.tsx +52 -0
- package/src/shell/components/auth/RecoveryForm.tsx +110 -0
- package/src/shell/components/auth/SignupForm.tsx +170 -0
- package/src/shell/components/auth/SplitLogin.tsx +124 -0
- package/src/shell/components/auth/index.ts +6 -0
- package/src/shell/components/billing/BillingPage.tsx +56 -0
- package/src/shell/components/billing/InvoiceList.tsx +123 -0
- package/src/shell/components/billing/PaywallGate.tsx +80 -0
- package/src/shell/components/billing/PlanSelector.tsx +125 -0
- package/src/shell/components/billing/SubscriptionCard.tsx +108 -0
- package/src/shell/components/billing/index.ts +5 -0
- package/src/shell/components/chat/ChatFab.tsx +195 -0
- package/src/shell/components/chat/ChatPanel.tsx +170 -0
- package/src/shell/components/chat/ChatSuggestions.tsx +102 -0
- package/src/shell/components/chat/index.ts +2 -0
- package/src/shell/components/crud/archetypes/ClientOrdersTab.tsx +212 -0
- package/src/shell/components/layout/BottomNav.tsx +131 -0
- package/src/shell/components/layout/CommandPalette.tsx +257 -0
- package/src/shell/components/layout/LocationSwitchOverlay.tsx +64 -0
- package/src/shell/components/layout/MockModeBanner.tsx +17 -0
- package/src/shell/components/layout/PrintChrome.tsx +200 -0
- package/src/shell/components/layout/index.ts +5 -0
- package/src/shell/components/notifications/ChangelogFeed.tsx +85 -0
- package/src/shell/components/notifications/NotificationBell.tsx +44 -0
- package/src/shell/components/notifications/NotificationInbox.tsx +175 -0
- package/src/shell/components/notifications/ToastProvider.tsx +2 -0
- package/src/shell/components/notifications/index.ts +4 -0
- package/src/shell/components/organization/ImpersonationBanner.tsx +29 -0
- package/src/shell/components/organization/InviteMemberDialog.tsx +161 -0
- package/src/shell/components/organization/OrgInitializer.tsx +179 -0
- package/src/shell/components/organization/OrgSwitcher.tsx +177 -0
- package/src/shell/components/organization/PermissionGate.tsx +16 -0
- package/src/shell/components/organization/PermissionMatrixEditor.tsx +206 -0
- package/src/shell/components/organization/PermissionProfilesTab.tsx +148 -0
- package/src/shell/components/organization/TeamTab.tsx +243 -0
- package/src/shell/components/organization/TenantOnboarding.tsx +179 -0
- package/src/shell/components/organization/index.ts +7 -0
- package/src/shell/components/plugins/PluginSettingsPanel.tsx +189 -0
- package/src/shell/components/settings/BrandingSettings.tsx +255 -0
- package/src/shell/components/settings/CompanySettings.tsx +188 -0
- package/src/shell/components/settings/ConnectedBrandingSettings.tsx +82 -0
- package/src/shell/components/settings/ConnectedCompanySettings.tsx +63 -0
- package/src/shell/components/settings/ConnectedFieldRulesSettings.tsx +47 -0
- package/src/shell/components/settings/ConnectedHolidaysSettings.tsx +99 -0
- package/src/shell/components/settings/ConnectedLocationsOverview.tsx +57 -0
- package/src/shell/components/settings/ConnectedSecuritySettings.tsx +59 -0
- package/src/shell/components/settings/ConnectedUserProfile.tsx +59 -0
- package/src/shell/components/settings/FieldRulesSettings.tsx +511 -0
- package/src/shell/components/settings/HolidaysSettings.tsx +141 -0
- package/src/shell/components/settings/LocationsCrudPage.ts +36 -0
- package/src/shell/components/settings/LocationsOverview.tsx +171 -0
- package/src/shell/components/settings/SecuritySettings.tsx +208 -0
- package/src/shell/components/settings/SettingsPage.tsx +162 -0
- package/src/shell/components/settings/UserProfile.tsx +151 -0
- package/src/shell/components/settings/index.ts +10 -0
- package/src/shell/config/index.ts +6 -0
- package/src/shell/config/permissions.ts +31 -0
- package/src/shell/config/tailwind-preset.ts +164 -0
- package/src/shell/config/theme/dark.ts +63 -0
- package/src/shell/config/theme/index.ts +5 -0
- package/src/shell/config/theme/light.ts +63 -0
- package/src/shell/config/theme/tokens.ts +105 -0
- package/src/shell/config/theme/utils.ts +249 -0
- package/src/shell/createSaasApp.tsx +922 -0
- package/src/shell/hooks/index.ts +18 -0
- package/src/shell/hooks/useAITools.ts +139 -0
- package/src/shell/hooks/useAuth.ts +109 -0
- package/src/shell/hooks/useBilling.ts +151 -0
- package/src/shell/hooks/useChat.ts +84 -0
- package/src/shell/hooks/useFieldRules.ts +19 -0
- package/src/shell/hooks/useFormatters.ts +56 -0
- package/src/shell/hooks/useLayout.ts +76 -0
- package/src/shell/hooks/useModuleNavigation.ts +124 -0
- package/src/shell/hooks/useNotifications.ts +114 -0
- package/src/shell/hooks/usePermission.ts +101 -0
- package/src/shell/hooks/usePluginPrefs.ts +113 -0
- package/src/shell/hooks/usePlugins.ts +18 -0
- package/src/shell/hooks/useTenant.ts +106 -0
- package/src/shell/hooks/useTenantPlugins.ts +103 -0
- package/src/shell/hooks/useTranslation.ts +10 -0
- package/src/shell/lib/api.ts +67 -0
- package/src/shell/lib/apply-field-rules.ts +14 -0
- package/src/shell/lib/auth-adapters/mock.ts +97 -0
- package/src/shell/lib/auth-adapters/supabase.ts +96 -0
- package/src/shell/lib/auth-context.ts +18 -0
- package/src/shell/lib/cache.ts +124 -0
- package/src/shell/lib/cn.ts +1 -0
- package/src/shell/lib/core-ai-tools.ts +90 -0
- package/src/shell/lib/create-client-orders-provider.ts +105 -0
- package/src/shell/lib/csv.ts +54 -0
- package/src/shell/lib/dedup.ts +17 -0
- package/src/shell/lib/entity-registry.ts +39 -0
- package/src/shell/lib/entity-routes.ts +53 -0
- package/src/shell/lib/format.ts +58 -0
- package/src/shell/lib/i18n.ts +3723 -0
- package/src/shell/lib/locale-config.ts +14 -0
- package/src/shell/lib/org-adapters/mock.ts +318 -0
- package/src/shell/lib/org-adapters/supabase.ts +533 -0
- package/src/shell/lib/org-context.ts +18 -0
- package/src/shell/lib/plugins.ts +11 -0
- package/src/shell/lib/router.ts +86 -0
- package/src/shell/lib/schedule-config.ts +42 -0
- package/src/shell/lib/schedule-service.ts +307 -0
- package/src/shell/lib/supabase.ts +19 -0
- package/src/shell/stores/auth.store.ts +29 -0
- package/src/shell/stores/billing.store.ts +1 -0
- package/src/shell/stores/chat.store.ts +45 -0
- package/src/shell/stores/index.ts +12 -0
- package/src/shell/stores/invite.store.ts +18 -0
- package/src/shell/stores/layout.store.ts +1 -0
- package/src/shell/stores/locale.store.ts +24 -0
- package/src/shell/stores/location.store.ts +89 -0
- package/src/shell/stores/notifications.store.ts +48 -0
- package/src/shell/stores/organization.store.ts +3 -0
- package/src/shell/stores/permissions.store.ts +1 -0
- package/src/shell/stores/plugin.store.ts +50 -0
- package/src/shell/stores/tenant.store.ts +26 -0
- package/src/shell/stores/theme.store.ts +162 -0
- package/src/shell/styles.css +433 -0
- package/src/shell/test-setup.ts +1 -0
- package/src/shell/types/auth-adapter.ts +11 -0
- package/src/shell/types/auth.ts +38 -0
- package/src/shell/types/billing.ts +90 -0
- package/src/shell/types/client-orders.ts +58 -0
- package/src/shell/types/crud.ts +134 -0
- package/src/shell/types/entities.ts +169 -0
- package/src/shell/types/entity-lookup.ts +25 -0
- package/src/shell/types/index.ts +16 -0
- package/src/shell/types/integrations.ts +51 -0
- package/src/shell/types/invite.ts +23 -0
- package/src/shell/types/layout.ts +29 -0
- package/src/shell/types/notifications.ts +33 -0
- package/src/shell/types/org-adapter.ts +72 -0
- package/src/shell/types/permissions.ts +64 -0
- package/src/shell/types/platform.ts +25 -0
- package/src/shell/types/plugins.ts +359 -0
- package/src/shell/types/tenant.ts +79 -0
- package/src/stores/createCrudStore.ts +138 -0
- package/src/supabase/client.ts +54 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
|
2
|
+
|
|
3
|
+
interface ApiOptions {
|
|
4
|
+
baseUrl?: string
|
|
5
|
+
headers?: Record<string, string>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface RequestOptions {
|
|
9
|
+
headers?: Record<string, string>
|
|
10
|
+
signal?: AbortSignal
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class ApiClient {
|
|
14
|
+
private baseUrl: string
|
|
15
|
+
private defaultHeaders: Record<string, string>
|
|
16
|
+
|
|
17
|
+
constructor(options: ApiOptions = {}) {
|
|
18
|
+
this.baseUrl = options.baseUrl || ''
|
|
19
|
+
this.defaultHeaders = {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
...options.headers,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setAuthToken(token: string) {
|
|
26
|
+
this.defaultHeaders['Authorization'] = `Bearer ${token}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
clearAuthToken() {
|
|
30
|
+
delete this.defaultHeaders['Authorization']
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private async request<T>(method: HttpMethod, path: string, body?: unknown, options?: RequestOptions): Promise<T> {
|
|
34
|
+
const url = `${this.baseUrl}${path}`
|
|
35
|
+
const response = await fetch(url, {
|
|
36
|
+
method,
|
|
37
|
+
headers: { ...this.defaultHeaders, ...options?.headers },
|
|
38
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
39
|
+
signal: options?.signal,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const error = await response.json().catch(() => ({ message: response.statusText }))
|
|
44
|
+
throw new ApiError(response.status, error.message || response.statusText)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (response.status === 204) return undefined as T
|
|
48
|
+
return response.json()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get<T>(path: string, options?: RequestOptions) { return this.request<T>('GET', path, undefined, options) }
|
|
52
|
+
post<T>(path: string, body?: unknown, options?: RequestOptions) { return this.request<T>('POST', path, body, options) }
|
|
53
|
+
put<T>(path: string, body?: unknown, options?: RequestOptions) { return this.request<T>('PUT', path, body, options) }
|
|
54
|
+
patch<T>(path: string, body?: unknown, options?: RequestOptions) { return this.request<T>('PATCH', path, body, options) }
|
|
55
|
+
delete<T>(path: string, options?: RequestOptions) { return this.request<T>('DELETE', path, undefined, options) }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class ApiError extends Error {
|
|
59
|
+
constructor(public status: number, message: string) {
|
|
60
|
+
super(message)
|
|
61
|
+
this.name = 'ApiError'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function createApiClient(options?: ApiOptions) {
|
|
66
|
+
return new ApiClient(options)
|
|
67
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FieldDef, EntityFieldRules } from '../types/crud'
|
|
2
|
+
|
|
3
|
+
/** Merge tenant field-rule overrides into static FieldDef[]. Pure & testable. */
|
|
4
|
+
export function applyFieldRules(
|
|
5
|
+
fields: FieldDef[],
|
|
6
|
+
rules: EntityFieldRules | undefined,
|
|
7
|
+
): FieldDef[] {
|
|
8
|
+
if (!rules) return fields
|
|
9
|
+
return fields.map((f) => {
|
|
10
|
+
const override = rules[f.key]
|
|
11
|
+
if (!override) return f
|
|
12
|
+
return { ...f, ...override }
|
|
13
|
+
})
|
|
14
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { AuthAdapter } from '../../types/auth-adapter'
|
|
2
|
+
import type { AuthUser, AuthSession } from '../../types/auth'
|
|
3
|
+
|
|
4
|
+
const STORAGE_KEY = 'saas-core:mock-auth'
|
|
5
|
+
|
|
6
|
+
function createMockUser(email: string, fullName?: string): AuthUser {
|
|
7
|
+
return {
|
|
8
|
+
id: crypto.randomUUID(),
|
|
9
|
+
email,
|
|
10
|
+
fullName: fullName || email.split('@')[0],
|
|
11
|
+
providers: ['email'],
|
|
12
|
+
emailVerified: true,
|
|
13
|
+
createdAt: new Date().toISOString(),
|
|
14
|
+
updatedAt: new Date().toISOString(),
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function createMockSession(user: AuthUser): AuthSession {
|
|
19
|
+
return {
|
|
20
|
+
accessToken: `mock-token-${user.id}`,
|
|
21
|
+
refreshToken: `mock-refresh-${user.id}`,
|
|
22
|
+
expiresAt: Math.floor(Date.now() / 1000) + 3600,
|
|
23
|
+
user,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createMockAuthAdapter(): AuthAdapter {
|
|
28
|
+
const listeners = new Set<(user: AuthUser | null) => void>()
|
|
29
|
+
|
|
30
|
+
function notifyListeners(user: AuthUser | null) {
|
|
31
|
+
for (const cb of listeners) cb(user)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getStored(): { user: AuthUser; session: AuthSession } | null {
|
|
35
|
+
try {
|
|
36
|
+
const raw = localStorage.getItem(STORAGE_KEY)
|
|
37
|
+
if (!raw) return null
|
|
38
|
+
return JSON.parse(raw)
|
|
39
|
+
} catch {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function setStored(data: { user: AuthUser; session: AuthSession } | null) {
|
|
45
|
+
if (data) {
|
|
46
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(data))
|
|
47
|
+
} else {
|
|
48
|
+
localStorage.removeItem(STORAGE_KEY)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
async getSession() {
|
|
54
|
+
return getStored()
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
onAuthStateChange(cb) {
|
|
58
|
+
listeners.add(cb)
|
|
59
|
+
return () => { listeners.delete(cb) }
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
async signIn(email, password) {
|
|
63
|
+
void password // mock accepts any password
|
|
64
|
+
const user = createMockUser(email)
|
|
65
|
+
const session = createMockSession(user)
|
|
66
|
+
setStored({ user, session })
|
|
67
|
+
notifyListeners(user)
|
|
68
|
+
return session
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
async signUp(email, password, fullName) {
|
|
72
|
+
void password
|
|
73
|
+
const user = createMockUser(email, fullName)
|
|
74
|
+
const session = createMockSession(user)
|
|
75
|
+
setStored({ user, session })
|
|
76
|
+
notifyListeners(user)
|
|
77
|
+
return session
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
async signOut() {
|
|
81
|
+
setStored(null)
|
|
82
|
+
notifyListeners(null)
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async signInWithOAuth(_provider) {
|
|
86
|
+
// Mock OAuth just signs in with a fake user
|
|
87
|
+
const user = createMockUser('oauth@example.com', 'OAuth User')
|
|
88
|
+
const session = createMockSession(user)
|
|
89
|
+
setStored({ user, session })
|
|
90
|
+
notifyListeners(user)
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
async resetPassword(_email) {
|
|
94
|
+
// No-op in mock
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { AuthAdapter } from '../../types/auth-adapter'
|
|
2
|
+
import type { AuthUser, AuthSession, AuthProvider } from '../../types/auth'
|
|
3
|
+
import { getSupabaseClient } from '../supabase'
|
|
4
|
+
|
|
5
|
+
function mapSupabaseUser(supabaseUser: any): AuthUser {
|
|
6
|
+
return {
|
|
7
|
+
id: supabaseUser.id,
|
|
8
|
+
email: supabaseUser.email ?? '',
|
|
9
|
+
fullName: supabaseUser.user_metadata?.full_name ?? supabaseUser.user_metadata?.name ?? '',
|
|
10
|
+
avatarUrl: supabaseUser.user_metadata?.avatar_url,
|
|
11
|
+
providers: (supabaseUser.app_metadata?.providers ?? [supabaseUser.app_metadata?.provider]).filter(Boolean) as AuthProvider[],
|
|
12
|
+
emailVerified: !!supabaseUser.email_confirmed_at,
|
|
13
|
+
createdAt: supabaseUser.created_at,
|
|
14
|
+
updatedAt: supabaseUser.updated_at ?? supabaseUser.created_at,
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function mapSupabaseSession(session: any): AuthSession {
|
|
19
|
+
return {
|
|
20
|
+
accessToken: session.access_token,
|
|
21
|
+
refreshToken: session.refresh_token,
|
|
22
|
+
expiresAt: session.expires_at ?? Math.floor(Date.now() / 1000) + (session.expires_in ?? 3600),
|
|
23
|
+
user: mapSupabaseUser(session.user),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createSupabaseAuthAdapter(): AuthAdapter {
|
|
28
|
+
const supabase = getSupabaseClient()
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
async getSession() {
|
|
32
|
+
const { data: { session } } = await supabase.auth.getSession()
|
|
33
|
+
if (!session) return null
|
|
34
|
+
const mapped = mapSupabaseSession(session)
|
|
35
|
+
return { user: mapped.user, session: mapped }
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
onAuthStateChange(cb) {
|
|
39
|
+
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
|
|
40
|
+
cb(session ? mapSupabaseUser(session.user) : null)
|
|
41
|
+
})
|
|
42
|
+
return () => subscription.unsubscribe()
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
async signIn(email, password) {
|
|
46
|
+
const { data, error } = await supabase.auth.signInWithPassword({ email, password })
|
|
47
|
+
if (error) throw error
|
|
48
|
+
return mapSupabaseSession(data.session)
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async signUp(email, password, fullName) {
|
|
52
|
+
const { data, error } = await supabase.auth.signUp({
|
|
53
|
+
email,
|
|
54
|
+
password,
|
|
55
|
+
options: { data: { full_name: fullName } },
|
|
56
|
+
})
|
|
57
|
+
if (error) throw error
|
|
58
|
+
|
|
59
|
+
// When email confirmation is enabled, session is null
|
|
60
|
+
if (!data.session) {
|
|
61
|
+
if (!data.user) throw new Error('Signup failed: no user returned')
|
|
62
|
+
// Return a partial session so the UI can show "check your email"
|
|
63
|
+
return {
|
|
64
|
+
accessToken: '',
|
|
65
|
+
refreshToken: '',
|
|
66
|
+
expiresAt: 0,
|
|
67
|
+
user: mapSupabaseUser(data.user),
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return mapSupabaseSession(data.session)
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async signOut() {
|
|
75
|
+
const { error } = await supabase.auth.signOut()
|
|
76
|
+
if (error) throw error
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
async signInWithOAuth(provider: AuthProvider) {
|
|
80
|
+
const { error } = await supabase.auth.signInWithOAuth({
|
|
81
|
+
provider: provider as 'google' | 'github' | 'apple',
|
|
82
|
+
options: {
|
|
83
|
+
redirectTo: typeof window !== 'undefined' ? `${window.location.origin}/auth/callback` : undefined,
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
if (error) throw error
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async resetPassword(email) {
|
|
90
|
+
const { error } = await supabase.auth.resetPasswordForEmail(email, {
|
|
91
|
+
redirectTo: typeof window !== 'undefined' ? `${window.location.origin}/auth/reset-password` : undefined,
|
|
92
|
+
})
|
|
93
|
+
if (error) throw error
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
2
|
+
import type { AuthAdapter } from '../types/auth-adapter'
|
|
3
|
+
|
|
4
|
+
const AuthAdapterContext = createContext<AuthAdapter | null>(null)
|
|
5
|
+
|
|
6
|
+
export const AuthAdapterProvider = AuthAdapterContext.Provider
|
|
7
|
+
|
|
8
|
+
export function useAuthAdapter(): AuthAdapter {
|
|
9
|
+
const adapter = useContext(AuthAdapterContext)
|
|
10
|
+
if (!adapter) {
|
|
11
|
+
throw new Error('useAuthAdapter must be used within an AuthAdapterProvider')
|
|
12
|
+
}
|
|
13
|
+
return adapter
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function useAuthAdapterOptional(): AuthAdapter | null {
|
|
17
|
+
return useContext(AuthAdapterContext)
|
|
18
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useOrganizationStore } from '../stores/organization.store'
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Types
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
interface CacheEntry<T = unknown> {
|
|
8
|
+
data: T
|
|
9
|
+
expiresAt: number
|
|
10
|
+
lastAccess: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CacheStore {
|
|
14
|
+
get<T>(key: string): T | undefined
|
|
15
|
+
set<T>(key: string, data: T, ttl?: number): void
|
|
16
|
+
/** Clear all entries whose key starts with `prefix` */
|
|
17
|
+
invalidate(prefix: string): void
|
|
18
|
+
clear(): void
|
|
19
|
+
size(): number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CacheConfig {
|
|
23
|
+
/** Default time-to-live in ms (default: 60 000 — 1 minute) */
|
|
24
|
+
defaultTTL?: number
|
|
25
|
+
/** Maximum cached entries before LRU eviction (default: 500) */
|
|
26
|
+
maxEntries?: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Stable key helper — deterministic JSON for cache keys
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
function sortDeep(value: unknown): unknown {
|
|
34
|
+
if (value === null || value === undefined || typeof value !== 'object') return value
|
|
35
|
+
if (Array.isArray(value)) return value.map(sortDeep)
|
|
36
|
+
const sorted: Record<string, unknown> = {}
|
|
37
|
+
for (const k of Object.keys(value as Record<string, unknown>).sort()) {
|
|
38
|
+
sorted[k] = sortDeep((value as Record<string, unknown>)[k])
|
|
39
|
+
}
|
|
40
|
+
return sorted
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function stableKey(...parts: unknown[]): string {
|
|
44
|
+
return parts.map((p) => (typeof p === 'string' ? p : JSON.stringify(sortDeep(p)))).join(':')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// CacheStore implementation
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
export function createCacheStore(config?: CacheConfig): CacheStore {
|
|
52
|
+
const defaultTTL = config?.defaultTTL ?? 60_000
|
|
53
|
+
const maxEntries = config?.maxEntries ?? 500
|
|
54
|
+
const entries = new Map<string, CacheEntry>()
|
|
55
|
+
|
|
56
|
+
function evictLRU() {
|
|
57
|
+
if (entries.size <= maxEntries) return
|
|
58
|
+
// Find the entry with the oldest lastAccess
|
|
59
|
+
let oldestKey: string | null = null
|
|
60
|
+
let oldestAccess = Infinity
|
|
61
|
+
for (const [key, entry] of entries) {
|
|
62
|
+
if (entry.lastAccess < oldestAccess) {
|
|
63
|
+
oldestAccess = entry.lastAccess
|
|
64
|
+
oldestKey = key
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (oldestKey) entries.delete(oldestKey)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
get<T>(key: string): T | undefined {
|
|
72
|
+
const entry = entries.get(key)
|
|
73
|
+
if (!entry) return undefined
|
|
74
|
+
if (Date.now() > entry.expiresAt) {
|
|
75
|
+
entries.delete(key)
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
entry.lastAccess = Date.now()
|
|
79
|
+
return entry.data as T
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
set<T>(key: string, data: T, ttl?: number): void {
|
|
83
|
+
const now = Date.now()
|
|
84
|
+
entries.set(key, {
|
|
85
|
+
data,
|
|
86
|
+
expiresAt: now + (ttl ?? defaultTTL),
|
|
87
|
+
lastAccess: now,
|
|
88
|
+
})
|
|
89
|
+
evictLRU()
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
invalidate(prefix: string): void {
|
|
93
|
+
for (const key of entries.keys()) {
|
|
94
|
+
if (key.startsWith(prefix)) {
|
|
95
|
+
entries.delete(key)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
clear(): void {
|
|
101
|
+
entries.clear()
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
size(): number {
|
|
105
|
+
return entries.size
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Global singleton — auto-flushes on tenant/org switch
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
export const globalCache = createCacheStore()
|
|
115
|
+
|
|
116
|
+
// Flush cache when the active organization changes
|
|
117
|
+
let lastOrgId: string | null | undefined
|
|
118
|
+
useOrganizationStore.subscribe((state) => {
|
|
119
|
+
const orgId = state.currentOrg?.id ?? null
|
|
120
|
+
if (lastOrgId !== undefined && orgId !== lastOrgId) {
|
|
121
|
+
globalCache.clear()
|
|
122
|
+
}
|
|
123
|
+
lastOrgId = orgId
|
|
124
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { cn } from '@fayz-ai/ui'
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { PluginAITool, PluginRegistryDef } from '../types/plugins'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Core SaaS-level AI tools — always available, no plugin required.
|
|
5
|
+
*/
|
|
6
|
+
export const coreAITools: PluginAITool[] = [
|
|
7
|
+
{
|
|
8
|
+
id: 'core.business-summary',
|
|
9
|
+
name: 'getBusinessSummary',
|
|
10
|
+
description: 'Returns a summary of the current business: name, plan, vertical, team size, and key metrics.',
|
|
11
|
+
icon: 'Building2',
|
|
12
|
+
mode: 'read',
|
|
13
|
+
parameters: { type: 'object', properties: {}, required: [] },
|
|
14
|
+
category: 'Core',
|
|
15
|
+
suggestions: [
|
|
16
|
+
{ label: "How's my business doing today?" },
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'core.team-members',
|
|
21
|
+
name: 'getTeamMembers',
|
|
22
|
+
description: 'Lists team members for the current tenant with their roles and status.',
|
|
23
|
+
icon: 'Users',
|
|
24
|
+
mode: 'read',
|
|
25
|
+
parameters: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
role: { type: 'string', description: 'Filter by role: owner, admin, manager, staff, viewer' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
permission: { feature: 'team', action: 'read' },
|
|
32
|
+
category: 'Core',
|
|
33
|
+
suggestions: [
|
|
34
|
+
{ label: 'Who is on my team?' },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'core.navigate',
|
|
39
|
+
name: 'navigateTo',
|
|
40
|
+
description: 'Helps the user find and navigate to a specific page or feature in the app.',
|
|
41
|
+
icon: 'Compass',
|
|
42
|
+
mode: 'read',
|
|
43
|
+
parameters: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
page: { type: 'string', description: 'Page name or feature the user is looking for' },
|
|
47
|
+
},
|
|
48
|
+
required: ['page'],
|
|
49
|
+
},
|
|
50
|
+
category: 'Core',
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Auto-generates read tools from plugin registry definitions.
|
|
56
|
+
* Each registry gets a basic "list" tool so plugins get AI capabilities for free.
|
|
57
|
+
*/
|
|
58
|
+
export function generateRegistryTools(pluginId: string, registries: PluginRegistryDef[]): PluginAITool[] {
|
|
59
|
+
return registries
|
|
60
|
+
.filter((r) => !r.readOnly)
|
|
61
|
+
.map((registry) => {
|
|
62
|
+
const plural = registry.entity.namePlural ?? `${registry.entity.name}s`
|
|
63
|
+
return {
|
|
64
|
+
id: `${pluginId}.list-${registry.id}`,
|
|
65
|
+
name: `list${plural.replace(/\s+/g, '')}`,
|
|
66
|
+
description: `Lists all ${plural.toLowerCase()} for the current business.`,
|
|
67
|
+
icon: registry.icon ?? registry.entity.icon,
|
|
68
|
+
mode: 'read' as const,
|
|
69
|
+
parameters: {
|
|
70
|
+
type: 'object' as const,
|
|
71
|
+
properties: {
|
|
72
|
+
search: { type: 'string' as const, description: 'Search text' },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
category: pluginId.charAt(0).toUpperCase() + pluginId.slice(1),
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Formats a tool as a function signature: e.g. "getRevenue(period?)"
|
|
82
|
+
*/
|
|
83
|
+
export function formatToolSignature(tool: PluginAITool): string {
|
|
84
|
+
const params = tool.parameters?.properties
|
|
85
|
+
? Object.entries(tool.parameters.properties)
|
|
86
|
+
.map(([key]) => tool.parameters?.required?.includes(key) ? key : `${key}?`)
|
|
87
|
+
.join(', ')
|
|
88
|
+
: ''
|
|
89
|
+
return `${tool.name}(${params})`
|
|
90
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Factory: creates a ClientOrdersProvider that queries saas_core.orders
|
|
3
|
+
// for a given client (party_id). The UI keeps the historical "stage" language,
|
|
4
|
+
// but current archetype schemas expose this lifecycle as orders.status.
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
import type { ClientOrdersProvider, ClientOrdersQuery, ClientDocument, ClientDocumentStage } from '../types/client-orders'
|
|
8
|
+
import { getSupabaseClientOptional } from './supabase'
|
|
9
|
+
|
|
10
|
+
const STAGE_FILTER_MAP: Record<string, string[]> = {
|
|
11
|
+
quoted: ['draft', 'quoted'],
|
|
12
|
+
booked: ['booked', 'scheduled', 'confirmed', 'in_progress', 'completed'],
|
|
13
|
+
invoiced: ['invoiced', 'partial', 'overdue'],
|
|
14
|
+
paid: ['paid'],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function mapOrderStatusToStage(status: string | null | undefined): ClientDocumentStage {
|
|
18
|
+
switch (status) {
|
|
19
|
+
case 'scheduled':
|
|
20
|
+
case 'confirmed':
|
|
21
|
+
case 'in_progress':
|
|
22
|
+
return 'booked'
|
|
23
|
+
case 'completed':
|
|
24
|
+
return 'completed'
|
|
25
|
+
case 'invoiced':
|
|
26
|
+
case 'paid':
|
|
27
|
+
case 'partial':
|
|
28
|
+
case 'overdue':
|
|
29
|
+
case 'cancelled':
|
|
30
|
+
case 'no_show':
|
|
31
|
+
case 'quoted':
|
|
32
|
+
case 'draft':
|
|
33
|
+
return status
|
|
34
|
+
default:
|
|
35
|
+
return 'draft'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createClientOrdersProvider(): ClientOrdersProvider {
|
|
40
|
+
return {
|
|
41
|
+
async getDocuments(query: ClientOrdersQuery): Promise<{ data: ClientDocument[]; total: number }> {
|
|
42
|
+
const supabase = getSupabaseClientOptional()
|
|
43
|
+
if (!supabase) return { data: [], total: 0 }
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
let qb = supabase
|
|
47
|
+
.schema('saas_core')
|
|
48
|
+
.from('orders')
|
|
49
|
+
.select('id, kind, status, total, reference_number, notes, metadata, created_at', { count: 'exact' })
|
|
50
|
+
.eq('party_id', query.clientId)
|
|
51
|
+
|
|
52
|
+
// Filter by stage groups against the current schema's status column.
|
|
53
|
+
if (query.stages && query.stages.length > 0) {
|
|
54
|
+
const allStages = query.stages.flatMap((s) => STAGE_FILTER_MAP[s] ?? [s])
|
|
55
|
+
qb = qb.in('status', allStages)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
qb = qb.order('created_at', { ascending: false })
|
|
59
|
+
|
|
60
|
+
const page = query.page ?? 1
|
|
61
|
+
const pageSize = query.pageSize ?? 50
|
|
62
|
+
qb = qb.range((page - 1) * pageSize, page * pageSize - 1)
|
|
63
|
+
|
|
64
|
+
const { data, count } = await qb
|
|
65
|
+
const orderIds = (data ?? []).map((row: any) => row.id).filter(Boolean)
|
|
66
|
+
|
|
67
|
+
const bookingStartsByOrderId = new Map<string, string>()
|
|
68
|
+
if (orderIds.length > 0) {
|
|
69
|
+
const { data: bookings } = await supabase
|
|
70
|
+
.schema('saas_core')
|
|
71
|
+
.from('bookings')
|
|
72
|
+
.select('order_id, starts_at')
|
|
73
|
+
.in('order_id', orderIds)
|
|
74
|
+
|
|
75
|
+
for (const booking of bookings ?? []) {
|
|
76
|
+
if (booking.order_id && booking.starts_at && !bookingStartsByOrderId.has(booking.order_id)) {
|
|
77
|
+
bookingStartsByOrderId.set(booking.order_id, booking.starts_at)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const results: ClientDocument[] = (data ?? []).map((row: any) => {
|
|
83
|
+
const meta = (row.metadata ?? {}) as Record<string, unknown>
|
|
84
|
+
const startsAt = bookingStartsByOrderId.get(row.id)
|
|
85
|
+
return {
|
|
86
|
+
id: row.id,
|
|
87
|
+
kind: row.kind as string,
|
|
88
|
+
stage: mapOrderStatusToStage(row.status as string | null | undefined),
|
|
89
|
+
referenceNumber: row.reference_number ?? undefined,
|
|
90
|
+
date: startsAt?.slice(0, 10) ?? (row.created_at as string)?.slice(0, 10) ?? '',
|
|
91
|
+
startsAt,
|
|
92
|
+
total: Number(row.total) || 0,
|
|
93
|
+
paidAmount: Number(meta.paidAmount) || 0,
|
|
94
|
+
description: (meta.itemsSummary as string) ?? (meta.serviceNames as string) ?? row.notes ?? undefined,
|
|
95
|
+
createdAt: row.created_at as string,
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
return { data: results, total: count ?? results.length }
|
|
100
|
+
} catch {
|
|
101
|
+
return { data: [], total: 0 }
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Column descriptor for generic CSV export */
|
|
2
|
+
export interface CSVColumn {
|
|
3
|
+
key: string
|
|
4
|
+
label: string
|
|
5
|
+
format?: (value: any, row: Record<string, any>) => string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function escapeCSVField(value: unknown): string {
|
|
9
|
+
if (value == null) return ''
|
|
10
|
+
const str = String(value)
|
|
11
|
+
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
12
|
+
return `"${str.replace(/"/g, '""')}"`
|
|
13
|
+
}
|
|
14
|
+
return str
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Build a CSV string from columns + rows. Each cell runs through `column.format` if provided, then escaping. */
|
|
18
|
+
export function buildCSV(
|
|
19
|
+
columns: CSVColumn[],
|
|
20
|
+
rows: Record<string, any>[],
|
|
21
|
+
): string {
|
|
22
|
+
const header = columns.map((c) => escapeCSVField(c.label)).join(',')
|
|
23
|
+
const body = rows.map((row) =>
|
|
24
|
+
columns
|
|
25
|
+
.map((col) => {
|
|
26
|
+
const raw = row[col.key]
|
|
27
|
+
const formatted = col.format ? col.format(raw, row) : raw
|
|
28
|
+
return escapeCSVField(formatted)
|
|
29
|
+
})
|
|
30
|
+
.join(','),
|
|
31
|
+
)
|
|
32
|
+
return [header, ...body].join('\n')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Trigger a CSV file download in the browser. */
|
|
36
|
+
export function downloadCSV(csv: string, filename: string): void {
|
|
37
|
+
const bom = '\uFEFF'
|
|
38
|
+
const blob = new Blob([bom + csv], { type: 'text/csv;charset=utf-8;' })
|
|
39
|
+
const url = URL.createObjectURL(blob)
|
|
40
|
+
const a = document.createElement('a')
|
|
41
|
+
a.href = url
|
|
42
|
+
a.download = filename.endsWith('.csv') ? filename : `${filename}.csv`
|
|
43
|
+
a.click()
|
|
44
|
+
URL.revokeObjectURL(url)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Convenience: build + download in one call. */
|
|
48
|
+
export function exportCSV(
|
|
49
|
+
columns: CSVColumn[],
|
|
50
|
+
rows: Record<string, any>[],
|
|
51
|
+
filename: string,
|
|
52
|
+
): void {
|
|
53
|
+
downloadCSV(buildCSV(columns, rows), filename)
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request deduplication — prevents duplicate in-flight requests with the same key.
|
|
3
|
+
* If a request with the same key is already in-flight, returns the existing promise.
|
|
4
|
+
*/
|
|
5
|
+
const inflight = new Map<string, Promise<any>>()
|
|
6
|
+
|
|
7
|
+
export function dedup<T>(key: string, fn: () => Promise<T>): Promise<T> {
|
|
8
|
+
const existing = inflight.get(key)
|
|
9
|
+
if (existing) return existing as Promise<T>
|
|
10
|
+
|
|
11
|
+
const promise = fn().finally(() => {
|
|
12
|
+
inflight.delete(key)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
inflight.set(key, promise)
|
|
16
|
+
return promise
|
|
17
|
+
}
|