@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
package/src/org/store.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import { setActiveTenantId, type Organization, type OrgMember, type OrgMembership } from '@fayz-ai/core'
|
|
3
|
+
|
|
4
|
+
const STORAGE_KEY = 'fayz:current-org'
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Persistence helpers
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
function getPersistedOrgId(): string | null {
|
|
11
|
+
try {
|
|
12
|
+
if (typeof localStorage === 'undefined') return null
|
|
13
|
+
return localStorage.getItem(STORAGE_KEY)
|
|
14
|
+
} catch {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function persistOrgId(orgId: string | null): void {
|
|
20
|
+
try {
|
|
21
|
+
if (typeof localStorage === 'undefined') return
|
|
22
|
+
if (orgId) {
|
|
23
|
+
localStorage.setItem(STORAGE_KEY, orgId)
|
|
24
|
+
} else {
|
|
25
|
+
localStorage.removeItem(STORAGE_KEY)
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// ignore
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Store interface
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
export interface OrgStore {
|
|
37
|
+
currentOrg: Organization | null
|
|
38
|
+
userOrgs: OrgMembership[]
|
|
39
|
+
members: OrgMember[]
|
|
40
|
+
loading: boolean
|
|
41
|
+
|
|
42
|
+
setCurrentOrg: (org: Organization | null) => void
|
|
43
|
+
setUserOrgs: (orgs: OrgMembership[]) => void
|
|
44
|
+
setMembers: (members: OrgMember[]) => void
|
|
45
|
+
setLoading: (loading: boolean) => void
|
|
46
|
+
reset: () => void
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const useOrganizationStore = create<OrgStore>((set) => ({
|
|
50
|
+
currentOrg: null,
|
|
51
|
+
userOrgs: [],
|
|
52
|
+
members: [],
|
|
53
|
+
loading: false,
|
|
54
|
+
|
|
55
|
+
setCurrentOrg: (org) => {
|
|
56
|
+
setActiveTenantId(org?.id)
|
|
57
|
+
persistOrgId(org?.id ?? null)
|
|
58
|
+
set({ currentOrg: org })
|
|
59
|
+
},
|
|
60
|
+
setUserOrgs: (orgs) => set({ userOrgs: orgs }),
|
|
61
|
+
setMembers: (members) => set({ members }),
|
|
62
|
+
setLoading: (loading) => set({ loading }),
|
|
63
|
+
reset: () => {
|
|
64
|
+
setActiveTenantId(undefined)
|
|
65
|
+
persistOrgId(null)
|
|
66
|
+
set({ currentOrg: null, userOrgs: [], members: [], loading: false })
|
|
67
|
+
},
|
|
68
|
+
}))
|
|
69
|
+
|
|
70
|
+
export { getPersistedOrgId }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import type { PermissionAction } from '@fayz-ai/core'
|
|
3
|
+
import { usePermissionOptional } from './context'
|
|
4
|
+
|
|
5
|
+
interface PermissionGateProps {
|
|
6
|
+
feature: string
|
|
7
|
+
action: PermissionAction
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
fallback?: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Renders children only when the current user has the given feature+action. */
|
|
13
|
+
export function PermissionGate({ feature, action, children, fallback }: PermissionGateProps) {
|
|
14
|
+
const can = usePermissionOptional()
|
|
15
|
+
if (!can(feature, action)) return <>{fallback ?? null}</>
|
|
16
|
+
return <>{children}</>
|
|
17
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import type { FeatureDeclaration, PermissionsConfig, PermissionProfile } from '@fayz-ai/core'
|
|
3
|
+
import { usePermissionsStore } from './store'
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Permission check helpers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
function profileHasPermission(
|
|
10
|
+
profile: PermissionProfile | null,
|
|
11
|
+
feature: string,
|
|
12
|
+
action?: string,
|
|
13
|
+
): boolean {
|
|
14
|
+
if (!profile) return true
|
|
15
|
+
|
|
16
|
+
const actions = profile.grants[feature]
|
|
17
|
+
if (!actions) return false
|
|
18
|
+
|
|
19
|
+
if (!action) return actions.length > 0
|
|
20
|
+
|
|
21
|
+
return actions.includes(action) || actions.includes('manage')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Context
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
interface PermissionsContextValue {
|
|
29
|
+
hasPermission: (feature: string, action?: string) => boolean
|
|
30
|
+
canImpersonate: boolean
|
|
31
|
+
startImpersonation: (profile: PermissionProfile) => void
|
|
32
|
+
stopImpersonation: () => void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const PermissionsContext = React.createContext<PermissionsContextValue | null>(null)
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Provider
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
export interface PermissionsProviderProps {
|
|
42
|
+
config?: PermissionsConfig
|
|
43
|
+
children: React.ReactNode
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function PermissionsProvider({ config, children }: PermissionsProviderProps) {
|
|
47
|
+
const { setFeatures, startImpersonation, stopImpersonation, currentProfile, isImpersonating } =
|
|
48
|
+
usePermissionsStore()
|
|
49
|
+
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
if (config?.features) {
|
|
52
|
+
setFeatures(config.features)
|
|
53
|
+
}
|
|
54
|
+
}, [config, setFeatures])
|
|
55
|
+
|
|
56
|
+
function hasPermission(feature: string, action?: string): boolean {
|
|
57
|
+
return profileHasPermission(currentProfile, feature, action)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const value: PermissionsContextValue = {
|
|
61
|
+
hasPermission,
|
|
62
|
+
canImpersonate: !isImpersonating,
|
|
63
|
+
startImpersonation,
|
|
64
|
+
stopImpersonation,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<PermissionsContext.Provider value={value}>
|
|
69
|
+
{children}
|
|
70
|
+
</PermissionsContext.Provider>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Hooks
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns a function that checks whether the current user has a given
|
|
80
|
+
* permission. Throws if used outside <PermissionsProvider>.
|
|
81
|
+
*/
|
|
82
|
+
export function usePermission(): (feature: string, action?: string) => boolean {
|
|
83
|
+
const ctx = React.useContext(PermissionsContext)
|
|
84
|
+
if (!ctx) {
|
|
85
|
+
throw new Error('usePermission must be used inside <PermissionsProvider>')
|
|
86
|
+
}
|
|
87
|
+
return ctx.hasPermission
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Like usePermission, but degrades to allow-all when no <PermissionsProvider>
|
|
92
|
+
* is mounted — so SDK components (e.g. the CRUD engine's PermissionGate) work
|
|
93
|
+
* under any host shell instead of throwing. Gating is an enhancement, not a
|
|
94
|
+
* hard requirement at the component level.
|
|
95
|
+
*/
|
|
96
|
+
export function usePermissionOptional(): (feature: string, action?: string) => boolean {
|
|
97
|
+
const ctx = React.useContext(PermissionsContext)
|
|
98
|
+
return ctx?.hasPermission ?? (() => true)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Convenience hook — returns true/false for a specific feature + action.
|
|
103
|
+
* Example: `const canEdit = useHasPermission('clients', 'write')`
|
|
104
|
+
*/
|
|
105
|
+
export function useHasPermission(feature: string, action?: string): boolean {
|
|
106
|
+
const ctx = React.useContext(PermissionsContext)
|
|
107
|
+
if (!ctx) {
|
|
108
|
+
throw new Error('useHasPermission must be used inside <PermissionsProvider>')
|
|
109
|
+
}
|
|
110
|
+
return ctx.hasPermission(feature, action)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Convenience hook that surfaces the full permissions context, including
|
|
115
|
+
* impersonation controls.
|
|
116
|
+
*/
|
|
117
|
+
export function usePermissions(): PermissionsContextValue {
|
|
118
|
+
const ctx = React.useContext(PermissionsContext)
|
|
119
|
+
if (!ctx) {
|
|
120
|
+
throw new Error('usePermissions must be used inside <PermissionsProvider>')
|
|
121
|
+
}
|
|
122
|
+
return ctx
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Re-export feature declarations for use in feature flag checks
|
|
126
|
+
export type { FeatureDeclaration }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Store
|
|
2
|
+
export { usePermissionsStore } from './store'
|
|
3
|
+
export type { PermissionsStore } from './store'
|
|
4
|
+
|
|
5
|
+
// Context & hooks
|
|
6
|
+
export {
|
|
7
|
+
PermissionsProvider,
|
|
8
|
+
usePermission,
|
|
9
|
+
usePermissionOptional,
|
|
10
|
+
useHasPermission,
|
|
11
|
+
usePermissions,
|
|
12
|
+
} from './context'
|
|
13
|
+
export type { PermissionsProviderProps, FeatureDeclaration } from './context'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import type { FeatureDeclaration, PermissionProfile } from '@fayz-ai/core'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Store interface
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export interface PermissionsStore {
|
|
9
|
+
features: FeatureDeclaration[]
|
|
10
|
+
profiles: PermissionProfile[]
|
|
11
|
+
/** The effective profile used for permission checks (may be impersonated) */
|
|
12
|
+
currentProfile: PermissionProfile | null
|
|
13
|
+
/** The user's actual profile (preserved during impersonation) */
|
|
14
|
+
realProfile: PermissionProfile | null
|
|
15
|
+
/** The profile being previewed, if any */
|
|
16
|
+
impersonatedProfile: PermissionProfile | null
|
|
17
|
+
/** Whether impersonation is active */
|
|
18
|
+
isImpersonating: boolean
|
|
19
|
+
|
|
20
|
+
setFeatures: (features: FeatureDeclaration[]) => void
|
|
21
|
+
setProfiles: (profiles: PermissionProfile[]) => void
|
|
22
|
+
setCurrentProfile: (profile: PermissionProfile | null) => void
|
|
23
|
+
startImpersonation: (profile: PermissionProfile) => void
|
|
24
|
+
stopImpersonation: () => void
|
|
25
|
+
reset: () => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const usePermissionsStore = create<PermissionsStore>((set, get) => ({
|
|
29
|
+
features: [],
|
|
30
|
+
profiles: [],
|
|
31
|
+
currentProfile: null,
|
|
32
|
+
realProfile: null,
|
|
33
|
+
impersonatedProfile: null,
|
|
34
|
+
isImpersonating: false,
|
|
35
|
+
|
|
36
|
+
setFeatures: (features) => set({ features }),
|
|
37
|
+
setProfiles: (profiles) => set({ profiles }),
|
|
38
|
+
setCurrentProfile: (profile) =>
|
|
39
|
+
set({
|
|
40
|
+
currentProfile: profile,
|
|
41
|
+
realProfile: profile,
|
|
42
|
+
impersonatedProfile: null,
|
|
43
|
+
isImpersonating: false,
|
|
44
|
+
}),
|
|
45
|
+
startImpersonation: (profile) =>
|
|
46
|
+
set({
|
|
47
|
+
currentProfile: profile,
|
|
48
|
+
impersonatedProfile: profile,
|
|
49
|
+
isImpersonating: true,
|
|
50
|
+
}),
|
|
51
|
+
stopImpersonation: () => {
|
|
52
|
+
const { realProfile } = get()
|
|
53
|
+
set({
|
|
54
|
+
currentProfile: realProfile,
|
|
55
|
+
impersonatedProfile: null,
|
|
56
|
+
isImpersonating: false,
|
|
57
|
+
})
|
|
58
|
+
},
|
|
59
|
+
reset: () =>
|
|
60
|
+
set({
|
|
61
|
+
features: [],
|
|
62
|
+
profiles: [],
|
|
63
|
+
currentProfile: null,
|
|
64
|
+
realProfile: null,
|
|
65
|
+
impersonatedProfile: null,
|
|
66
|
+
isImpersonating: false,
|
|
67
|
+
}),
|
|
68
|
+
}))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Page stub for modules that are planned but not built yet.
|
|
5
|
+
* Usage: { path: '/marketing', label: 'Marketing', icon: 'Megaphone', component: createPlaceholder('Marketing') }
|
|
6
|
+
*/
|
|
7
|
+
export function createPlaceholder(title: string, description?: string): React.FC {
|
|
8
|
+
return function PlaceholderPage() {
|
|
9
|
+
return React.createElement(
|
|
10
|
+
'div',
|
|
11
|
+
{ className: 'mx-auto max-w-4xl' },
|
|
12
|
+
React.createElement('h1', { className: 'text-3xl font-bold text-foreground' }, title),
|
|
13
|
+
description
|
|
14
|
+
? React.createElement('p', { className: 'mt-1 text-muted-foreground' }, description)
|
|
15
|
+
: null,
|
|
16
|
+
React.createElement(
|
|
17
|
+
'div',
|
|
18
|
+
{ className: 'mt-8 flex items-center justify-center rounded-card border border-dashed border-border bg-card p-16' },
|
|
19
|
+
React.createElement(
|
|
20
|
+
'div',
|
|
21
|
+
{ className: 'text-center' },
|
|
22
|
+
React.createElement('p', { className: 'text-lg font-medium text-muted-foreground' }, 'Coming Soon'),
|
|
23
|
+
React.createElement(
|
|
24
|
+
'p',
|
|
25
|
+
{ className: 'mt-1 text-sm text-muted-foreground/70' },
|
|
26
|
+
'This module is being built as a plugin.',
|
|
27
|
+
),
|
|
28
|
+
),
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Settings } from 'lucide-react'
|
|
3
|
+
import { Button } from '@fayz-ai/ui'
|
|
4
|
+
import type { PluginQuickAction } from '@fayz-ai/core'
|
|
5
|
+
import { QuickActionsButton } from './QuickActionsButton'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ModuleActionBar — the single, standard top-right action area for every plugin
|
|
9
|
+
* module page. Pass it straight to `ModulePage`'s `headerAction` prop.
|
|
10
|
+
*
|
|
11
|
+
* Owns the two things every module's header repeated by hand:
|
|
12
|
+
* - a primary "New" action (rendered via QuickActionsButton, which adapts its
|
|
13
|
+
* variant to the layout: solid in `tabs` products, outline in `rail`),
|
|
14
|
+
* - a settings gear that deep-links into the central Settings area.
|
|
15
|
+
*
|
|
16
|
+
* Plugins stop hand-rolling `<div className="flex gap-2">…gear…</div>` blocks:
|
|
17
|
+
* headerAction={<ModuleActionBar quickActions={quickActions} settingsPath="/settings/crm" />}
|
|
18
|
+
*/
|
|
19
|
+
export function ModuleActionBar({
|
|
20
|
+
quickActions,
|
|
21
|
+
settingsPath,
|
|
22
|
+
settingsLabel,
|
|
23
|
+
leading,
|
|
24
|
+
trailing,
|
|
25
|
+
}: {
|
|
26
|
+
/** Primary insert actions. Omit (or empty) to hide the New button. */
|
|
27
|
+
quickActions?: PluginQuickAction[]
|
|
28
|
+
/** Hash route to the central settings tab, e.g. '/settings/crm'. Omit to hide the gear. */
|
|
29
|
+
settingsPath?: string
|
|
30
|
+
/** Accessible title for the gear button. */
|
|
31
|
+
settingsLabel?: string
|
|
32
|
+
/** Optional extra controls rendered before the New button (e.g. a filter). */
|
|
33
|
+
leading?: React.ReactNode
|
|
34
|
+
/** Optional extra controls rendered after the gear. */
|
|
35
|
+
trailing?: React.ReactNode
|
|
36
|
+
}) {
|
|
37
|
+
const hasQuickActions = !!quickActions && quickActions.length > 0
|
|
38
|
+
if (!hasQuickActions && !settingsPath && !leading && !trailing) return null
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="flex items-center gap-2">
|
|
42
|
+
{leading}
|
|
43
|
+
{hasQuickActions && <QuickActionsButton actions={quickActions} />}
|
|
44
|
+
{settingsPath && (
|
|
45
|
+
<Button
|
|
46
|
+
variant="outline"
|
|
47
|
+
size="icon"
|
|
48
|
+
onClick={() => { window.location.hash = settingsPath }}
|
|
49
|
+
title={settingsLabel ?? 'Settings'}
|
|
50
|
+
>
|
|
51
|
+
<Settings className="h-4 w-4" />
|
|
52
|
+
</Button>
|
|
53
|
+
)}
|
|
54
|
+
{trailing}
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React, { useState, useMemo } from 'react'
|
|
2
|
+
import type { PluginRegistryDef } from '@fayz-ai/core'
|
|
3
|
+
import type { EntityDef } from '@fayz-ai/core'
|
|
4
|
+
import { createCrudStore } from '../stores/createCrudStore'
|
|
5
|
+
import { createMockProvider } from '@fayz-ai/core'
|
|
6
|
+
import { CrudPage } from '../crud/CrudPage'
|
|
7
|
+
import { cn } from '@fayz-ai/ui'
|
|
8
|
+
import { ICON_MAP } from '@fayz-ai/ui'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Internal CRUD view for a single registry.
|
|
12
|
+
* Manages its own store and uses a hash-based basePath for proper CRUD routing.
|
|
13
|
+
*/
|
|
14
|
+
function RegistryCrudView({ registry, basePath }: { registry: PluginRegistryDef; basePath: string }) {
|
|
15
|
+
const useStore = useMemo(() => {
|
|
16
|
+
const data = (registry.mockData ?? registry.seedData ?? []) as any[]
|
|
17
|
+
const provider = createMockProvider(registry.entity as EntityDef<any>, data)
|
|
18
|
+
return createCrudStore(provider)
|
|
19
|
+
}, [registry.id])
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<CrudPage
|
|
23
|
+
entityDef={registry.entity as EntityDef<any>}
|
|
24
|
+
useStore={useStore as any}
|
|
25
|
+
basePath={basePath}
|
|
26
|
+
display={registry.display === 'cards' ? 'cards' : 'table'}
|
|
27
|
+
readOnly={registry.readOnly}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reusable tabbed CRUD manager for plugin registries.
|
|
34
|
+
* @param routeBase — hash route prefix for CRUD sub-routing (e.g. '/financial/settings')
|
|
35
|
+
*/
|
|
36
|
+
export function PluginRegistryManager({ registries, routeBase, className }: {
|
|
37
|
+
registries: PluginRegistryDef[]
|
|
38
|
+
/** Base route for CRUD navigation (e.g. '/financial/settings'). Each registry gets routeBase/registryId */
|
|
39
|
+
routeBase?: string
|
|
40
|
+
className?: string
|
|
41
|
+
}) {
|
|
42
|
+
// Derive active tab from hash if routeBase provided, otherwise use state
|
|
43
|
+
const getTabFromHash = () => {
|
|
44
|
+
if (!routeBase) return registries[0]?.id ?? ''
|
|
45
|
+
const hash = window.location.hash.slice(1) || '/'
|
|
46
|
+
if (hash.startsWith(routeBase + '/')) {
|
|
47
|
+
const rest = hash.slice(routeBase.length + 1)
|
|
48
|
+
const tabId = rest.split('/')[0]
|
|
49
|
+
if (registries.find((r) => r.id === tabId)) return tabId
|
|
50
|
+
}
|
|
51
|
+
return registries[0]?.id ?? ''
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const [activeTab, setActiveTab] = useState(getTabFromHash)
|
|
55
|
+
|
|
56
|
+
// Sync tab from hash changes
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
if (!routeBase) return
|
|
59
|
+
const handler = () => setActiveTab(getTabFromHash())
|
|
60
|
+
window.addEventListener('hashchange', handler)
|
|
61
|
+
return () => window.removeEventListener('hashchange', handler)
|
|
62
|
+
}, [routeBase])
|
|
63
|
+
|
|
64
|
+
function switchTab(tabId: string) {
|
|
65
|
+
setActiveTab(tabId)
|
|
66
|
+
if (routeBase) {
|
|
67
|
+
window.location.hash = `${routeBase}/${tabId}`
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (registries.length === 0) return null
|
|
72
|
+
|
|
73
|
+
const activeRegistry = registries.find((r) => r.id === activeTab) ?? registries[0]
|
|
74
|
+
const crudBasePath = routeBase ? `${routeBase}/${activeRegistry.id}` : `/__registry/${activeRegistry.id}`
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div className={cn('space-y-4', className)}>
|
|
78
|
+
{/* Tabs */}
|
|
79
|
+
<div className="flex gap-1 overflow-x-auto pb-1">
|
|
80
|
+
{registries.map((reg) => {
|
|
81
|
+
const Icon = reg.icon ? (ICON_MAP[reg.icon] ?? null) : null
|
|
82
|
+
const active = reg.id === activeTab
|
|
83
|
+
return (
|
|
84
|
+
<button
|
|
85
|
+
key={reg.id}
|
|
86
|
+
onClick={() => switchTab(reg.id)}
|
|
87
|
+
className={cn(
|
|
88
|
+
'flex items-center gap-1.5 whitespace-nowrap rounded-lg px-3 py-2 text-sm font-medium transition-colors shrink-0',
|
|
89
|
+
active
|
|
90
|
+
? 'bg-primary text-primary-foreground'
|
|
91
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted/50'
|
|
92
|
+
)}
|
|
93
|
+
>
|
|
94
|
+
{Icon && <Icon className="h-3.5 w-3.5" />}
|
|
95
|
+
{reg.entity.name}
|
|
96
|
+
</button>
|
|
97
|
+
)
|
|
98
|
+
})}
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
{/* Description */}
|
|
102
|
+
{activeRegistry?.description && (
|
|
103
|
+
<p className="text-xs text-muted-foreground">{activeRegistry.description}</p>
|
|
104
|
+
)}
|
|
105
|
+
|
|
106
|
+
{/* CRUD content — keyed so it remounts when switching tabs */}
|
|
107
|
+
{activeRegistry && <RegistryCrudView key={activeRegistry.id} registry={activeRegistry} basePath={crudBasePath} />}
|
|
108
|
+
</div>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react'
|
|
2
|
+
import { Plus, X } from 'lucide-react'
|
|
3
|
+
import { cn } from '@fayz-ai/ui'
|
|
4
|
+
import { Button } from '@fayz-ai/ui'
|
|
5
|
+
import { ICON_MAP } from '@fayz-ai/ui'
|
|
6
|
+
import { useModuleLayout } from '@fayz-ai/ui'
|
|
7
|
+
import type { PluginQuickAction } from '@fayz-ai/core'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Quick actions button for plugin modules.
|
|
11
|
+
* Shows a "+" button that opens a popover with contextual insert actions.
|
|
12
|
+
* Reusable by any plugin.
|
|
13
|
+
*/
|
|
14
|
+
export function QuickActionsButton({ actions, className }: {
|
|
15
|
+
actions: PluginQuickAction[]
|
|
16
|
+
className?: string
|
|
17
|
+
}) {
|
|
18
|
+
const [open, setOpen] = useState(false)
|
|
19
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
20
|
+
// Solid primary in sidebar/tabs products (GHL-style), outline in topbar products.
|
|
21
|
+
const variant = useModuleLayout() === 'tabs' ? 'default' : 'outline'
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
function handler(e: MouseEvent) {
|
|
25
|
+
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)
|
|
26
|
+
}
|
|
27
|
+
document.addEventListener('mousedown', handler)
|
|
28
|
+
return () => document.removeEventListener('mousedown', handler)
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
31
|
+
if (actions.length === 0) return null
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div ref={ref} className={cn('relative', className)}>
|
|
35
|
+
<Button
|
|
36
|
+
variant={variant}
|
|
37
|
+
size="sm"
|
|
38
|
+
onClick={() => setOpen(!open)}
|
|
39
|
+
className={cn(open && variant === 'outline' && 'bg-muted')}
|
|
40
|
+
>
|
|
41
|
+
<Plus className={cn('h-3.5 w-3.5 transition-transform duration-200', open && 'rotate-45')} />
|
|
42
|
+
<span>New</span>
|
|
43
|
+
</Button>
|
|
44
|
+
|
|
45
|
+
{open && (
|
|
46
|
+
<div className="fayz-glass-surface absolute top-full right-0 z-50 mt-2 w-64 rounded-xl border bg-popover shadow-lg overflow-hidden animate-in fade-in-0 zoom-in-95 origin-top-right">
|
|
47
|
+
<div className="p-1.5">
|
|
48
|
+
{actions.map((action) => {
|
|
49
|
+
const Icon = action.icon ? (ICON_MAP[action.icon] ?? Plus) : Plus
|
|
50
|
+
return (
|
|
51
|
+
<button
|
|
52
|
+
key={action.id}
|
|
53
|
+
onClick={() => { action.action(); setOpen(false) }}
|
|
54
|
+
className="w-full flex items-center gap-3 rounded-lg px-3 py-2.5 text-left hover:bg-muted/50 transition-colors group"
|
|
55
|
+
>
|
|
56
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-muted group-hover:bg-primary/10 transition-colors shrink-0">
|
|
57
|
+
<Icon className="h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" />
|
|
58
|
+
</div>
|
|
59
|
+
<div className="min-w-0">
|
|
60
|
+
<p className="text-sm font-medium">{action.label}</p>
|
|
61
|
+
{action.description && (
|
|
62
|
+
<p className="text-[10px] text-muted-foreground leading-tight">{action.description}</p>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</button>
|
|
66
|
+
)
|
|
67
|
+
})}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@fayz-ai/ui'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// ToggleRow — a single setting with label, description, and checkbox
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export function ToggleRow({ label, description, checked, onChange, disabled }: {
|
|
9
|
+
label: string
|
|
10
|
+
description?: string
|
|
11
|
+
checked: boolean
|
|
12
|
+
onChange: (checked: boolean) => void
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<label className={cn(
|
|
17
|
+
'flex items-center justify-between gap-4 py-3 cursor-pointer',
|
|
18
|
+
disabled && 'opacity-50 cursor-not-allowed',
|
|
19
|
+
)}>
|
|
20
|
+
<div className="min-w-0">
|
|
21
|
+
<p className="text-sm font-medium">{label}</p>
|
|
22
|
+
{description && <p className="text-xs text-muted-foreground mt-0.5">{description}</p>}
|
|
23
|
+
</div>
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
role="switch"
|
|
27
|
+
aria-checked={checked}
|
|
28
|
+
disabled={disabled}
|
|
29
|
+
onClick={() => !disabled && onChange(!checked)}
|
|
30
|
+
className={cn(
|
|
31
|
+
'relative inline-flex h-5 w-9 shrink-0 rounded-full transition-colors duration-200',
|
|
32
|
+
checked ? 'bg-primary' : 'bg-muted',
|
|
33
|
+
disabled && 'cursor-not-allowed',
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
<span className={cn(
|
|
37
|
+
'inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 mt-0.5',
|
|
38
|
+
checked ? 'translate-x-4 ml-0.5' : 'translate-x-0.5',
|
|
39
|
+
)} />
|
|
40
|
+
</button>
|
|
41
|
+
</label>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// SelectRow — a single setting with label, description, and select dropdown
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
export function SelectRow({ label, description, value, options, onChange, disabled }: {
|
|
50
|
+
label: string
|
|
51
|
+
description?: string
|
|
52
|
+
value: string
|
|
53
|
+
options: { value: string; label: string }[]
|
|
54
|
+
onChange: (value: string) => void
|
|
55
|
+
disabled?: boolean
|
|
56
|
+
}) {
|
|
57
|
+
return (
|
|
58
|
+
<label className={cn(
|
|
59
|
+
'flex items-center justify-between gap-4 py-3',
|
|
60
|
+
disabled && 'opacity-50 cursor-not-allowed',
|
|
61
|
+
)}>
|
|
62
|
+
<div className="min-w-0">
|
|
63
|
+
<p className="text-sm font-medium">{label}</p>
|
|
64
|
+
{description && <p className="text-xs text-muted-foreground mt-0.5">{description}</p>}
|
|
65
|
+
</div>
|
|
66
|
+
<select
|
|
67
|
+
value={value}
|
|
68
|
+
onChange={(e) => onChange(e.target.value)}
|
|
69
|
+
disabled={disabled}
|
|
70
|
+
className="h-8 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2 text-sm"
|
|
71
|
+
>
|
|
72
|
+
{options.map((opt) => (
|
|
73
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
74
|
+
))}
|
|
75
|
+
</select>
|
|
76
|
+
</label>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// SettingsGroup — a bordered section with title and children
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
export function SettingsGroup({ title, description, children }: {
|
|
85
|
+
title: string
|
|
86
|
+
description?: string
|
|
87
|
+
children: React.ReactNode
|
|
88
|
+
}) {
|
|
89
|
+
return (
|
|
90
|
+
<div>
|
|
91
|
+
<div className="px-1 pb-1 pt-4 first:pt-0">
|
|
92
|
+
<h3 className="text-sm font-semibold">{title}</h3>
|
|
93
|
+
{description && <p className="text-[10px] text-muted-foreground mt-0.5">{description}</p>}
|
|
94
|
+
</div>
|
|
95
|
+
<div className="rounded-lg border bg-card shadow-sm px-5 divide-y">
|
|
96
|
+
{children}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
}
|