@fayz-ai/saas 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/access/context.d.ts +47 -0
  2. package/dist/access/context.d.ts.map +1 -0
  3. package/dist/access/headless.d.ts +21 -0
  4. package/dist/access/headless.d.ts.map +1 -0
  5. package/dist/access/index.d.ts +12 -0
  6. package/dist/access/index.d.ts.map +1 -0
  7. package/dist/access/limits-registry.d.ts +33 -0
  8. package/dist/access/limits-registry.d.ts.map +1 -0
  9. package/dist/access/module-nav.d.ts +15 -0
  10. package/dist/access/module-nav.d.ts.map +1 -0
  11. package/dist/access/resolver.d.ts +2 -0
  12. package/dist/access/resolver.d.ts.map +1 -0
  13. package/dist/access/types.d.ts +43 -0
  14. package/dist/access/types.d.ts.map +1 -0
  15. package/dist/access/upgrade-modal-store.d.ts +23 -0
  16. package/dist/access/upgrade-modal-store.d.ts.map +1 -0
  17. package/dist/app/AdminShell.d.ts +3 -0
  18. package/dist/app/AdminShell.d.ts.map +1 -1
  19. package/dist/app/admin-app.d.ts +3 -1
  20. package/dist/app/admin-app.d.ts.map +1 -1
  21. package/dist/app/config.d.ts +47 -2
  22. package/dist/app/config.d.ts.map +1 -1
  23. package/dist/app/core-entities.d.ts +25 -0
  24. package/dist/app/core-entities.d.ts.map +1 -0
  25. package/dist/app/derive-contract.d.ts +8 -0
  26. package/dist/app/derive-contract.d.ts.map +1 -0
  27. package/dist/app/scaffold.d.ts.map +1 -1
  28. package/dist/archetype-lookup.d.ts.map +1 -1
  29. package/dist/billing/index.cjs +6 -2
  30. package/dist/billing/index.d.ts +2 -0
  31. package/dist/billing/index.d.ts.map +1 -1
  32. package/dist/billing/index.js +1 -1
  33. package/dist/billing/plan-badge.d.ts +17 -0
  34. package/dist/billing/plan-badge.d.ts.map +1 -0
  35. package/dist/billing/store.d.ts +6 -1
  36. package/dist/billing/store.d.ts.map +1 -1
  37. package/dist/{chunk-3M25GRRU.js → chunk-I3DY47N5.js} +3 -4
  38. package/dist/chunk-I3DY47N5.js.map +1 -0
  39. package/dist/chunk-NNFGMUF2.cjs +39 -0
  40. package/dist/chunk-NNFGMUF2.cjs.map +1 -0
  41. package/dist/{chunk-4XTQ4V5L.cjs → chunk-SAZLW54Y.cjs} +16 -12
  42. package/dist/chunk-SAZLW54Y.cjs.map +1 -0
  43. package/dist/{chunk-IPWCHJPE.cjs → chunk-VKYZACG5.cjs} +5 -6
  44. package/dist/chunk-VKYZACG5.cjs.map +1 -0
  45. package/dist/chunk-VN5NTK26.js +36 -0
  46. package/dist/chunk-VN5NTK26.js.map +1 -0
  47. package/dist/{chunk-XNWCGJYH.js → chunk-VTEP2AQX.js} +15 -11
  48. package/dist/chunk-VTEP2AQX.js.map +1 -0
  49. package/dist/components/shared/ContactPicker.d.ts +92 -0
  50. package/dist/components/shared/ContactPicker.d.ts.map +1 -0
  51. package/dist/components/shared/PersonLink.d.ts +12 -1
  52. package/dist/components/shared/PersonLink.d.ts.map +1 -1
  53. package/dist/components/shared/create-person.d.ts +20 -0
  54. package/dist/components/shared/create-person.d.ts.map +1 -0
  55. package/dist/crud/CrudDetailPage.d.ts.map +1 -1
  56. package/dist/crud/CrudFormPage.d.ts.map +1 -1
  57. package/dist/crud/CrudListView.d.ts.map +1 -1
  58. package/dist/crud/CrudPage.d.ts.map +1 -1
  59. package/dist/crud/archetypes/AddressesTab.d.ts +21 -0
  60. package/dist/crud/archetypes/AddressesTab.d.ts.map +1 -0
  61. package/dist/crud/archetypes/PersonDetailTabs.d.ts +2 -1
  62. package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -1
  63. package/dist/crud/createCrudPage.d.ts.map +1 -1
  64. package/dist/crud/index.d.ts +1 -0
  65. package/dist/crud/index.d.ts.map +1 -1
  66. package/dist/index.cjs +4300 -722
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.d.ts +17 -2
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +4080 -542
  71. package/dist/index.js.map +1 -1
  72. package/dist/org/index.cjs +11 -11
  73. package/dist/org/index.js +2 -2
  74. package/dist/org/store.d.ts.map +1 -1
  75. package/dist/permissions/context.d.ts +2 -0
  76. package/dist/permissions/context.d.ts.map +1 -1
  77. package/dist/permissions/index.cjs +7 -7
  78. package/dist/permissions/index.js +1 -1
  79. package/dist/shell/components/auth/ProtectedRoute.d.ts.map +1 -1
  80. package/dist/shell/components/billing/SoftLimitBanner.d.ts +5 -0
  81. package/dist/shell/components/billing/SoftLimitBanner.d.ts.map +1 -0
  82. package/dist/shell/components/billing/SubscriptionPage.d.ts +5 -0
  83. package/dist/shell/components/billing/SubscriptionPage.d.ts.map +1 -0
  84. package/dist/shell/components/billing/UpgradeModal.d.ts +3 -0
  85. package/dist/shell/components/billing/UpgradeModal.d.ts.map +1 -0
  86. package/dist/shell/components/billing/UpgradePrompt.d.ts +10 -0
  87. package/dist/shell/components/billing/UpgradePrompt.d.ts.map +1 -0
  88. package/dist/shell/components/billing/access-contract.d.ts +18 -0
  89. package/dist/shell/components/billing/access-contract.d.ts.map +1 -0
  90. package/dist/shell/components/billing/gates.d.ts +14 -0
  91. package/dist/shell/components/billing/gates.d.ts.map +1 -0
  92. package/dist/shell/components/billing/index.d.ts +9 -2
  93. package/dist/shell/components/billing/index.d.ts.map +1 -1
  94. package/dist/shell/components/chat/ChatFab.d.ts +3 -1
  95. package/dist/shell/components/chat/ChatFab.d.ts.map +1 -1
  96. package/dist/shell/components/chat/ChatPanel.d.ts +3 -1
  97. package/dist/shell/components/chat/ChatPanel.d.ts.map +1 -1
  98. package/dist/shell/components/chat/ConfirmActionCard.d.ts +13 -0
  99. package/dist/shell/components/chat/ConfirmActionCard.d.ts.map +1 -0
  100. package/dist/shell/components/chat/markdown.d.ts +5 -0
  101. package/dist/shell/components/chat/markdown.d.ts.map +1 -0
  102. package/dist/shell/components/organization/TeamTab.d.ts.map +1 -1
  103. package/dist/shell/components/organization/TenantOnboarding.d.ts.map +1 -1
  104. package/dist/shell/components/organization/index.d.ts +0 -1
  105. package/dist/shell/components/organization/index.d.ts.map +1 -1
  106. package/dist/shell/components/settings/ConnectedCompanySettings.d.ts.map +1 -1
  107. package/dist/shell/components/settings/ConnectedFieldRulesSettings.d.ts.map +1 -1
  108. package/dist/shell/components/settings/ConnectedSecuritySettings.d.ts.map +1 -1
  109. package/dist/shell/components/settings/LocationsCrudPage.d.ts +1 -1
  110. package/dist/shell/components/settings/LocationsCrudPage.d.ts.map +1 -1
  111. package/dist/shell/components/settings/SettingsPage.d.ts +23 -2
  112. package/dist/shell/components/settings/SettingsPage.d.ts.map +1 -1
  113. package/dist/shell/hooks/index.d.ts +3 -2
  114. package/dist/shell/hooks/index.d.ts.map +1 -1
  115. package/dist/shell/hooks/useAITools.d.ts +2 -0
  116. package/dist/shell/hooks/useAITools.d.ts.map +1 -1
  117. package/dist/shell/hooks/useChat.d.ts +22 -0
  118. package/dist/shell/hooks/useChat.d.ts.map +1 -1
  119. package/dist/shell/hooks/useSystemPermission.d.ts +3 -0
  120. package/dist/shell/hooks/useSystemPermission.d.ts.map +1 -0
  121. package/dist/shell/hooks/useTenantPluginSettings.d.ts +12 -0
  122. package/dist/shell/hooks/useTenantPluginSettings.d.ts.map +1 -0
  123. package/dist/shell/lib/ai-tool-handlers.d.ts +119 -0
  124. package/dist/shell/lib/ai-tool-handlers.d.ts.map +1 -0
  125. package/dist/shell/lib/core-ai-tools.d.ts +14 -0
  126. package/dist/shell/lib/core-ai-tools.d.ts.map +1 -1
  127. package/dist/shell/lib/create-client-orders-provider.d.ts.map +1 -1
  128. package/dist/shell/lib/fayz-agent.d.ts +23 -0
  129. package/dist/shell/lib/fayz-agent.d.ts.map +1 -0
  130. package/dist/shell/lib/i18n.d.ts.map +1 -1
  131. package/dist/shell/stores/chat.store.d.ts +69 -0
  132. package/dist/shell/stores/chat.store.d.ts.map +1 -1
  133. package/dist/shell/types/billing.d.ts +7 -0
  134. package/dist/shell/types/billing.d.ts.map +1 -1
  135. package/dist/shell/types/crud.d.ts +1 -1
  136. package/dist/shell/types/crud.d.ts.map +1 -1
  137. package/dist/shell/types/plugins.d.ts +2 -45
  138. package/dist/shell/types/plugins.d.ts.map +1 -1
  139. package/dist/stores/createCrudStore.d.ts.map +1 -1
  140. package/package.json +15 -12
  141. package/src/access/context.tsx +228 -0
  142. package/src/access/headless.ts +71 -0
  143. package/src/access/index.ts +27 -0
  144. package/src/access/limits-registry.ts +86 -0
  145. package/src/access/module-nav.test.ts +78 -0
  146. package/src/access/module-nav.ts +82 -0
  147. package/src/access/resolver.test.ts +143 -0
  148. package/src/access/resolver.ts +5 -0
  149. package/src/access/types.ts +47 -0
  150. package/src/access/upgrade-modal-store.ts +30 -0
  151. package/src/app/AdminShell.tsx +451 -68
  152. package/src/app/admin-app.tsx +46 -9
  153. package/src/app/config.ts +54 -1
  154. package/src/app/core-entities.ts +110 -0
  155. package/src/app/derive-contract.test.ts +155 -0
  156. package/src/app/derive-contract.ts +214 -0
  157. package/src/app/scaffold.tsx +4 -0
  158. package/src/archetype-lookup.ts +8 -2
  159. package/src/billing/index.ts +2 -0
  160. package/src/billing/plan-badge.ts +32 -0
  161. package/src/billing/store.ts +8 -1
  162. package/src/components/shared/ContactPicker.tsx +394 -0
  163. package/src/components/shared/PersonLink.tsx +11 -3
  164. package/src/components/shared/create-person.ts +74 -0
  165. package/src/crud/CrudDetailPage.tsx +8 -1
  166. package/src/crud/CrudFormPage.tsx +124 -6
  167. package/src/crud/CrudListView.tsx +16 -1
  168. package/src/crud/CrudPage.tsx +27 -5
  169. package/src/crud/DeleteConfirmDialog.tsx +1 -1
  170. package/src/crud/archetypes/AddressesTab.tsx +387 -0
  171. package/src/crud/archetypes/PersonDetailTabs.tsx +5 -0
  172. package/src/crud/createCrudPage.tsx +2 -0
  173. package/src/crud/index.ts +6 -0
  174. package/src/env.d.ts +7 -0
  175. package/src/index.ts +49 -1
  176. package/src/org/store.ts +9 -1
  177. package/src/permissions/context.tsx +29 -15
  178. package/src/shell/components/auth/ProtectedRoute.tsx +12 -2
  179. package/src/shell/components/billing/SoftLimitBanner.tsx +81 -0
  180. package/src/shell/components/billing/SubscriptionPage.tsx +372 -0
  181. package/src/shell/components/billing/UpgradeModal.tsx +102 -0
  182. package/src/shell/components/billing/UpgradePrompt.tsx +229 -0
  183. package/src/shell/components/billing/access-contract.ts +64 -0
  184. package/src/shell/components/billing/gates.tsx +62 -0
  185. package/src/shell/components/billing/index.ts +18 -2
  186. package/src/shell/components/chat/ChatFab.tsx +4 -2
  187. package/src/shell/components/chat/ChatPanel.tsx +174 -23
  188. package/src/shell/components/chat/ConfirmActionCard.tsx +57 -0
  189. package/src/shell/components/chat/markdown.tsx +102 -0
  190. package/src/shell/components/organization/TeamTab.tsx +21 -4
  191. package/src/shell/components/organization/TenantOnboarding.tsx +3 -1
  192. package/src/shell/components/organization/index.ts +0 -1
  193. package/src/shell/components/settings/ConnectedCompanySettings.tsx +4 -1
  194. package/src/shell/components/settings/ConnectedFieldRulesSettings.tsx +3 -1
  195. package/src/shell/components/settings/ConnectedSecuritySettings.tsx +3 -1
  196. package/src/shell/components/settings/LocationsCrudPage.ts +6 -1
  197. package/src/shell/components/settings/SettingsPage.tsx +63 -21
  198. package/src/shell/hooks/index.ts +3 -2
  199. package/src/shell/hooks/useAITools.ts +27 -23
  200. package/src/shell/hooks/useChat.ts +381 -16
  201. package/src/shell/hooks/useSystemPermission.ts +23 -0
  202. package/src/shell/hooks/useTenantPluginSettings.ts +127 -0
  203. package/src/shell/lib/ai-tool-handlers.ts +910 -0
  204. package/src/shell/lib/core-ai-tools.ts +234 -2
  205. package/src/shell/lib/create-client-orders-provider.ts +24 -2
  206. package/src/shell/lib/fayz-agent.ts +56 -0
  207. package/src/shell/lib/i18n.ts +71 -0
  208. package/src/shell/stores/chat.store.ts +118 -1
  209. package/src/shell/styles.css +30 -0
  210. package/src/shell/types/billing.ts +8 -0
  211. package/src/shell/types/crud.ts +1 -1
  212. package/src/shell/types/plugins.ts +13 -50
  213. package/src/stores/createCrudStore.ts +9 -0
  214. package/dist/chunk-3M25GRRU.js.map +0 -1
  215. package/dist/chunk-4XTQ4V5L.cjs.map +0 -1
  216. package/dist/chunk-5GYQK5IW.js +0 -21
  217. package/dist/chunk-5GYQK5IW.js.map +0 -1
  218. package/dist/chunk-IPWCHJPE.cjs.map +0 -1
  219. package/dist/chunk-KYID2MR6.cjs +0 -23
  220. package/dist/chunk-KYID2MR6.cjs.map +0 -1
  221. package/dist/chunk-XNWCGJYH.js.map +0 -1
  222. package/dist/shell/components/billing/BillingPage.d.ts +0 -7
  223. package/dist/shell/components/billing/BillingPage.d.ts.map +0 -1
  224. package/dist/shell/components/billing/PaywallGate.d.ts +0 -11
  225. package/dist/shell/components/billing/PaywallGate.d.ts.map +0 -1
  226. package/dist/shell/components/organization/PermissionGate.d.ts +0 -11
  227. package/dist/shell/components/organization/PermissionGate.d.ts.map +0 -1
  228. package/dist/shell/hooks/useBilling.d.ts +0 -14
  229. package/dist/shell/hooks/useBilling.d.ts.map +0 -1
  230. package/dist/shell/hooks/usePermission.d.ts +0 -16
  231. package/dist/shell/hooks/usePermission.d.ts.map +0 -1
  232. package/src/shell/components/billing/BillingPage.tsx +0 -56
  233. package/src/shell/components/billing/PaywallGate.tsx +0 -80
  234. package/src/shell/components/organization/PermissionGate.tsx +0 -16
  235. package/src/shell/hooks/useBilling.ts +0 -151
  236. package/src/shell/hooks/usePermission.ts +0 -101
