@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.
- package/dist/access/context.d.ts +47 -0
- package/dist/access/context.d.ts.map +1 -0
- package/dist/access/headless.d.ts +21 -0
- package/dist/access/headless.d.ts.map +1 -0
- package/dist/access/index.d.ts +12 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/limits-registry.d.ts +33 -0
- package/dist/access/limits-registry.d.ts.map +1 -0
- package/dist/access/module-nav.d.ts +15 -0
- package/dist/access/module-nav.d.ts.map +1 -0
- package/dist/access/resolver.d.ts +2 -0
- package/dist/access/resolver.d.ts.map +1 -0
- package/dist/access/types.d.ts +43 -0
- package/dist/access/types.d.ts.map +1 -0
- package/dist/access/upgrade-modal-store.d.ts +23 -0
- package/dist/access/upgrade-modal-store.d.ts.map +1 -0
- package/dist/app/AdminShell.d.ts +3 -0
- package/dist/app/AdminShell.d.ts.map +1 -1
- package/dist/app/admin-app.d.ts +3 -1
- package/dist/app/admin-app.d.ts.map +1 -1
- package/dist/app/config.d.ts +47 -2
- package/dist/app/config.d.ts.map +1 -1
- package/dist/app/core-entities.d.ts +25 -0
- package/dist/app/core-entities.d.ts.map +1 -0
- package/dist/app/derive-contract.d.ts +8 -0
- package/dist/app/derive-contract.d.ts.map +1 -0
- package/dist/app/scaffold.d.ts.map +1 -1
- package/dist/archetype-lookup.d.ts.map +1 -1
- package/dist/billing/index.cjs +6 -2
- package/dist/billing/index.d.ts +2 -0
- package/dist/billing/index.d.ts.map +1 -1
- package/dist/billing/index.js +1 -1
- package/dist/billing/plan-badge.d.ts +17 -0
- package/dist/billing/plan-badge.d.ts.map +1 -0
- package/dist/billing/store.d.ts +6 -1
- package/dist/billing/store.d.ts.map +1 -1
- package/dist/{chunk-3M25GRRU.js → chunk-I3DY47N5.js} +3 -4
- package/dist/chunk-I3DY47N5.js.map +1 -0
- package/dist/chunk-NNFGMUF2.cjs +39 -0
- package/dist/chunk-NNFGMUF2.cjs.map +1 -0
- package/dist/{chunk-4XTQ4V5L.cjs → chunk-SAZLW54Y.cjs} +16 -12
- package/dist/chunk-SAZLW54Y.cjs.map +1 -0
- package/dist/{chunk-IPWCHJPE.cjs → chunk-VKYZACG5.cjs} +5 -6
- package/dist/chunk-VKYZACG5.cjs.map +1 -0
- package/dist/chunk-VN5NTK26.js +36 -0
- package/dist/chunk-VN5NTK26.js.map +1 -0
- package/dist/{chunk-XNWCGJYH.js → chunk-VTEP2AQX.js} +15 -11
- package/dist/chunk-VTEP2AQX.js.map +1 -0
- package/dist/components/shared/ContactPicker.d.ts +92 -0
- package/dist/components/shared/ContactPicker.d.ts.map +1 -0
- package/dist/components/shared/PersonLink.d.ts +12 -1
- package/dist/components/shared/PersonLink.d.ts.map +1 -1
- package/dist/components/shared/create-person.d.ts +20 -0
- package/dist/components/shared/create-person.d.ts.map +1 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -1
- package/dist/crud/CrudFormPage.d.ts.map +1 -1
- package/dist/crud/CrudListView.d.ts.map +1 -1
- package/dist/crud/CrudPage.d.ts.map +1 -1
- package/dist/crud/archetypes/AddressesTab.d.ts +21 -0
- package/dist/crud/archetypes/AddressesTab.d.ts.map +1 -0
- package/dist/crud/archetypes/PersonDetailTabs.d.ts +2 -1
- package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -1
- package/dist/crud/createCrudPage.d.ts.map +1 -1
- package/dist/crud/index.d.ts +1 -0
- package/dist/crud/index.d.ts.map +1 -1
- package/dist/index.cjs +4300 -722
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4080 -542
- package/dist/index.js.map +1 -1
- package/dist/org/index.cjs +11 -11
- package/dist/org/index.js +2 -2
- package/dist/org/store.d.ts.map +1 -1
- package/dist/permissions/context.d.ts +2 -0
- package/dist/permissions/context.d.ts.map +1 -1
- package/dist/permissions/index.cjs +7 -7
- package/dist/permissions/index.js +1 -1
- package/dist/shell/components/auth/ProtectedRoute.d.ts.map +1 -1
- package/dist/shell/components/billing/SoftLimitBanner.d.ts +5 -0
- package/dist/shell/components/billing/SoftLimitBanner.d.ts.map +1 -0
- package/dist/shell/components/billing/SubscriptionPage.d.ts +5 -0
- package/dist/shell/components/billing/SubscriptionPage.d.ts.map +1 -0
- package/dist/shell/components/billing/UpgradeModal.d.ts +3 -0
- package/dist/shell/components/billing/UpgradeModal.d.ts.map +1 -0
- package/dist/shell/components/billing/UpgradePrompt.d.ts +10 -0
- package/dist/shell/components/billing/UpgradePrompt.d.ts.map +1 -0
- package/dist/shell/components/billing/access-contract.d.ts +18 -0
- package/dist/shell/components/billing/access-contract.d.ts.map +1 -0
- package/dist/shell/components/billing/gates.d.ts +14 -0
- package/dist/shell/components/billing/gates.d.ts.map +1 -0
- package/dist/shell/components/billing/index.d.ts +9 -2
- package/dist/shell/components/billing/index.d.ts.map +1 -1
- package/dist/shell/components/chat/ChatFab.d.ts +3 -1
- package/dist/shell/components/chat/ChatFab.d.ts.map +1 -1
- package/dist/shell/components/chat/ChatPanel.d.ts +3 -1
- package/dist/shell/components/chat/ChatPanel.d.ts.map +1 -1
- package/dist/shell/components/chat/ConfirmActionCard.d.ts +13 -0
- package/dist/shell/components/chat/ConfirmActionCard.d.ts.map +1 -0
- package/dist/shell/components/chat/markdown.d.ts +5 -0
- package/dist/shell/components/chat/markdown.d.ts.map +1 -0
- package/dist/shell/components/organization/TeamTab.d.ts.map +1 -1
- package/dist/shell/components/organization/TenantOnboarding.d.ts.map +1 -1
- package/dist/shell/components/organization/index.d.ts +0 -1
- package/dist/shell/components/organization/index.d.ts.map +1 -1
- package/dist/shell/components/settings/ConnectedCompanySettings.d.ts.map +1 -1
- package/dist/shell/components/settings/ConnectedFieldRulesSettings.d.ts.map +1 -1
- package/dist/shell/components/settings/ConnectedSecuritySettings.d.ts.map +1 -1
- package/dist/shell/components/settings/LocationsCrudPage.d.ts +1 -1
- package/dist/shell/components/settings/LocationsCrudPage.d.ts.map +1 -1
- package/dist/shell/components/settings/SettingsPage.d.ts +23 -2
- package/dist/shell/components/settings/SettingsPage.d.ts.map +1 -1
- package/dist/shell/hooks/index.d.ts +3 -2
- package/dist/shell/hooks/index.d.ts.map +1 -1
- package/dist/shell/hooks/useAITools.d.ts +2 -0
- package/dist/shell/hooks/useAITools.d.ts.map +1 -1
- package/dist/shell/hooks/useChat.d.ts +22 -0
- package/dist/shell/hooks/useChat.d.ts.map +1 -1
- package/dist/shell/hooks/useSystemPermission.d.ts +3 -0
- package/dist/shell/hooks/useSystemPermission.d.ts.map +1 -0
- package/dist/shell/hooks/useTenantPluginSettings.d.ts +12 -0
- package/dist/shell/hooks/useTenantPluginSettings.d.ts.map +1 -0
- package/dist/shell/lib/ai-tool-handlers.d.ts +119 -0
- package/dist/shell/lib/ai-tool-handlers.d.ts.map +1 -0
- package/dist/shell/lib/core-ai-tools.d.ts +14 -0
- package/dist/shell/lib/core-ai-tools.d.ts.map +1 -1
- package/dist/shell/lib/create-client-orders-provider.d.ts.map +1 -1
- package/dist/shell/lib/fayz-agent.d.ts +23 -0
- package/dist/shell/lib/fayz-agent.d.ts.map +1 -0
- package/dist/shell/lib/i18n.d.ts.map +1 -1
- package/dist/shell/stores/chat.store.d.ts +69 -0
- package/dist/shell/stores/chat.store.d.ts.map +1 -1
- package/dist/shell/types/billing.d.ts +7 -0
- package/dist/shell/types/billing.d.ts.map +1 -1
- package/dist/shell/types/crud.d.ts +1 -1
- package/dist/shell/types/crud.d.ts.map +1 -1
- package/dist/shell/types/plugins.d.ts +2 -45
- package/dist/shell/types/plugins.d.ts.map +1 -1
- package/dist/stores/createCrudStore.d.ts.map +1 -1
- package/package.json +15 -12
- package/src/access/context.tsx +228 -0
- package/src/access/headless.ts +71 -0
- package/src/access/index.ts +27 -0
- package/src/access/limits-registry.ts +86 -0
- package/src/access/module-nav.test.ts +78 -0
- package/src/access/module-nav.ts +82 -0
- package/src/access/resolver.test.ts +143 -0
- package/src/access/resolver.ts +5 -0
- package/src/access/types.ts +47 -0
- package/src/access/upgrade-modal-store.ts +30 -0
- package/src/app/AdminShell.tsx +451 -68
- package/src/app/admin-app.tsx +46 -9
- package/src/app/config.ts +54 -1
- package/src/app/core-entities.ts +110 -0
- package/src/app/derive-contract.test.ts +155 -0
- package/src/app/derive-contract.ts +214 -0
- package/src/app/scaffold.tsx +4 -0
- package/src/archetype-lookup.ts +8 -2
- package/src/billing/index.ts +2 -0
- package/src/billing/plan-badge.ts +32 -0
- package/src/billing/store.ts +8 -1
- package/src/components/shared/ContactPicker.tsx +394 -0
- package/src/components/shared/PersonLink.tsx +11 -3
- package/src/components/shared/create-person.ts +74 -0
- package/src/crud/CrudDetailPage.tsx +8 -1
- package/src/crud/CrudFormPage.tsx +124 -6
- package/src/crud/CrudListView.tsx +16 -1
- package/src/crud/CrudPage.tsx +27 -5
- package/src/crud/DeleteConfirmDialog.tsx +1 -1
- package/src/crud/archetypes/AddressesTab.tsx +387 -0
- package/src/crud/archetypes/PersonDetailTabs.tsx +5 -0
- package/src/crud/createCrudPage.tsx +2 -0
- package/src/crud/index.ts +6 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +49 -1
- package/src/org/store.ts +9 -1
- package/src/permissions/context.tsx +29 -15
- package/src/shell/components/auth/ProtectedRoute.tsx +12 -2
- package/src/shell/components/billing/SoftLimitBanner.tsx +81 -0
- package/src/shell/components/billing/SubscriptionPage.tsx +372 -0
- package/src/shell/components/billing/UpgradeModal.tsx +102 -0
- package/src/shell/components/billing/UpgradePrompt.tsx +229 -0
- package/src/shell/components/billing/access-contract.ts +64 -0
- package/src/shell/components/billing/gates.tsx +62 -0
- package/src/shell/components/billing/index.ts +18 -2
- package/src/shell/components/chat/ChatFab.tsx +4 -2
- package/src/shell/components/chat/ChatPanel.tsx +174 -23
- package/src/shell/components/chat/ConfirmActionCard.tsx +57 -0
- package/src/shell/components/chat/markdown.tsx +102 -0
- package/src/shell/components/organization/TeamTab.tsx +21 -4
- package/src/shell/components/organization/TenantOnboarding.tsx +3 -1
- package/src/shell/components/organization/index.ts +0 -1
- package/src/shell/components/settings/ConnectedCompanySettings.tsx +4 -1
- package/src/shell/components/settings/ConnectedFieldRulesSettings.tsx +3 -1
- package/src/shell/components/settings/ConnectedSecuritySettings.tsx +3 -1
- package/src/shell/components/settings/LocationsCrudPage.ts +6 -1
- package/src/shell/components/settings/SettingsPage.tsx +63 -21
- package/src/shell/hooks/index.ts +3 -2
- package/src/shell/hooks/useAITools.ts +27 -23
- package/src/shell/hooks/useChat.ts +381 -16
- package/src/shell/hooks/useSystemPermission.ts +23 -0
- package/src/shell/hooks/useTenantPluginSettings.ts +127 -0
- package/src/shell/lib/ai-tool-handlers.ts +910 -0
- package/src/shell/lib/core-ai-tools.ts +234 -2
- package/src/shell/lib/create-client-orders-provider.ts +24 -2
- package/src/shell/lib/fayz-agent.ts +56 -0
- package/src/shell/lib/i18n.ts +71 -0
- package/src/shell/stores/chat.store.ts +118 -1
- package/src/shell/styles.css +30 -0
- package/src/shell/types/billing.ts +8 -0
- package/src/shell/types/crud.ts +1 -1
- package/src/shell/types/plugins.ts +13 -50
- package/src/stores/createCrudStore.ts +9 -0
- package/dist/chunk-3M25GRRU.js.map +0 -1
- package/dist/chunk-4XTQ4V5L.cjs.map +0 -1
- package/dist/chunk-5GYQK5IW.js +0 -21
- package/dist/chunk-5GYQK5IW.js.map +0 -1
- package/dist/chunk-IPWCHJPE.cjs.map +0 -1
- package/dist/chunk-KYID2MR6.cjs +0 -23
- package/dist/chunk-KYID2MR6.cjs.map +0 -1
- package/dist/chunk-XNWCGJYH.js.map +0 -1
- package/dist/shell/components/billing/BillingPage.d.ts +0 -7
- package/dist/shell/components/billing/BillingPage.d.ts.map +0 -1
- package/dist/shell/components/billing/PaywallGate.d.ts +0 -11
- package/dist/shell/components/billing/PaywallGate.d.ts.map +0 -1
- package/dist/shell/components/organization/PermissionGate.d.ts +0 -11
- package/dist/shell/components/organization/PermissionGate.d.ts.map +0 -1
- package/dist/shell/hooks/useBilling.d.ts +0 -14
- package/dist/shell/hooks/useBilling.d.ts.map +0 -1
- package/dist/shell/hooks/usePermission.d.ts +0 -16
- package/dist/shell/hooks/usePermission.d.ts.map +0 -1
- package/src/shell/components/billing/BillingPage.tsx +0 -56
- package/src/shell/components/billing/PaywallGate.tsx +0 -80
- package/src/shell/components/organization/PermissionGate.tsx +0 -16
- package/src/shell/hooks/useBilling.ts +0 -151
- package/src/shell/hooks/usePermission.ts +0 -101
|
@@ -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
|
-
}
|