@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
|
@@ -7,11 +7,31 @@ import { ConnectedUserProfile } from './ConnectedUserProfile'
|
|
|
7
7
|
import { ConnectedSecuritySettings } from './ConnectedSecuritySettings'
|
|
8
8
|
import { ConnectedBrandingSettings } from './ConnectedBrandingSettings'
|
|
9
9
|
|
|
10
|
-
interface SettingsTab {
|
|
10
|
+
export interface SettingsTab {
|
|
11
11
|
id: string
|
|
12
12
|
label: string
|
|
13
13
|
icon?: React.ReactNode
|
|
14
|
+
/** Lucide icon name — used when these tabs are rendered in the app sidebar
|
|
15
|
+
* (which resolves icons by name) instead of in the in-page nav. */
|
|
16
|
+
iconName?: string
|
|
14
17
|
component: React.ReactNode
|
|
18
|
+
/** Plugin-contributed tab — draws the core/plugins divider. */
|
|
19
|
+
isPlugin?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The core (non-plugin, non-org) settings tabs. Exported so the shell can
|
|
23
|
+
* build the same list for the contextual /settings sidebar. */
|
|
24
|
+
export function useCoreSettingsTabs({
|
|
25
|
+
showCompany = true,
|
|
26
|
+
showBranding = true,
|
|
27
|
+
}: { showCompany?: boolean; showBranding?: boolean } = {}): SettingsTab[] {
|
|
28
|
+
const { t } = useTranslation()
|
|
29
|
+
return React.useMemo(() => [
|
|
30
|
+
...(showCompany ? [{ id: 'general', label: t('settings.general'), icon: <Building2 className="h-4 w-4" />, iconName: 'Building2', component: <ConnectedCompanySettings /> }] : []),
|
|
31
|
+
{ id: 'profile', label: t('settings.profile'), icon: <User className="h-4 w-4" />, iconName: 'User', component: <ConnectedUserProfile /> },
|
|
32
|
+
{ id: 'security', label: t('settings.security'), icon: <Shield className="h-4 w-4" />, iconName: 'Shield', component: <ConnectedSecuritySettings /> },
|
|
33
|
+
...(showBranding ? [{ id: 'branding', label: t('settings.branding'), icon: <Palette className="h-4 w-4" />, iconName: 'Palette', component: <ConnectedBrandingSettings /> }] : []),
|
|
34
|
+
], [t, showCompany, showBranding])
|
|
15
35
|
}
|
|
16
36
|
|
|
17
37
|
interface SettingsPageProps {
|
|
@@ -28,6 +48,16 @@ interface SettingsPageProps {
|
|
|
28
48
|
/** Show the branding ("Identidade Visual") tab. Default: true. Ignored when
|
|
29
49
|
* `tabs` is supplied explicitly. */
|
|
30
50
|
showBranding?: boolean
|
|
51
|
+
/** The shell already renders this nav (contextual /settings sidebar), so the
|
|
52
|
+
* desktop in-page nav column is dropped and the content spans the page.
|
|
53
|
+
* Mobile keeps its master/detail list — the sidebar is a drawer there. */
|
|
54
|
+
hideNav?: boolean
|
|
55
|
+
/** Controlled active tab. When provided (the shell owns the settings nav and
|
|
56
|
+
* drives it from the URL), this — not the internal hashchange state — decides
|
|
57
|
+
* which tab's content renders, so navigating between tabs from the shell
|
|
58
|
+
* sidebar reliably swaps the content. Uncontrolled (undefined) keeps the
|
|
59
|
+
* self-managed behaviour used by standalone <SettingsPage> mounts. */
|
|
60
|
+
activeTabId?: string
|
|
31
61
|
}
|
|
32
62
|
|
|
33
63
|
export function SettingsPage({
|
|
@@ -39,19 +69,21 @@ export function SettingsPage({
|
|
|
39
69
|
afterContent,
|
|
40
70
|
showCompany = true,
|
|
41
71
|
showBranding = true,
|
|
72
|
+
hideNav = false,
|
|
73
|
+
activeTabId,
|
|
42
74
|
}: SettingsPageProps) {
|
|
43
75
|
const { t } = useTranslation()
|
|
44
76
|
|
|
45
|
-
const defaultTabs
|
|
46
|
-
...(showCompany ? [{ id: 'general', label: t('settings.general'), icon: <Building2 className="h-4 w-4" />, component: <ConnectedCompanySettings /> }] : []),
|
|
47
|
-
{ id: 'profile', label: t('settings.profile'), icon: <User className="h-4 w-4" />, component: <ConnectedUserProfile /> },
|
|
48
|
-
{ id: 'security', label: t('settings.security'), icon: <Shield className="h-4 w-4" />, component: <ConnectedSecuritySettings /> },
|
|
49
|
-
...(showBranding ? [{ id: 'branding', label: t('settings.branding'), icon: <Palette className="h-4 w-4" />, component: <ConnectedBrandingSettings /> }] : []),
|
|
50
|
-
], [t, showCompany, showBranding])
|
|
77
|
+
const defaultTabs = useCoreSettingsTabs({ showCompany, showBranding })
|
|
51
78
|
|
|
52
79
|
const baseTabs = tabs ?? defaultTabs
|
|
53
80
|
const resolvedTabs = extraTabs ? [...baseTabs, ...extraTabs] : baseTabs
|
|
54
81
|
|
|
82
|
+
// Controlled mode: the shell owns the settings nav (contextual sidebar) and
|
|
83
|
+
// passes the active tab parsed from the URL. In that mode the URL — not the
|
|
84
|
+
// internal hashchange state below — is the single source of truth.
|
|
85
|
+
const controlled = activeTabId !== undefined
|
|
86
|
+
|
|
55
87
|
// Detect active tab from URL: /settings/financial/... → 'financial'
|
|
56
88
|
function getTabFromHash(): string | null {
|
|
57
89
|
const hash = typeof window !== 'undefined' ? window.location.hash.slice(1) || '/' : '/'
|
|
@@ -79,12 +111,15 @@ export function SettingsPage({
|
|
|
79
111
|
// Desktop always shows a selected tab; when none is chosen yet (landing on
|
|
80
112
|
// /settings), default to the first tab — 'general'. Mobile keeps its
|
|
81
113
|
// list-first master/detail behaviour (driven by the raw activeTab below).
|
|
82
|
-
|
|
114
|
+
// Controlled mode uses the shell-supplied tab instead of internal state.
|
|
115
|
+
const effectiveTab = controlled ? activeTabId ?? null : activeTab
|
|
116
|
+
const desktopTab = effectiveTab ?? resolvedTabs[0]?.id ?? null
|
|
83
117
|
const activeContent = desktopTab ? resolvedTabs.find((t) => t.id === desktopTab)?.component : null
|
|
84
|
-
const
|
|
118
|
+
const desktopLabel = desktopTab ? resolvedTabs.find((t) => t.id === desktopTab)?.label : null
|
|
85
119
|
|
|
86
|
-
// On mobile: show list OR content. On desktop: show both.
|
|
87
|
-
|
|
120
|
+
// On mobile: show list OR content. On desktop: show both. In controlled mode
|
|
121
|
+
// the shell drawer holds the nav list, so SettingsPage always shows content.
|
|
122
|
+
const showingContent = controlled ? true : activeTab !== null
|
|
88
123
|
|
|
89
124
|
const handleSelectTab = (tabId: string) => {
|
|
90
125
|
setActiveTab(tabId)
|
|
@@ -100,7 +135,7 @@ export function SettingsPage({
|
|
|
100
135
|
<ul className="space-y-0.5">
|
|
101
136
|
{resolvedTabs.map((tab, i) => (
|
|
102
137
|
<React.Fragment key={tab.id}>
|
|
103
|
-
{
|
|
138
|
+
{tab.isPlugin && (i === 0 || !resolvedTabs[i - 1]?.isPlugin) && (
|
|
104
139
|
<li className="py-2">
|
|
105
140
|
<div className="border-t" />
|
|
106
141
|
<p className="px-3 pt-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">{t('common.plugins')}</p>
|
|
@@ -129,8 +164,9 @@ export function SettingsPage({
|
|
|
129
164
|
|
|
130
165
|
return (
|
|
131
166
|
<div className={cn('space-y-6', className)}>
|
|
132
|
-
{/* Mobile: back button + tab title when viewing content
|
|
133
|
-
|
|
167
|
+
{/* Mobile: back button + tab title when viewing content. Skipped in
|
|
168
|
+
controlled mode — the shell drawer owns the settings list + back. */}
|
|
169
|
+
{!controlled && showingContent && (
|
|
134
170
|
<button
|
|
135
171
|
onClick={handleBack}
|
|
136
172
|
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors md:hidden"
|
|
@@ -140,19 +176,25 @@ export function SettingsPage({
|
|
|
140
176
|
</button>
|
|
141
177
|
)}
|
|
142
178
|
|
|
143
|
-
{/* Header — hidden on mobile when viewing content
|
|
179
|
+
{/* Header — hidden on mobile when viewing content. With the nav in the
|
|
180
|
+
shell sidebar the page title is the selected section, not "Settings"
|
|
181
|
+
(the shell top bar already says that). */}
|
|
144
182
|
<div className={cn(showingContent && 'hidden md:block')}>
|
|
145
|
-
<h1 className="text-2xl font-bold tracking-tight">
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
183
|
+
<h1 className="text-2xl font-bold tracking-tight">
|
|
184
|
+
{hideNav ? desktopLabel ?? t('settings.title') : t('settings.title')}
|
|
185
|
+
</h1>
|
|
186
|
+
{!hideNav && (
|
|
187
|
+
<p className="text-sm text-muted-foreground">
|
|
188
|
+
{t('settings.subtitle')}
|
|
189
|
+
</p>
|
|
190
|
+
)}
|
|
149
191
|
</div>
|
|
150
192
|
|
|
151
193
|
{beforeContent}
|
|
152
194
|
|
|
153
|
-
{/* Desktop: side-by-side layout */}
|
|
195
|
+
{/* Desktop: side-by-side layout, or content-only when the shell owns the nav */}
|
|
154
196
|
<div className="hidden md:flex md:gap-6">
|
|
155
|
-
<nav className="w-52 shrink-0">{renderNav(desktopTab)}</nav>
|
|
197
|
+
{!hideNav && <nav className="w-52 shrink-0">{renderNav(desktopTab)}</nav>}
|
|
156
198
|
<div className="min-w-0 flex-1">{activeContent ?? resolvedTabs[0]?.component}</div>
|
|
157
199
|
</div>
|
|
158
200
|
|
package/src/shell/hooks/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export { useAuth } from './useAuth'
|
|
2
2
|
export { useTenant } from './useTenant'
|
|
3
|
-
export {
|
|
3
|
+
export { useSystemPermission } from './useSystemPermission'
|
|
4
4
|
export { useLayout } from './useLayout'
|
|
5
|
-
export { useBilling } from './useBilling'
|
|
6
5
|
export { useNotifications } from './useNotifications'
|
|
7
6
|
export { useTranslation } from './useTranslation'
|
|
8
7
|
export { useFormatters } from './useFormatters'
|
|
@@ -15,4 +14,6 @@ export { useModuleNavigation } from './useModuleNavigation'
|
|
|
15
14
|
export type { NavDirection, DepthMap } from './useModuleNavigation'
|
|
16
15
|
export { usePluginPrefs, getPluginPref, setPluginPref } from './usePluginPrefs'
|
|
17
16
|
export type { PluginPrefs } from './usePluginPrefs'
|
|
17
|
+
export { useTenantPluginSettings } from './useTenantPluginSettings'
|
|
18
|
+
export type { TenantPluginSettings } from './useTenantPluginSettings'
|
|
18
19
|
export { useFieldRules } from './useFieldRules'
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { usePluginRuntimeOptional } from '../lib/plugins'
|
|
3
|
-
import {
|
|
3
|
+
import { useAccessOptional } from '../../access/context'
|
|
4
|
+
import { getAllEntities } from '@fayz-ai/core'
|
|
5
|
+
import { coreAITools, buildDataPrimitiveTools, formatToolSignature } from '../lib/core-ai-tools'
|
|
4
6
|
import type { PluginAITool, AIToolSuggestion } from '../types/plugins'
|
|
5
7
|
|
|
6
8
|
export interface ResolvedSuggestion extends AIToolSuggestion {
|
|
@@ -49,40 +51,42 @@ function detectActivePluginId(runtime: ReturnType<typeof usePluginRuntimeOptiona
|
|
|
49
51
|
|
|
50
52
|
export function useAITools(): {
|
|
51
53
|
tools: PluginAITool[]
|
|
54
|
+
/** Plugin owning the current route, or null on non-plugin pages. */
|
|
55
|
+
activePluginId: string | null
|
|
52
56
|
suggestions: ResolvedSuggestion[]
|
|
53
57
|
/** Suggestions from the currently active plugin only (empty on non-plugin pages) */
|
|
54
58
|
contextualSuggestions: ResolvedSuggestion[]
|
|
55
59
|
toolGroups: ResolvedToolGroup[]
|
|
56
60
|
} {
|
|
57
61
|
const runtime = usePluginRuntimeOptional()
|
|
62
|
+
const access = useAccessOptional()
|
|
58
63
|
|
|
59
64
|
return React.useMemo(() => {
|
|
60
65
|
const hasPermission = runtime?.context.hasPermission
|
|
66
|
+
const can = access.can
|
|
61
67
|
const verticalId = runtime?.context.tenant?.verticalId
|
|
62
68
|
const activePluginId = detectActivePluginId(runtime)
|
|
63
69
|
|
|
64
|
-
// Collect all tools: core +
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Filter by permission
|
|
70
|
+
// Collect all tools: core + the TWO data primitives (searchRecords/
|
|
71
|
+
// queryData over every CRUD entity and plugin registry — the target is a
|
|
72
|
+
// parameter, its read permission checked per call) + plugin-declared.
|
|
73
|
+
const allTools: PluginAITool[] = [
|
|
74
|
+
...coreAITools,
|
|
75
|
+
...buildDataPrimitiveTools({
|
|
76
|
+
entities: getAllEntities(),
|
|
77
|
+
registries: runtime?.registries ?? new Map(),
|
|
78
|
+
queryEntities: runtime?.activePlugins.flatMap((p) => p.queryEntities ?? []) ?? [],
|
|
79
|
+
}),
|
|
80
|
+
]
|
|
81
|
+
if (runtime) allTools.push(...runtime.aiTools)
|
|
82
|
+
|
|
83
|
+
// Filter by role × plan. UX only — the REAL authorization runs server-side
|
|
84
|
+
// (broker catalog filter) and in the guarded executor (checkAccess/
|
|
85
|
+
// guardLimit); hiding here just keeps the visible catalog honest. Falls
|
|
86
|
+
// back to the runtime's role-only check when no AccessProvider is mounted.
|
|
84
87
|
const tools = allTools.filter((tool) => {
|
|
85
88
|
if (!tool.permission) return true
|
|
89
|
+
if (can) return can(tool.permission.feature, tool.permission.action).allowed
|
|
86
90
|
if (!hasPermission) return true
|
|
87
91
|
return hasPermission(tool.permission)
|
|
88
92
|
})
|
|
@@ -134,6 +138,6 @@ export function useAITools(): {
|
|
|
134
138
|
toolGroups.push({ category, tools: groupTools })
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
return { tools, suggestions, contextualSuggestions, toolGroups }
|
|
138
|
-
}, [runtime])
|
|
141
|
+
return { tools, activePluginId, suggestions, contextualSuggestions, toolGroups }
|
|
142
|
+
}, [runtime, access.can])
|
|
139
143
|
}
|