@fayz-ai/saas 0.1.6 → 0.2.0

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/dist/crud/CrudDetailPage.d.ts.map +1 -1
  4. package/dist/crud/CrudFormPage.d.ts.map +1 -1
  5. package/dist/crud/archetypes/PersonDetailTabs.d.ts +12 -0
  6. package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -1
  7. package/dist/crud/fieldToColumn.d.ts.map +1 -1
  8. package/dist/crud/index.d.ts +1 -0
  9. package/dist/crud/index.d.ts.map +1 -1
  10. package/dist/crud/relation-field.d.ts +28 -0
  11. package/dist/crud/relation-field.d.ts.map +1 -0
  12. package/dist/hooks/useModuleNavigation.d.ts +2 -0
  13. package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
  14. package/dist/index.cjs +726 -379
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +550 -204
  19. package/dist/index.js.map +1 -1
  20. package/dist/shell/components/plugins/ConnectorsHub.d.ts +6 -0
  21. package/dist/shell/components/plugins/ConnectorsHub.d.ts.map +1 -0
  22. package/dist/shell/components/plugins/PluginSettingsPanel.d.ts +4 -2
  23. package/dist/shell/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
  24. package/dist/shell/createSaasApp.d.ts.map +1 -1
  25. package/dist/shell/hooks/useModuleNavigation.d.ts +2 -0
  26. package/dist/shell/hooks/useModuleNavigation.d.ts.map +1 -1
  27. package/dist/shell/types/crud.d.ts +12 -1
  28. package/dist/shell/types/crud.d.ts.map +1 -1
  29. package/package.json +4 -4
  30. package/src/crud/CrudDetailPage.tsx +5 -0
  31. package/src/crud/CrudFormPage.tsx +12 -0
  32. package/src/crud/archetypes/PersonDetailTabs.tsx +22 -0
  33. package/src/crud/fieldToColumn.tsx +3 -0
  34. package/src/crud/index.ts +1 -0
  35. package/src/crud/relation-field.tsx +115 -0
  36. package/src/hooks/useModuleNavigation.ts +27 -1
  37. package/src/index.ts +1 -0
  38. package/src/shell/components/plugins/ConnectorsHub.tsx +163 -0
  39. package/src/shell/components/plugins/PluginSettingsPanel.tsx +106 -27
  40. package/src/shell/createSaasApp.tsx +22 -1
  41. package/src/shell/hooks/useModuleNavigation.ts +27 -1
  42. package/src/shell/types/crud.ts +13 -0
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { ConnectorDefinition } from '@fayz-ai/core';
3
+ export declare function ConnectorsHub({ connectors }: {
4
+ connectors: ConnectorDefinition[];
5
+ }): React.JSX.Element;
6
+ //# sourceMappingURL=ConnectorsHub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectorsHub.d.ts","sourceRoot":"","sources":["../../../../src/shell/components/plugins/ConnectorsHub.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAG/D,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,eAAe,CAAA;AAqJzE,wBAAgB,aAAa,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,mBAAmB,EAAE,CAAA;CAAE,qBAUlF"}
@@ -6,17 +6,19 @@ export interface SettingsPanelTab {
6
6
  icon?: string;
7
7
  content: React.ReactNode;
8
8
  }
