@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.
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -1
- package/dist/crud/CrudFormPage.d.ts.map +1 -1
- package/dist/crud/archetypes/PersonDetailTabs.d.ts +12 -0
- package/dist/crud/archetypes/PersonDetailTabs.d.ts.map +1 -1
- package/dist/crud/fieldToColumn.d.ts.map +1 -1
- package/dist/crud/index.d.ts +1 -0
- package/dist/crud/index.d.ts.map +1 -1
- package/dist/crud/relation-field.d.ts +28 -0
- package/dist/crud/relation-field.d.ts.map +1 -0
- package/dist/hooks/useModuleNavigation.d.ts +2 -0
- package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
- package/dist/index.cjs +726 -379
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +550 -204
- package/dist/index.js.map +1 -1
- package/dist/shell/components/plugins/ConnectorsHub.d.ts +6 -0
- package/dist/shell/components/plugins/ConnectorsHub.d.ts.map +1 -0
- package/dist/shell/components/plugins/PluginSettingsPanel.d.ts +4 -2
- package/dist/shell/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
- package/dist/shell/createSaasApp.d.ts.map +1 -1
- package/dist/shell/hooks/useModuleNavigation.d.ts +2 -0
- package/dist/shell/hooks/useModuleNavigation.d.ts.map +1 -1
- package/dist/shell/types/crud.d.ts +12 -1
- package/dist/shell/types/crud.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/crud/CrudDetailPage.tsx +5 -0
- package/src/crud/CrudFormPage.tsx +12 -0
- package/src/crud/archetypes/PersonDetailTabs.tsx +22 -0
- package/src/crud/fieldToColumn.tsx +3 -0
- package/src/crud/index.ts +1 -0
- package/src/crud/relation-field.tsx +115 -0
- package/src/hooks/useModuleNavigation.ts +27 -1
- package/src/index.ts +1 -0
- package/src/shell/components/plugins/ConnectorsHub.tsx +163 -0
- package/src/shell/components/plugins/PluginSettingsPanel.tsx +106 -27
- package/src/shell/createSaasApp.tsx +22 -1
- package/src/shell/hooks/useModuleNavigation.ts +27 -1
- package/src/shell/types/crud.ts +13 -0
|
@@ -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 —
|
|
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;
|
|
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;;;;
|
|
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":"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"}
|
|
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.
|
|
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/
|
|
51
|
-
"@fayz-ai/
|
|
52
|
-
"@fayz-ai/
|
|
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
|
+
}
|