@@ -1,7 +0,0 @@
1
- import * as React from 'react';
2
- interface BillingPageProps {
3
- className?: string;
4
- }
5
- export declare function BillingPage({ className }: BillingPageProps): React.JSX.Element;
6
- export {};
7
- //# sourceMappingURL=BillingPage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BillingPage.d.ts","sourceRoot":"","sources":["../../../../src/shell/components/billing/BillingPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,UAAU,gBAAgB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,EAAE,gBAAgB,qBA2C1D"}
@@ -1,11 +0,0 @@
1
- import * as React from 'react';
2
- interface PaywallGateProps {
3
- requiredPlan?: string;
4
- requiredFeature?: string;
5
- children: React.ReactNode;
6
- fallback?: React.ReactNode;
7
- onUpgrade?: () => void;
8
- }
9
- export declare function PaywallGate({ requiredPlan, requiredFeature, children, fallback, onUpgrade, }: PaywallGateProps): React.JSX.Element;
10
- export {};
11
- //# sourceMappingURL=PaywallGate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PaywallGate.d.ts","sourceRoot":"","sources":["../../../../src/shell/components/billing/PaywallGate.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,UAAU,gBAAgB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,WAAW,CAAC,EAC1B,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE,gBAAgB,qBA0DlB"}
@@ -1,11 +0,0 @@
1
- import * as React from 'react';
2
- import type { PermissionAction } from '../../types/permissions';
3
- interface PermissionGateProps {
4
- feature: string;
5
- action: PermissionAction;
6
- children: React.ReactNode;
7
- fallback?: React.ReactNode;
8
- }
9
- export declare function PermissionGate({ feature, action, children, fallback }: PermissionGateProps): React.JSX.Element;
10
- export {};
11
- //# sourceMappingURL=PermissionGate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PermissionGate.d.ts","sourceRoot":"","sources":["../../../../src/shell/components/organization/PermissionGate.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D,UAAU,mBAAmB;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,gBAAgB,CAAA;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,mBAAmB,qBAI1F"}
@@ -1,14 +0,0 @@
1
- import type { PlanInterval } from '../types';
2
- export declare function useBilling(): {
3
- subscription: import("../..").Subscription | null;
4
- invoices: import("../..").Invoice[];
5
- plans: import("@fayz-ai/core").Plan[];
6
- loading: boolean;
7
- fetchPlans: () => Promise<void>;
8
- fetchSubscription: () => Promise<void>;
9
- fetchInvoices: () => Promise<void>;
10
- subscribe: (planId: string, interval: PlanInterval) => Promise<void>;
11
- cancelSubscription: () => Promise<void>;
12
- resumeSubscription: () => Promise<void>;
13
- };
14
- //# sourceMappingURL=useBilling.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useBilling.d.ts","sourceRoot":"","sources":["../../../src/shell/hooks/useBilling.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,wBAAgB,UAAU;;;;;;;;wBA0DP,MAAM,YAAY,YAAY;;;EAuFhD"}
@@ -1,16 +0,0 @@
1
- import type { MemberRole } from '../types';
2
- import type { PermissionAction, SystemPermission } from '../types/permissions';
3
- declare const PERMISSIONS: readonly ["manage_billing", "manage_members", "manage_settings", "manage_content", "view_content", "view_analytics"];
4
- export type Permission = (typeof PERMISSIONS)[number];
5
- export declare function usePermission(): {
6
- currentRole: MemberRole | null;
7
- currentProfile: import("@fayz-ai/core").PermissionProfile | null;
8
- permissions: ("manage_billing" | "manage_settings" | "manage_members" | "manage_content" | "view_content" | "view_analytics")[];
9
- can: (featureId: string, action: PermissionAction) => boolean;
10
- hasSystemPermission: (perm: SystemPermission) => boolean;
11
- hasPermission: (permission: Permission | string) => boolean;
12
- requireRole: (role: MemberRole) => boolean;
13
- canAccess: (requiredPermissions: Permission[]) => boolean;
14
- };
15
- export {};
16
- //# sourceMappingURL=usePermission.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"usePermission.d.ts","sourceRoot":"","sources":["../../../src/shell/hooks/usePermission.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG9E,QAAA,MAAM,WAAW,sHAOP,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;AAkBrD,wBAAgB,aAAa;;;;qBAMb,MAAM,UAAU,gBAAgB,KAAG,OAAO;gCAU/C,gBAAgB,KAAG,OAAO;gCAcpB,UAAU,GAAG,MAAM,KAAG,OAAO;wBAYnC,UAAU,KAAG,OAAO;qCAQL,UAAU,EAAE,KAAG,OAAO;EAgB/C"}
@@ -1,56 +0,0 @@
1
- import * as React from 'react'
2
- import { PlanSelector } from './PlanSelector'
3
- import { SubscriptionCard } from './SubscriptionCard'
4
- import { useBillingStore } from '../../stores/billing.store'
5
- import { useBilling } from '../../hooks/useBilling'
6
- import { useTranslation } from '../../hooks/useTranslation'
7
- import type { PlanInterval } from '../../types'
8
-
9
- interface BillingPageProps {
10
- className?: string
11
- }
12
-
13
- export function BillingPage({ className }: BillingPageProps) {
14
- const { plans, subscription, loading } = useBillingStore()
15
- const billing = useBilling()
16
- const { t } = useTranslation()
17
- const [interval, setInterval] = React.useState<PlanInterval>('monthly')
18
-
19
- // Fetch subscription on mount
20
- React.useEffect(() => {
21
- billing.fetchSubscription()
22
- }, [])
23
-
24
- const currentPlan = plans.find((p) => p.id === subscription?.planId)
25
-
26
- return (
27
- <div className={className ?? 'mx-auto max-w-5xl space-y-8'}>
28
- <div>
29
- <h1 className="text-2xl font-bold tracking-tight">{t('billing.title')}</h1>
30
- <p className="text-sm text-muted-foreground">
31
- {t('billing.subtitle')}
32
- </p>
33
- </div>
34
-
35
- {subscription && (
36
- <SubscriptionCard
37
- subscription={subscription as any}
38
- plan={currentPlan as any}
39
- onCancel={billing.cancelSubscription}
40
- loading={loading}
41
- />
42
- )}
43
-
44
- {plans.length > 0 && (
45
- <PlanSelector
46
- plans={plans as any}
47
- currentPlanId={subscription?.planId}
48
- interval={interval}
49
- onIntervalChange={setInterval}
50
- onSelectPlan={(planId) => billing.subscribe(planId, interval)}
51
- loading={loading}
52
- />
53
- )}
54
- </div>
55
- )
56
- }
@@ -1,80 +0,0 @@
1
- import * as React from 'react'
2
- import { Lock } from 'lucide-react'
3
- import { Button } from '@fayz-ai/ui'
4
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@fayz-ai/ui'
5
- import { useBillingStore } from '../../stores/billing.store'
6
- import { useTranslation } from '../../hooks/useTranslation'
7
-
8
- interface PaywallGateProps {
9
- requiredPlan?: string
10
- requiredFeature?: string
11
- children: React.ReactNode
12
- fallback?: React.ReactNode
13
- onUpgrade?: () => void
14
- }
15
-
16
- export function PaywallGate({
17
- requiredPlan,
18
- requiredFeature,
19
- children,
20
- fallback,
21
- onUpgrade,
22
- }: PaywallGateProps) {
23
- const { subscription, plans } = useBillingStore()
24
-
25
- const hasAccess = React.useMemo(() => {
26
- if (!requiredPlan && !requiredFeature) return true
27
- if (!subscription || subscription.status === 'canceled') return false
28
-
29
- if (requiredPlan) {
30
- const currentPlan = plans.find((p) => p.id === subscription.planId)
31
- const requiredPlanData = plans.find((p) => p.id === requiredPlan)
32
- if (!currentPlan || !requiredPlanData) return false
33
-
34
- const currentIndex = plans.indexOf(currentPlan)
35
- const requiredIndex = plans.indexOf(requiredPlanData)
36
- return currentIndex >= requiredIndex
37
- }
38
-
39
- if (requiredFeature) {
40
- const currentPlan = plans.find((p) => p.id === subscription.planId)
41
- if (!currentPlan) return false
42
- return currentPlan.features.some(
43
- (f) => f.toLowerCase().includes(requiredFeature.toLowerCase())
44
- )
45
- }
46
-
47
- return true
48
- }, [subscription, plans, requiredPlan, requiredFeature])
49
-
50
- if (hasAccess) {
51
- return <>{children}</>
52
- }
53
-
54
- if (fallback) {
55
- return <>{fallback}</>
56
- }
57
-
58
- const { t } = useTranslation()
59
-
60
- return (
61
- <Card className="border-dashed">
62
- <CardHeader className="text-center">
63
- <div className="mx-auto mb-2 flex h-12 w-12 items-center justify-center rounded-full bg-muted">
64
- <Lock className="h-6 w-6 text-muted-foreground" />
65
- </div>
66
- <CardTitle className="text-lg">{t('billing.paywall.upgradeRequired')}</CardTitle>
67
- <CardDescription>
68
- {requiredPlan
69
- ? t('billing.paywall.requiresPlan', { plan: requiredPlan })
70
- : t('billing.paywall.notAvailable')}
71
- </CardDescription>
72
- </CardHeader>
73
- <CardContent className="flex justify-center">
74
- <Button onClick={onUpgrade}>
75
- {t('billing.paywall.viewPlans')}
76
- </Button>
77
- </CardContent>
78
- </Card>
79
- )
80
- }
@@ -1,16 +0,0 @@
1
- import * as React from 'react'
2
- import { usePermission } from '../../hooks/usePermission'
3
- import type { PermissionAction } from '../../types/permissions'
4
-
5
- interface PermissionGateProps {
6
- feature: string
7
- action: PermissionAction
8
- children: React.ReactNode
9
- fallback?: React.ReactNode
10
- }
11
-
12
- export function PermissionGate({ feature, action, children, fallback }: PermissionGateProps) {
13
- const { can } = usePermission()
14
- if (!can(feature, action)) return <>{fallback ?? null}</>
15
- return <>{children}</>
16
- }
@@ -1,151 +0,0 @@
1
- import { useCallback, useMemo } from 'react'
2
- import { useBillingStore } from '../stores/billing.store'
3
- import { getSupabaseClient } from '../lib/supabase'
4
- import type { PlanInterval } from '../types'
5
-
6
- export function useBilling() {
7
- const store = useBillingStore()
8
-
9
- const fetchPlans = useCallback(async () => {
10
- store.setLoading(true)
11
- try {
12
- const supabase = getSupabaseClient()
13
- const { data, error } = await supabase
14
- .from('plans')
15
- .select('*')
16
- .order('prices->monthly', { ascending: true })
17
-
18
- if (error) throw error
19
- store.setPlans(data ?? [])
20
- } catch (err) {
21
- console.error('Failed to fetch plans:', err)
22
- } finally {
23
- store.setLoading(false)
24
- }
25
- }, [store])
26
-
27
- const fetchSubscription = useCallback(async () => {
28
- store.setLoading(true)
29
- try {
30
- const supabase = getSupabaseClient()
31
- const { data, error } = await supabase
32
- .from('subscriptions')
33
- .select('*')
34
- .single()
35
-
36
- if (error && error.code !== 'PGRST116') throw error
37
- store.setSubscription(data ?? null)
38
- } catch (err) {
39
- console.error('Failed to fetch subscription:', err)
40
- } finally {
41
- store.setLoading(false)
42
- }
43
- }, [store])
44
-
45
- const fetchInvoices = useCallback(async () => {
46
- store.setLoading(true)
47
- try {
48
- const supabase = getSupabaseClient()
49
- const { data, error } = await supabase
50
- .from('invoices')
51
- .select('*')
52
- .order('created_at', { ascending: false })
53
-
54
- if (error) throw error
55
- store.setInvoices(data ?? [])
56
- } catch (err) {
57
- console.error('Failed to fetch invoices:', err)
58
- } finally {
59
- store.setLoading(false)
60
- }
61
- }, [store])
62
-
63
- const subscribe = useCallback(
64
- async (planId: string, interval: PlanInterval) => {
65
- store.setLoading(true)
66
- try {
67
- const supabase = getSupabaseClient()
68
- const { data, error } = await supabase.functions.invoke(
69
- 'create-checkout',
70
- { body: { planId, interval } }
71
- )
72
-
73
- if (error) throw error
74
- if (data?.url) {
75
- window.location.href = data.url
76
- }
77
- } catch (err) {
78
- console.error('Failed to create checkout session:', err)
79
- throw err
80
- } finally {
81
- store.setLoading(false)
82
- }
83
- },
84
- [store]
85
- )
86
-
87
- const cancelSubscription = useCallback(async () => {
88
- store.setLoading(true)
89
- try {
90
- const supabase = getSupabaseClient()
91
- const { error } = await supabase.functions.invoke(
92
- 'cancel-subscription',
93
- { body: {} }
94
- )
95
-
96
- if (error) throw error
97
- await fetchSubscription()
98
- } catch (err) {
99
- console.error('Failed to cancel subscription:', err)
100
- throw err
101
- } finally {
102
- store.setLoading(false)
103
- }
104
- }, [store, fetchSubscription])
105
-
106
- const resumeSubscription = useCallback(async () => {
107
- store.setLoading(true)
108
- try {
109
- const supabase = getSupabaseClient()
110
- const { error } = await supabase.functions.invoke(
111
- 'resume-subscription',
112
- { body: {} }
113
- )
114
-
115
- if (error) throw error
116
- await fetchSubscription()
117
- } catch (err) {
118
- console.error('Failed to resume subscription:', err)
119
- throw err
120
- } finally {
121
- store.setLoading(false)
122
- }
123
- }, [store, fetchSubscription])
124
-
125
- return useMemo(
126
- () => ({
127
- subscription: store.subscription,
128
- invoices: store.invoices,
129
- plans: store.plans,
130
- loading: store.loading,
131
- fetchPlans,
132
- fetchSubscription,
133
- fetchInvoices,
134
- subscribe,
135
- cancelSubscription,
136
- resumeSubscription,
137
- }),
138
- [
139
- store.subscription,
140
- store.invoices,
141
- store.plans,
142
- store.loading,
143
- fetchPlans,
144
- fetchSubscription,
145
- fetchInvoices,
146
- subscribe,
147
- cancelSubscription,
148
- resumeSubscription,
149
- ]
150
- )
151
- }
@@ -1,101 +0,0 @@
1
- import { useCallback, useMemo } from 'react'
2
- import { useTenantStore } from '../stores/tenant.store'
3
- import { usePermissionsStore } from '../stores/permissions.store'
4
- import type { MemberRole } from '../types'
5
- import type { PermissionAction, SystemPermission } from '../types/permissions'
6
-
7
- // Legacy role-based permissions (backward compat)
8
- const PERMISSIONS = [
9
- 'manage_billing',
10
- 'manage_members',
11
- 'manage_settings',
12
- 'manage_content',
13
- 'view_content',
14
- 'view_analytics',
15
- ] as const
16
-
17
- export type Permission = (typeof PERMISSIONS)[number]
18
-
19
- const ROLE_PERMISSIONS: Record<MemberRole, Permission[]> = {
20
- owner: [...PERMISSIONS],
21
- admin: PERMISSIONS.filter((p) => p !== 'manage_billing'),
22
- manager: ['manage_content', 'view_content', 'view_analytics'],
23
- staff: ['view_content'],
24
- viewer: ['view_content'],
25
- }
26
-
27
- const ROLE_HIERARCHY: Record<MemberRole, number> = {
28
- owner: 5,
29
- admin: 4,
30
- manager: 3,
31
- staff: 2,
32
- viewer: 1,
33
- }
34
-
35
- export function usePermission() {
36
- const currentRole = useTenantStore((s) => s.currentRole)
37
- const currentProfile = usePermissionsStore((s) => s.currentProfile)
38
-
39
- // Profile-based permission check (feature × action matrix)
40
- const can = useCallback(
41
- (featureId: string, action: PermissionAction): boolean => {
42
- if (!currentProfile) return true // no permissions configured = allow all
43
- // Owner bypasses all feature checks
44
- if (currentProfile.id === 'owner') return true
45
- return currentProfile.grants[featureId]?.includes(action) ?? false
46
- },
47
- [currentProfile],
48
- )
49
-
50
- const hasSystemPermission = useCallback(
51
- (perm: SystemPermission): boolean => {
52
- if (!currentProfile) return true
53
- return (currentProfile.systemPermissions ?? []).includes(perm)
54
- },
55
- [currentProfile],
56
- )
57
-
58
- // Legacy role-based permissions
59
- const permissions = useMemo<Permission[]>(() => {
60
- if (!currentRole) return []
61
- return ROLE_PERMISSIONS[currentRole]
62
- }, [currentRole])
63
-
64
- const hasPermission = useCallback(
65
- (permission: Permission | string): boolean => {
66
- // First try profile-based system permissions
67
- if (currentProfile) {
68
- return (currentProfile.systemPermissions ?? []).includes(permission as SystemPermission)
69
- }
70
- // Fall back to legacy role-based
71
- return permissions.includes(permission as Permission)
72
- },
73
- [currentProfile, permissions],
74
- )
75
-
76
- const requireRole = useCallback(
77
- (role: MemberRole): boolean => {
78
- if (!currentRole) return false
79
- return ROLE_HIERARCHY[currentRole] >= ROLE_HIERARCHY[role]
80
- },
81
- [currentRole],
82
- )
83
-
84
- const canAccess = useCallback(
85
- (requiredPermissions: Permission[]): boolean => {
86
- return requiredPermissions.every((p) => permissions.includes(p))
87
- },
88
- [permissions],
89
- )
90
-
91
- return {
92
- currentRole,
93
- currentProfile,
94
- permissions,
95
- can,
96
- hasSystemPermission,
97
- hasPermission,
98
- requireRole,
99
- canAccess,
100
- }
101
- }