9
- export declare function PluginSettingsPanel({ title, subtitle, generalSettings, registries, customTabs, routeBase, onClose }: {
9
+ export declare function PluginSettingsPanel({ title, subtitle, generalSettings, registries, customTabs, routeBase, hostPluginId, onClose }: {
10
10
  title: string;
11
11
  subtitle?: string;
12
12
  /** General preferences component */
13
13
  generalSettings?: React.ReactNode;
14
- /** CRUD registries — each becomes its own tab */
14
+ /** CRUD registries — grouped under a "Properties" tab */
15
15
  registries?: PluginRegistryDef[];
16
16
  /** Additional custom tabs (e.g., Pipeline config for CRM) */
17
17
  customTabs?: SettingsPanelTab[];
18
18
  /** Hash route base for CRUD sub-routing */
19
19
  routeBase: string;
20
+ /** This plugin's id — used to surface connectors addons registered for it in an Integrations tab. */
21
+ hostPluginId?: string;
20
22
  /** Called when back button is clicked. If not provided, back button is hidden. */
21
23
  onClose?: () => void;
22
24
  }): React.JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"PluginSettingsPanel.d.ts","sourceRoot":"","sources":["../../../../src/shell/components/plugins/PluginSettingsPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAA;AAKhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAU5D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,KAAK,CAAC,SAAS,CAAA;CACzB;AA4BD,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;IACpH,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACjC,iDAAiD;IACjD,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAChC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC/B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB,qBA+HA"}
1
+ {"version":3,"file":"PluginSettingsPanel.d.ts","sourceRoot":"","sources":["../../../../src/shell/components/plugins/PluginSettingsPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAA;AAKhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAW5D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,KAAK,CAAC,SAAS,CAAA;CACzB;AAmGD,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE;IAClI,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACjC,yDAAyD;IACzD,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAChC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC/B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,qGAAqG;IACrG,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB,qBAoIA"}
@@ -1 +1 @@
1
- {"version":3,"file":"createSaasApp.d.ts","sourceRoot":"","sources":["../../src/shell/createSaasApp.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createSaasApp.d.ts","sourceRoot":"","sources":["../../src/shell/createSaasApp.tsx"],"names":[],"mappings":"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"}
@@ -21,5 +21,7 @@ export declare function useModuleNavigation(hashBase: string, depthMap?: DepthMa
21
21
  direction: NavDirection;
22
22
  animationClass: string;
23
23
  navigate: (nextView: string, hash?: string) => void;
24
+ previousView: string | null;
25
+ back: (fallbackView?: string) => void;
24
26
  };
25
27
  //# sourceMappingURL=useModuleNavigation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModuleNavigation.d.ts","sourceRoot":"","sources":["../../../src/shell/hooks/useModuleNavigation.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAA;AAE7C;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAW7C;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,SAAY;;;;yBA8DrD,MAAM,SAAS,MAAM;EA8B9D"}
1
+ {"version":3,"file":"useModuleNavigation.d.ts","sourceRoot":"","sources":["../../../src/shell/hooks/useModuleNavigation.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAA;AAE7C;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAW7C;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,SAAY;;;;yBA6ErD,MAAM,SAAS,MAAM;;0BA8BpB,MAAM;EAWhD"}
@@ -1,6 +1,15 @@
1
1
  import type React from 'react';
2
2
  import type { EntityArchetype } from './entities';
3
- export type FieldType = 'text' | 'email' | 'phone' | 'url' | 'image' | 'number' | 'currency' | 'select' | 'multiselect' | 'segmented' | 'date' | 'datetime' | 'time' | 'boolean' | 'textarea' | 'color' | 'computed';
3
+ export type FieldType = 'text' | 'email' | 'phone' | 'url' | 'image' | 'number' | 'currency' | 'select' | 'multiselect' | 'segmented' | 'relation' | 'date' | 'datetime' | 'time' | 'boolean' | 'textarea' | 'color' | 'computed';
4
+ /** Foreign-key source for a `relation` field (options loaded from a table). */
5
+ export interface FieldRelation {
6
+ table: string;
7
+ valueField?: string;
8
+ labelField?: string;
9
+ tenantScoped?: boolean;
10
+ schema?: string;
11
+ filter?: Record<string, unknown>;
12
+ }
4
13
  /** Read-only display produced by a `computed` field's `compute()`. */
5
14
  export interface ComputedFieldValue {
6
15
  display: string;
@@ -19,6 +28,8 @@ export interface FieldDef {
19
28
  value: string;
20
29
  description?: string;
21
30
  }[];
31
+ /** Required for `relation` fields — where to load the option list from. */
32
+ relation?: FieldRelation;
22
33
  min?: number;
23
34
  max?: number;
24
35
  currency?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../../src/shell/types/crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,MAAM,MAAM,SAAS,GACjB,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAC5C,QAAQ,GAAG,UAAU,GACrB,QAAQ,GAAG,aAAa,GAAG,WAAW,GACtC,MAAM,GAAG,UAAU,GAAG,MAAM,GAC5B,SAAS,GAAG,UAAU,GACtB,OAAO,GAAG,UAAU,CAAA;AAExB,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAA;CAC3C;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,SAAS,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,kEAAkE;IAClE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,KAAK,CAAC,SAAS,CAAA;IACtD,oFAAoF;IACpF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,kBAAkB,GAAG,IAAI,CAAA;IACpE,YAAY,CAAC,EAAE,GAAG,CAAA;IAClB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IACZ,yGAAyG;IACzG,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACnB,yEAAyE;IACzE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;2EACuE;IACvE,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;IACpC,+FAA+F;IAC/F,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;AAExG,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,gFAAgF;IAChF,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,MAAM,EAAE,QAAQ,EAAE,CAAA;IAClB,mEAAmE;IACnE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;IAC1B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,YAAY,CAAC,EAAE,OAAO,CAAA;QACtB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;QACxB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAClC,yFAAyF;QACzF,SAAS,CAAC,EAAE,eAAe,CAAA;QAC3B,6DAA6D;QAC7D,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,wEAAwE;QACxE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,sDAAsD;QACtD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAClC,oEAAoE;QACpE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;mFAC+E;IAC/E,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAChD;AAMD,oDAAoD;AACpD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,qEAAqE;AACrE,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;AAEhE,yFAAyF;AACzF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA"}
1
+ {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../../src/shell/types/crud.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fayz-ai/saas",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "description": "Fayz SDK SaaS layer — multi-tenancy, billing, permissions, org management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -47,9 +47,9 @@
47
47
  "@tanstack/react-table": "^8.20.0",
48
48
  "cmdk": "^1.0.0",
49
49
  "@radix-ui/react-dialog": "^1.1.0",
50
- "@fayz-ai/auth": "^0.1.6",
51
- "@fayz-ai/ui": "^0.1.6",
52
- "@fayz-ai/core": "^0.1.6"
50
+ "@fayz-ai/core": "^0.2.0",
51
+ "@fayz-ai/auth": "^0.2.0",
52
+ "@fayz-ai/ui": "^0.2.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/react": "^18.3.0",
@@ -15,6 +15,7 @@ import { SERVICE_DETAIL_TABS } from './archetypes/ServiceDetailTabs'
15
15
  import { LOCATION_DETAIL_TABS } from './archetypes/LocationDetailTabs'
16
16
  import { SUBJECT_DETAIL_TABS } from './archetypes/SubjectDetailTabs'
17
17
  import { useTranslation } from '@fayz-ai/core'
18
+ import { usePluginRuntimeOptional, getWidgetsForZone } from '@fayz-ai/core'
18
19
  import type { EntityDef, FieldDef, FieldGroup, FormLayout, DetailTab } from '@fayz-ai/core'
19
20
 
20
21
  interface CrudDetailPageProps {
@@ -246,8 +247,12 @@ export function CrudDetailPage({
246
247
  const initial = typeof displayValue === 'string' ? displayValue.charAt(0).toUpperCase() : '?'
247
248
 
248
249
  // Merge archetype tabs + custom entity tabs
250
+ const runtime = usePluginRuntimeOptional()
249
251
  const archetypeTabs = getArchetypeTabs(entityDef.layout, entityDef.data?.archetypeKind)
250
252
  const customTabs = [...archetypeTabs, ...(entityDef.detailTabs ?? [])]
253
+ // Drop tabs that require a plugin-contributed widget when none is registered
254
+ // (e.g. the financial statement tab only appears when the financial plugin is active).
255
+ .filter((tab) => !tab.requiresWidgetZone || (runtime ? getWidgetsForZone(runtime, tab.requiresWidgetZone).length > 0 : false))
251
256
  const entityId = entityDef.name.toLowerCase().replace(/\s+/g, '-')
252
257
  const widgetZone = `${entityId}.detail.tabs`
253
258
 
@@ -15,6 +15,7 @@ import { SubjectFormLayout } from './archetypes/SubjectFormLayout'
15
15
  import { useTranslation } from '@fayz-ai/core'
16
16
  import type { FieldDef, FieldGroup, EntityDef } from '@fayz-ai/core'
17
17
  import type { FormLayout } from '@fayz-ai/core'
18
+ import { RelationSelect } from './relation-field'
18
19
 
19
20
  interface CrudFormPageProps {
20
21
  entityDef: EntityDef
@@ -133,6 +134,17 @@ function renderField(field: FieldDef, value: any, onChange: (val: any) => void,
133
134
  className={`${baseClass} min-h-[60px] py-1.5`}
134
135
  />
135
136
  )
137
+ case 'relation':
138
+ return (
139
+ <RelationSelect
140
+ relation={field.relation}
141
+ value={value}
142
+ onChange={onChange}
143
+ className={baseClass}
144
+ required={field.required}
145
+ placeholder={field.placeholder ?? `Select ${field.label.toLowerCase()}...`}
146
+ />
147
+ )
136
148
  case 'select': {
137
149
  const options = (field.options ?? []).map((o) =>
138
150
  typeof o === 'string' ? { label: o, value: o } : o,
@@ -66,6 +66,25 @@ export function DocumentsTab({ item }: { item?: any }) {
66
66
  )
67
67
  }
68
68
 
69
+ export function FinancialTab({ item }: { item?: any }) {
70
+ const runtime = usePluginsOptional()
71
+ const widgets = runtime ? getWidgetsForZone(runtime, 'person.detail.financial') : []
72
+ if (widgets.length > 0 && item) {
73
+ const w = widgets[0]
74
+ const Widget = w.component as React.ComponentType<any>
75
+ return (
76
+ <Widget
77
+ item={item}
78
+ config={w.config}
79
+ runtime={runtime?.context}
80
+ plugin={w.plugin}
81
+ widget={w}
82
+ />
83
+ )
84
+ }
85
+ return null
86
+ }
87
+
69
88
  export function ScheduleTab({ item }: { item: any }) {
70
89
  const t = useTranslation()
71
90
  if (!item?.id) {
@@ -83,5 +102,8 @@ export function ScheduleTab({ item }: { item: any }) {
83
102
  export const PERSON_DETAIL_TABS = [
84
103
  { id: 'schedule', label: 'Schedule', icon: 'CalendarDays', component: ScheduleTab, visibleFor: ['staff'] },
85
104
  { id: 'documents', label: 'Documents', icon: 'FileText', component: DocumentsTab },
105
+ // Per-person financial statement — only shown when the financial plugin contributes
106
+ // its widget (see requiresWidgetZone). Clients = paid invoices; staff = commissions.
107
+ { id: 'financial', label: 'Statement', icon: 'DollarSign', component: FinancialTab, requiresWidgetZone: 'person.detail.financial' },
86
108
  { id: 'activity', label: 'Activity', icon: 'Clock', component: ActivityTab },
87
109
  ]
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import type { FieldDef } from '@fayz-ai/core'
3
3
  import { Badge } from '@fayz-ai/ui'
4
+ import { RelationCell } from './relation-field'
4
5
  import {
5
6
  formatCurrency as formatCurrencyLocale,
6
7
  formatDate as formatDateLocale,
@@ -95,6 +96,8 @@ export function fieldToColumns(fields: FieldDef[], options?: FieldToColumnsOptio
95
96
  <span className="text-xs text-muted-foreground">{String(value)}</span>
96
97
  </span>
97
98
  )
99
+ case 'relation':
100
+ return <RelationCell relation={field.relation} value={value} />
98
101
  case 'select':
99
102
  return <Badge variant="secondary">{String(value)}</Badge>
100
103
  case 'number':
package/src/crud/index.ts CHANGED
@@ -9,3 +9,4 @@ export { DeleteConfirmDialog } from './DeleteConfirmDialog'
9
9
  export { ImportWizard } from './ImportWizard'
10
10
  export type { ImportRowError } from './ImportWizard'
11
11
  export { exportToCSV } from './csv-export'
12
+ export { RelationSelect, RelationCell, useRelationOptions, loadRelationOptions, invalidateRelationOptions } from './relation-field'
@@ -0,0 +1,115 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import { Badge } from '@fayz-ai/ui'
3
+ import { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'
4
+ import type { FieldRelation } from '@fayz-ai/core'
5
+
6
+ // Relation field support for the CRUD engine: load a select's options from a
7
+ // table at runtime so a foreign-key column stores a real id (e.g. uuid) instead
8
+ // of a static string. Shared by the form input (RelationSelect) and the table
9
+ // cell (RelationCell). Options are cached per (schema, table, fields, tenant,
10
+ // filter) so the list + every cell reuse one query.
11
+
12
+ type Option = { value: string; label: string }
13
+
14
+ const cache = new Map<string, Promise<Option[]>>()
15
+
16
+ function keyOf(r: FieldRelation): string {
17
+ const tenant = r.tenantScoped !== false ? (getActiveTenantId() ?? '') : ''
18
+ return JSON.stringify([
19
+ r.schema ?? 'public', r.table, r.valueField ?? 'id', r.labelField ?? 'name',
20
+ tenant, r.filter ?? null,
21
+ ])
22
+ }
23
+
24
+ async function fetchOptions(r: FieldRelation): Promise<Option[]> {
25
+ const supabase = getSupabaseClientOptional() as any
26
+ if (!supabase) return []
27
+ const valueField = r.valueField ?? 'id'
28
+ const labelField = r.labelField ?? 'name'
29
+ let q = (r.schema ? supabase.schema(r.schema) : supabase)
30
+ .from(r.table)
31
+ .select(`${valueField}, ${labelField}`)
32
+ if (r.tenantScoped !== false) {
33
+ const tid = getActiveTenantId()
34
+ if (tid) q = q.eq('tenant_id', tid)
35
+ }
36
+ if (r.filter) for (const [k, v] of Object.entries(r.filter)) q = q.eq(k, v)
37
+ q = q.order(labelField, { ascending: true })
38
+ const { data } = await q
39
+ return (data ?? []).map((row: any) => ({
40
+ value: String(row[valueField]),
41
+ label: String(row[labelField] ?? row[valueField]),
42
+ }))
43
+ }
44
+
45
+ export function loadRelationOptions(r: FieldRelation): Promise<Option[]> {
46
+ const k = keyOf(r)
47
+ if (!cache.has(k)) {
48
+ cache.set(k, fetchOptions(r).catch(() => { cache.delete(k); return [] }))
49
+ }
50
+ return cache.get(k)!
51
+ }
52
+
53
+ /** Drop cached relation options (call after creating/editing a related row). */
54
+ export function invalidateRelationOptions(): void {
55
+ cache.clear()
56
+ }
57
+
58
+ export function useRelationOptions(r: FieldRelation | undefined): {
59
+ options: Option[]
60
+ loading: boolean
61
+ labelFor: (value: any) => string
62
+ } {
63
+ const [options, setOptions] = useState<Option[]>([])
64
+ const [loading, setLoading] = useState(true)
65
+ const dep = r ? keyOf(r) : 'none'
66
+ useEffect(() => {
67
+ let alive = true
68
+ if (!r) { setLoading(false); return }
69
+ setLoading(true)
70
+ loadRelationOptions(r).then((opts) => {
71
+ if (alive) { setOptions(opts); setLoading(false) }
72
+ })
73
+ return () => { alive = false }
74
+ }, [dep]) // eslint-disable-line react-hooks/exhaustive-deps
75
+ const labelFor = (value: any) => options.find((o) => o.value === String(value))?.label ?? ''
76
+ return { options, loading, labelFor }
77
+ }
78
+
79
+ export function RelationSelect({
80
+ relation,
81
+ value,
82
+ onChange,
83
+ className,
84
+ required,
85
+ placeholder,
86
+ }: {
87
+ relation?: FieldRelation
88
+ value: any
89
+ onChange: (value: any) => void
90
+ className?: string
91
+ required?: boolean
92
+ placeholder?: string
93
+ }) {
94
+ const { options, loading } = useRelationOptions(relation)
95
+ return (
96
+ <select
97
+ value={value ?? ''}
98
+ onChange={(e) => onChange(e.target.value || null)}
99
+ required={required}
100
+ className={className}
101
+ >
102
+ <option value="">{loading ? 'Loading…' : (placeholder ?? 'Select...')}</option>
103
+ {options.map((o) => (
104
+ <option key={o.value} value={o.value}>{o.label}</option>
105
+ ))}
106
+ </select>
107
+ )
108
+ }
109
+
110
+ export function RelationCell({ relation, value }: { relation?: FieldRelation; value: any }) {
111
+ const { labelFor, loading } = useRelationOptions(relation)
112
+ if (value == null || value === '') return <span className="text-muted-foreground">—</span>
113
+ const label = labelFor(value)
114
+ return <Badge variant="secondary">{label || (loading ? '…' : String(value))}</Badge>
115
+ }
@@ -60,6 +60,21 @@ export function useModuleNavigation(hashBase: string, depthMap?: DepthMap, homeV
60
60
  const [direction, setDirection] = useState<NavDirection>('forward')
61
61
  const prevDepthRef = useRef(getDepth(getInitialView()))
62
62
 
63
+ // Track recent in-module views so detail/subpages can render a context-aware
64
+ // back link ("Back to {where you came from}") and return to the actual
65
+ // previous page rather than a hard-coded parent route. Records every distinct
66
+ // view (regardless of how it was reached: navigate(), topbar, browser back).
67
+ const historyRef = useRef<string[]>([getInitialView()])
68
+ const [previousView, setPreviousView] = useState<string | null>(null)
69
+ useEffect(() => {
70
+ const stack = historyRef.current
71
+ if (stack[stack.length - 1] !== view) {
72
+ stack.push(view)
73
+ if (stack.length > 30) stack.shift()
74
+ }
75
+ setPreviousView(stack.length >= 2 ? stack[stack.length - 2] : null)
76
+ }, [view])
77
+
63
78
  // Sync view from external hash changes (e.g., topbar nav clicking the module link)
64
79
  useEffect(() => {
65
80
  function handler() {
@@ -118,7 +133,18 @@ export function useModuleNavigation(hashBase: string, depthMap?: DepthMap, homeV
118
133
  }
119
134
  }, [hashBase])
120
135
 
136
+ // Return to the actual previous page. Uses the browser history when there's
137
+ // in-app history to pop (preserving the prior page); otherwise navigates to a
138
+ // sensible fallback (deep links / direct loads have nowhere to go back to).
139
+ const back = useCallback((fallbackView?: string) => {
140
+ if (historyRef.current.length >= 2) {
141
+ window.history.back()
142
+ } else {
143
+ navigate(fallbackView ?? homeView)
144
+ }
145
+ }, [navigate, homeView])
146
+
121
147
  const animationClass = direction === 'forward' ? 'saas-nav-forward' : 'saas-nav-back'
122
148
 
123
- return { view, direction, animationClass, navigate }
149
+ return { view, direction, animationClass, navigate, previousView, back }
124
150
  }
package/src/index.ts CHANGED
@@ -159,6 +159,7 @@ export { setScheduleBlockConfig, getScheduleBlockConfig, subscribeScheduleBlockC
159
159
 
160
160
  // Shell plugin-settings panel + dedup util (used by vertical plugins)
161
161
  export { PluginSettingsPanel } from './shell/components/plugins/PluginSettingsPanel'
162
+ export { ConnectorsHub } from './shell/components/plugins/ConnectorsHub'
162
163
  export { dedup } from './shell/lib/dedup'
163
164
 
164
165
  // Full admin theme type (the shell's theme system, richer than @fayz-ai/core's).
@@ -0,0 +1,163 @@
1
+ import React, { useCallback, useEffect, useState } from 'react'
2
+ import { Plug, CheckCircle2, Loader2, ChevronDown, Power } from 'lucide-react'
3
+ import { Button, ICON_MAP } from '@fayz-ai/ui'
4
+ import type { ConnectorDefinition, ConnectorStatus } from '@fayz-ai/core'
5
+ import { useTranslation } from '../../hooks/useTranslation'
6
+ import { toast } from '../notifications/ToastProvider'
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Connectors hub — the ONE unified setup experience every connector uses.
10
+ //
11
+ // An addon plugin contributes a ConnectorDefinition; the host plugin's settings
12
+ // render this hub in its "Integrations" tab. Credential connectors (api-key /
13
+ // mtls) get a declarative form (Test / Save); OAuth connectors get a Connect
14
+ // button. Each connector's own extras (import statement, sync now, history) come
15
+ // from its `ExtraPanel`. Adding a new connector needs no change here.
16
+ // ---------------------------------------------------------------------------
17
+
18
+ function ConnectorCard({ connector }: { connector: ConnectorDefinition }) {
19
+ const { t } = useTranslation()
20
+ const [status, setStatus] = useState<ConnectorStatus | null>(null)
21
+ const [open, setOpen] = useState(false)
22
+ const [values, setValues] = useState<Record<string, string>>({})
23
+ const [testing, setTesting] = useState(false)
24
+ const [saving, setSaving] = useState(false)
25
+ const [oauthing, setOauthing] = useState(false)
26
+
27
+ const loadStatus = useCallback(async () => {
28
+ try { setStatus(await connector.getStatus()) } catch { setStatus({ connected: false }) }
29
+ }, [connector])
30
+
31
+ useEffect(() => { void loadStatus() }, [loadStatus])
32
+
33
+ const connected = !!status?.connected
34
+ const Icon = connector.icon ? (ICON_MAP[connector.icon] ?? Plug) : Plug
35
+
36
+ async function handleTest() {
37
+ if (!connector.testConnection) return
38
+ setTesting(true)
39
+ try {
40
+ const res = await connector.testConnection(values)
41
+ res.ok ? toast.success(res.message ?? t('connectors.connected')) : toast.error(res.message ?? t('common.error'))
42
+ } catch (e: any) { toast.error(e?.message ?? t('common.error')) }
43
+ finally { setTesting(false) }
44
+ }
45
+
46
+ async function handleSave() {
47
+ if (!connector.saveConnection) return
48
+ setSaving(true)
49
+ try {
50
+ await connector.saveConnection(values)
51
+ toast.success(t('connectors.save'))
52
+ await loadStatus()
53
+ } catch (e: any) { toast.error(e?.message ?? t('common.error')) }
54
+ finally { setSaving(false) }
55
+ }
56
+
57
+ async function handleConnectOAuth() {
58
+ if (!connector.startOAuth) return
59
+ setOauthing(true)
60
+ try { window.location.href = await connector.startOAuth(window.location.href) }
61
+ catch (e: any) { toast.error(e?.message ?? t('common.error')); setOauthing(false) }
62
+ }
63
+
64
+ async function handleDisconnect() {
65
+ if (!connector.disconnect) return
66
+ try { await connector.disconnect(); toast.success(t('connectors.disconnect')); await loadStatus() }
67
+ catch (e: any) { toast.error(e?.message ?? t('common.error')) }
68
+ }
69
+
70
+ const ExtraPanel = connector.ExtraPanel
71
+
72
+ return (
73
+ <div className="rounded-lg border bg-card shadow-sm overflow-hidden">
74
+ {/* Header — always visible */}
75
+ <button
76
+ onClick={() => setOpen((o) => !o)}
77
+ className="w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-muted/30 transition-colors"
78
+ >
79
+ <div className="flex h-9 w-9 items-center justify-center rounded-lg bg-muted shrink-0">
80
+ <Icon className="h-4 w-4 text-muted-foreground" />
81
+ </div>
82
+ <div className="min-w-0 flex-1">
83
+ <p className="text-sm font-semibold truncate">{connector.name}</p>
84
+ {connector.description && <p className="text-[11px] text-muted-foreground truncate">{connector.description}</p>}
85
+ </div>
86
+ <span className={`inline-flex items-center gap-1 text-[11px] shrink-0 ${connected ? 'text-success' : 'text-muted-foreground'}`}>
87
+ {connected
88
+ ? <><CheckCircle2 className="h-3.5 w-3.5" /> {t('connectors.connected')}</>
89
+ : t('connectors.notConnected')}
90
+ </span>
91
+ <ChevronDown className={`h-4 w-4 text-muted-foreground shrink-0 transition-transform ${open ? 'rotate-180' : ''}`} />
92
+ </button>
93
+
94
+ {open && (
95
+ <div className="border-t px-4 py-4 space-y-4">
96
+ {/* Connect panel — credentials form OR OAuth */}
97
+ {connector.authKind === 'oauth' ? (
98
+ <div className="flex items-center gap-2">
99
+ <Button onClick={handleConnectOAuth} disabled={oauthing}>
100
+ {oauthing ? <Loader2 className="h-4 w-4 animate-spin" /> : <Plug className="h-4 w-4" />}
101
+ {connected ? t('connectors.connected') : `${t('connectors.connect')} ${connector.name}`}
102
+ </Button>
103
+ {connected && connector.disconnect && (
104
+ <Button variant="outline" size="sm" onClick={handleDisconnect}>
105
+ <Power className="h-3.5 w-3.5" /> {t('connectors.disconnect')}
106
+ </Button>
107
+ )}
108
+ </div>
109
+ ) : (
110
+ <div className="space-y-3">
111
+ <div className="grid gap-3 sm:grid-cols-2">
112
+ {(connector.fields ?? []).map((f) => (
113
+ <label key={f.key} className="block">
114
+ <span className="text-xs font-medium text-muted-foreground">{f.label}</span>
115
+ <input
116
+ type={f.type}
117
+ value={values[f.key] ?? ''}
118
+ onChange={(e) => setValues((v) => ({ ...v, [f.key]: e.target.value }))}
119
+ placeholder={f.placeholder}
120
+ className="w-full mt-1 rounded-input border border-input bg-card px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
121
+ />
122
+ </label>
123
+ ))}
124
+ </div>
125
+ <div className="flex gap-2">
126
+ {connector.testConnection && (
127
+ <Button variant="outline" size="sm" onClick={handleTest} disabled={testing}>
128
+ {testing ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Plug className="h-3.5 w-3.5" />} {t('connectors.test')}
129
+ </Button>
130
+ )}
131
+ {connector.saveConnection && (
132
+ <Button size="sm" onClick={handleSave} disabled={saving}>
133
+ {saving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <CheckCircle2 className="h-3.5 w-3.5" />} {t('connectors.save')}
134
+ </Button>
135
+ )}
136
+ {connected && connector.disconnect && (
137
+ <Button variant="outline" size="sm" onClick={handleDisconnect}>
138
+ <Power className="h-3.5 w-3.5" /> {t('connectors.disconnect')}
139
+ </Button>
140
+ )}
141
+ </div>
142
+ </div>
143
+ )}
144
+
145
+ {/* Connector-specific extras (import / sync / history) */}
146
+ {ExtraPanel && <div className="pt-1"><ExtraPanel /></div>}
147
+ </div>
148
+ )}
149
+ </div>
150
+ )
151
+ }
152
+
153
+ export function ConnectorsHub({ connectors }: { connectors: ConnectorDefinition[] }) {
154
+ const { t } = useTranslation()
155
+ if (!connectors || connectors.length === 0) {
156
+ return <p className="text-sm text-muted-foreground py-8 text-center">{t('connectors.empty')}</p>
157
+ }
158
+ return (
159
+ <div className="space-y-3 max-w-3xl">
160
+ {connectors.map((c) => <ConnectorCard key={c.id} connector={c} />)}
161
+ </div>
162
+ )
163
+ }