nucleus-core-ts 0.9.781 → 0.9.783
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/client.js +1 -1
- package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.js +85 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.js +165 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionForm.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionForm.js +112 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionList.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionList.js +184 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.js +131 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.d.ts +21 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.js +49 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.d.ts +12 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.js +95 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTest.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +116 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialFields.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialFields.js +171 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.js +63 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointForm.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointForm.js +166 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.js +162 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointList.d.ts +12 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointList.js +205 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointSample.d.ts +6 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +204 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.js +101 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.d.ts +17 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.js +179 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +115 -0
- package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.js +175 -0
- package/dist/fe/components/IntegrationsPage/components/MappingEditor.d.ts +15 -0
- package/dist/fe/components/IntegrationsPage/components/MappingEditor.js +193 -0
- package/dist/fe/components/IntegrationsPage/components/MappingList.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/MappingList.js +191 -0
- package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +150 -0
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +154 -0
- package/dist/fe/components/IntegrationsPage/components/PaginationFields.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/PaginationFields.js +218 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.d.ts +24 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.js +180 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.d.ts +33 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.js +100 -0
- package/dist/fe/components/IntegrationsPage/components/Primitives.d.ts +65 -0
- package/dist/fe/components/IntegrationsPage/components/Primitives.js +94 -0
- package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.js +48 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistory.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistory.js +124 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.d.ts +7 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.js +129 -0
- package/dist/fe/components/IntegrationsPage/components/RunPanel.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/RunPanel.js +160 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.js +43 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.js +92 -0
- package/dist/fe/components/IntegrationsPage/components/RunProgressBar.d.ts +8 -0
- package/dist/fe/components/IntegrationsPage/components/RunProgressBar.js +77 -0
- package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.js +65 -0
- package/dist/fe/components/IntegrationsPage/components/RunsTab.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/RunsTab.js +39 -0
- package/dist/fe/components/IntegrationsPage/components/Skeletons.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/Skeletons.js +139 -0
- package/dist/fe/components/IntegrationsPage/components/authOptions.d.ts +29 -0
- package/dist/fe/components/IntegrationsPage/components/authOptions.js +71 -0
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.d.ts +64 -0
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.js +112 -0
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.d.ts +25 -0
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.js +67 -0
- package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.d.ts +16 -0
- package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.js +41 -0
- package/dist/fe/components/IntegrationsPage/components/formSupport.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/formSupport.js +25 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.d.ts +40 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.js +44 -0
- package/dist/fe/components/IntegrationsPage/components/pathParams.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/pathParams.js +15 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.d.ts +33 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.js +85 -0
- package/dist/fe/components/IntegrationsPage/components/useInfiniteList.d.ts +40 -0
- package/dist/fe/components/IntegrationsPage/components/useInfiniteList.js +110 -0
- package/dist/fe/components/IntegrationsPage/index.d.ts +18 -0
- package/dist/fe/components/IntegrationsPage/index.js +11 -0
- package/dist/fe/components/IntegrationsPage/store/index.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/store/index.js +124 -0
- package/dist/fe/components/IntegrationsPage/store/state.d.ts +80 -0
- package/dist/fe/components/IntegrationsPage/store/state.js +32 -0
- package/dist/fe/components/IntegrationsPage/theme/index.d.ts +103 -0
- package/dist/fe/components/IntegrationsPage/theme/index.js +113 -0
- package/dist/fe/components/IntegrationsPage/types/actions.d.ts +91 -0
- package/dist/fe/components/IntegrationsPage/types/actions.js +1 -0
- package/dist/fe/components/IntegrationsPage/types/index.d.ts +3 -0
- package/dist/fe/components/IntegrationsPage/types/index.js +3 -0
- package/dist/fe/components/IntegrationsPage/types/records.d.ts +139 -0
- package/dist/fe/components/IntegrationsPage/types/records.js +11 -0
- package/dist/fe/components/IntegrationsPage/types/results.d.ts +52 -0
- package/dist/fe/components/IntegrationsPage/types/results.js +4 -0
- package/dist/fe/index.d.ts +2 -0
- package/dist/fe/index.js +1 -0
- package/dist/index.js +14 -14
- package/dist/src/Client/ApiCaller/system-tables.d.ts +505 -0
- package/dist/src/Client/Proxy/wsProxy.js +25 -1
- package/dist/src/ElysiaPlugin/routes/backup/types.d.ts +3 -1
- package/dist/src/ElysiaPlugin/routes/integrations/index.d.ts +49 -0
- package/dist/src/ElysiaPlugin/routes/integrations/inspect.d.ts +87 -0
- package/dist/src/ElysiaPlugin/routes/integrations/inspectRoutes.d.ts +41 -0
- package/dist/src/ElysiaPlugin/routes/integrations/repository.d.ts +88 -0
- package/dist/src/ElysiaPlugin/routes/integrations/types.d.ts +22 -0
- package/dist/src/ElysiaPlugin/routes/pubsub/frame.d.ts +37 -0
- package/dist/src/ElysiaPlugin/routes/pubsub/frame.test.d.ts +1 -0
- package/dist/src/Services/Backup/BackupService.d.ts +16 -8
- package/dist/src/Services/Backup/types.d.ts +25 -0
- package/dist/src/Services/Integrations/bindings.d.ts +116 -0
- package/dist/src/Services/Integrations/bindings.test.d.ts +1 -0
- package/dist/src/Services/Integrations/collect.d.ts +68 -0
- package/dist/src/Services/Integrations/collect.test.d.ts +1 -0
- package/dist/src/Services/Integrations/fetcher.d.ts +100 -0
- package/dist/src/Services/Integrations/fetcher.test.d.ts +1 -0
- package/dist/src/Services/Integrations/lock.d.ts +77 -0
- package/dist/src/Services/Integrations/lock.test.d.ts +1 -0
- package/dist/src/Services/Integrations/mapping.d.ts +63 -0
- package/dist/src/Services/Integrations/mapping.test.d.ts +1 -0
- package/dist/src/Services/Integrations/oauth.d.ts +89 -0
- package/dist/src/Services/Integrations/oauth.test.d.ts +1 -0
- package/dist/src/Services/Integrations/pagination.d.ts +97 -0
- package/dist/src/Services/Integrations/pagination.test.d.ts +1 -0
- package/dist/src/Services/Integrations/plan.d.ts +74 -0
- package/dist/src/Services/Integrations/plan.test.d.ts +1 -0
- package/dist/src/Services/Integrations/revert.d.ts +78 -0
- package/dist/src/Services/Integrations/revert.test.d.ts +1 -0
- package/dist/src/Services/Integrations/runner.d.ts +126 -0
- package/dist/src/Services/Integrations/runner.test.d.ts +1 -0
- package/dist/src/Services/Integrations/types.d.ts +208 -0
- package/dist/src/Services/Integrations/writer.d.ts +105 -0
- package/dist/src/Services/Integrations/writer.test.d.ts +1 -0
- package/dist/src/Utils/cron.d.ts +65 -0
- package/dist/src/Utils/cron.test.d.ts +1 -0
- package/dist/src/types.d.ts +67 -1
- package/package.json +1 -1
- package/schemas/config.nucleus.json +43 -0
- package/schemas/table.schema.json +2 -1
- package/scripts/generate-schema.test.ts +78 -0
- package/scripts/generate-schema.ts +43 -5
- package/src/system.tables.json +362 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useGSAP } from '@gsap/react';
|
|
4
|
+
import gsap from 'gsap';
|
|
5
|
+
import { useEffect, useEffectEvent, useRef } from 'react';
|
|
6
|
+
import { cn } from '../../../utils/cn';
|
|
7
|
+
import { useIntegrationsStore } from '../store';
|
|
8
|
+
import { integrationsPageTheme } from '../theme';
|
|
9
|
+
import { ConnectionList } from './ConnectionList';
|
|
10
|
+
import { ConnectionSettingsTab } from './ConnectionSettingsTab';
|
|
11
|
+
import { ConnectionTabs, connectionTabId, connectionTabPanelId } from './ConnectionTabs';
|
|
12
|
+
import { EndpointsTab } from './EndpointsTab';
|
|
13
|
+
import { MappingsTab } from './MappingsTab';
|
|
14
|
+
import { Notice } from './Primitives';
|
|
15
|
+
import { RunsTab } from './RunsTab';
|
|
16
|
+
/**
|
|
17
|
+
* The shell.
|
|
18
|
+
*
|
|
19
|
+
* It owns three things and nothing else: the selection rail, which section of the
|
|
20
|
+
* selected connection is on screen, and the one place an error is reported. Every
|
|
21
|
+
* call it needs arrives as an injected action, and each section fetches its own
|
|
22
|
+
* page from the server rather than being handed a list to slice.
|
|
23
|
+
*/ gsap.registerPlugin(useGSAP);
|
|
24
|
+
const theme = integrationsPageTheme;
|
|
25
|
+
export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect an external service, describe the data you want and import it on your terms.', className, actions, onSubscribeProgress }) {
|
|
26
|
+
const store = useIntegrationsStore();
|
|
27
|
+
const containerRef = useRef(null);
|
|
28
|
+
const contentRef = useRef(null);
|
|
29
|
+
// One measured entrance. Anything longer makes an operator wait to read counts
|
|
30
|
+
// they came here to check.
|
|
31
|
+
useGSAP(()=>{
|
|
32
|
+
if (!contentRef.current) return;
|
|
33
|
+
gsap.fromTo(contentRef.current, {
|
|
34
|
+
opacity: 0,
|
|
35
|
+
y: 10
|
|
36
|
+
}, {
|
|
37
|
+
opacity: 1,
|
|
38
|
+
y: 0,
|
|
39
|
+
duration: 0.3,
|
|
40
|
+
ease: 'power2.out'
|
|
41
|
+
});
|
|
42
|
+
}, {
|
|
43
|
+
scope: containerRef
|
|
44
|
+
});
|
|
45
|
+
// Progress is pushed by the app, not polled here. The unsubscribe is returned
|
|
46
|
+
// straight back so a panel that closes mid-import stops receiving frames.
|
|
47
|
+
const subscribeProgress = useEffectEvent(()=>onSubscribeProgress?.((progress)=>{
|
|
48
|
+
store.setProgress(progress);
|
|
49
|
+
}));
|
|
50
|
+
useEffect(()=>{
|
|
51
|
+
if (!onSubscribeProgress) return;
|
|
52
|
+
return subscribeProgress();
|
|
53
|
+
}, [
|
|
54
|
+
onSubscribeProgress
|
|
55
|
+
]);
|
|
56
|
+
const dismissError = ()=>{
|
|
57
|
+
store.setError(null);
|
|
58
|
+
};
|
|
59
|
+
const changeTab = (tab)=>{
|
|
60
|
+
store.setTab(tab);
|
|
61
|
+
};
|
|
62
|
+
const tab = store.tab;
|
|
63
|
+
const hasConnection = store.selectedSourceId !== null;
|
|
64
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
65
|
+
className: cn(theme.container, className),
|
|
66
|
+
ref: containerRef,
|
|
67
|
+
children: [
|
|
68
|
+
/*#__PURE__*/ _jsx("header", {
|
|
69
|
+
className: theme.header.wrapper,
|
|
70
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
71
|
+
className: "flex min-w-0 flex-col gap-1",
|
|
72
|
+
children: [
|
|
73
|
+
/*#__PURE__*/ _jsx("h1", {
|
|
74
|
+
className: theme.header.title,
|
|
75
|
+
children: title
|
|
76
|
+
}),
|
|
77
|
+
subtitle ? /*#__PURE__*/ _jsx("p", {
|
|
78
|
+
className: theme.header.subtitle,
|
|
79
|
+
children: subtitle
|
|
80
|
+
}) : null
|
|
81
|
+
]
|
|
82
|
+
})
|
|
83
|
+
}),
|
|
84
|
+
/*#__PURE__*/ _jsx("div", {
|
|
85
|
+
"aria-live": "assertive",
|
|
86
|
+
className: "contents",
|
|
87
|
+
role: "alert",
|
|
88
|
+
children: store.error ? /*#__PURE__*/ _jsxs("div", {
|
|
89
|
+
className: "relative",
|
|
90
|
+
children: [
|
|
91
|
+
/*#__PURE__*/ _jsx(Notice, {
|
|
92
|
+
title: "Something went wrong",
|
|
93
|
+
tone: "danger",
|
|
94
|
+
children: store.error
|
|
95
|
+
}),
|
|
96
|
+
/*#__PURE__*/ _jsx("button", {
|
|
97
|
+
"aria-label": "Dismiss error",
|
|
98
|
+
className: cn(theme.button.ghost, 'absolute top-1.5 right-1.5 h-8 w-8 px-0 sm:h-8'),
|
|
99
|
+
onClick: dismissError,
|
|
100
|
+
type: "button",
|
|
101
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
102
|
+
"aria-hidden": "true",
|
|
103
|
+
children: "✕"
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
}) : null
|
|
108
|
+
}),
|
|
109
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
110
|
+
className: theme.layout.split,
|
|
111
|
+
ref: contentRef,
|
|
112
|
+
children: [
|
|
113
|
+
/*#__PURE__*/ _jsx("aside", {
|
|
114
|
+
"aria-label": "Connections",
|
|
115
|
+
className: theme.layout.aside,
|
|
116
|
+
children: /*#__PURE__*/ _jsx(ConnectionList, {
|
|
117
|
+
actions: actions
|
|
118
|
+
})
|
|
119
|
+
}),
|
|
120
|
+
/*#__PURE__*/ _jsx("section", {
|
|
121
|
+
"aria-label": "Connection detail",
|
|
122
|
+
className: theme.layout.main,
|
|
123
|
+
children: hasConnection ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
124
|
+
children: [
|
|
125
|
+
/*#__PURE__*/ _jsx(ConnectionTabs, {
|
|
126
|
+
counts: {
|
|
127
|
+
endpoints: store.endpointsTotal,
|
|
128
|
+
mappings: store.mappingsTotal,
|
|
129
|
+
runs: store.runsTotal
|
|
130
|
+
},
|
|
131
|
+
onChange: changeTab,
|
|
132
|
+
value: tab
|
|
133
|
+
}),
|
|
134
|
+
/*#__PURE__*/ _jsxs("section", {
|
|
135
|
+
"aria-labelledby": connectionTabId(tab),
|
|
136
|
+
className: theme.layout.main,
|
|
137
|
+
id: connectionTabPanelId(tab),
|
|
138
|
+
role: "tabpanel",
|
|
139
|
+
children: [
|
|
140
|
+
tab === 'connection' ? /*#__PURE__*/ _jsx(ConnectionSettingsTab, {
|
|
141
|
+
actions: actions
|
|
142
|
+
}) : null,
|
|
143
|
+
tab === 'endpoints' ? /*#__PURE__*/ _jsx(EndpointsTab, {
|
|
144
|
+
actions: actions
|
|
145
|
+
}) : null,
|
|
146
|
+
tab === 'mappings' ? /*#__PURE__*/ _jsx(MappingsTab, {
|
|
147
|
+
actions: actions
|
|
148
|
+
}) : null,
|
|
149
|
+
tab === 'runs' ? /*#__PURE__*/ _jsx(RunsTab, {
|
|
150
|
+
actions: actions
|
|
151
|
+
}) : null
|
|
152
|
+
]
|
|
153
|
+
})
|
|
154
|
+
]
|
|
155
|
+
}) : // Not a dead end. With nothing selected the only useful thing on this
|
|
156
|
+
// screen is creating a connection, so that is what it shows — the
|
|
157
|
+
// previous message told an operator to select from a list that is
|
|
158
|
+
// empty on a fresh install, with no way to add the first one.
|
|
159
|
+
/*#__PURE__*/ _jsxs(_Fragment, {
|
|
160
|
+
children: [
|
|
161
|
+
/*#__PURE__*/ _jsx("p", {
|
|
162
|
+
className: theme.list.empty,
|
|
163
|
+
children: "Select a connection on the left, or set up a new one here."
|
|
164
|
+
}),
|
|
165
|
+
/*#__PURE__*/ _jsx(ConnectionSettingsTab, {
|
|
166
|
+
actions: actions
|
|
167
|
+
})
|
|
168
|
+
]
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
]
|
|
172
|
+
})
|
|
173
|
+
]
|
|
174
|
+
});
|
|
175
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type MappingDraft, type MappingTarget } from './mappingDraft';
|
|
2
|
+
export type MappingEditorProps = {
|
|
3
|
+
/** Starting values. Read once — remount with a new `key` to load another. */
|
|
4
|
+
mapping: MappingDraft;
|
|
5
|
+
targets: MappingTarget[];
|
|
6
|
+
targetsLoading: boolean;
|
|
7
|
+
/** Field names the selected endpoint's last sample reported. */
|
|
8
|
+
sourceFields: string[];
|
|
9
|
+
saving: boolean;
|
|
10
|
+
deleting: boolean;
|
|
11
|
+
onSave: (draft: MappingDraft) => void;
|
|
12
|
+
onDelete: (id: string) => void;
|
|
13
|
+
onCancel: () => void;
|
|
14
|
+
};
|
|
15
|
+
export declare function MappingEditor({ mapping, targets, targetsLoading, sourceFields, saving, deleting, onSave, onDelete, onCancel, }: MappingEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useState } from 'react';
|
|
4
|
+
import { integrationsPageTheme } from '../theme';
|
|
5
|
+
import { FieldMappingRows } from './FieldMappingRows';
|
|
6
|
+
import { MappingWriteModeFields } from './MappingWriteModeFields';
|
|
7
|
+
import { mappingDraftIssues } from './mappingDraft';
|
|
8
|
+
import { Field } from './Primitives';
|
|
9
|
+
import { CardSkeleton } from './Skeletons';
|
|
10
|
+
/**
|
|
11
|
+
* Everything about one mapping, on one screen.
|
|
12
|
+
*
|
|
13
|
+
* The draft is local until Save: an import is not a text field where a stray
|
|
14
|
+
* keystroke is harmless, and half a mapping saved between two thoughts is a
|
|
15
|
+
* mapping that could be RUN in that state.
|
|
16
|
+
*
|
|
17
|
+
* Mount it with a `key` of the mapping id so switching rows starts a fresh
|
|
18
|
+
* draft instead of pouring one mapping's edits into another.
|
|
19
|
+
*/ const theme = integrationsPageTheme;
|
|
20
|
+
export function MappingEditor({ mapping, targets, targetsLoading, sourceFields, saving, deleting, onSave, onDelete, onCancel }) {
|
|
21
|
+
const uid = useId();
|
|
22
|
+
const [draft, setDraft] = useState(mapping);
|
|
23
|
+
const patch = (next)=>{
|
|
24
|
+
setDraft((current)=>({
|
|
25
|
+
...current,
|
|
26
|
+
...next
|
|
27
|
+
}));
|
|
28
|
+
};
|
|
29
|
+
// Switching entity invalidates every column name chosen for the old one.
|
|
30
|
+
// Blanking them is louder than leaving a select pointing at a column that no
|
|
31
|
+
// longer exists, which renders empty and saves the stale name anyway.
|
|
32
|
+
const chooseTarget = (entity)=>{
|
|
33
|
+
const fields = targets.find((option)=>option.entity === entity)?.fields ?? [];
|
|
34
|
+
const keepDedup = fields.includes(draft.dedupTargetField ?? '');
|
|
35
|
+
patch({
|
|
36
|
+
targetEntity: entity,
|
|
37
|
+
dedupTargetField: keepDedup ? draft.dedupTargetField : null,
|
|
38
|
+
fieldMappings: draft.fieldMappings.map((row)=>fields.length === 0 || fields.includes(row.target) ? row : {
|
|
39
|
+
...row,
|
|
40
|
+
target: ''
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
if (targetsLoading) return /*#__PURE__*/ _jsx(CardSkeleton, {});
|
|
45
|
+
const targetFields = targets.find((option)=>option.entity === draft.targetEntity)?.fields ?? [];
|
|
46
|
+
const issues = mappingDraftIssues(draft);
|
|
47
|
+
const busy = saving || deleting;
|
|
48
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
49
|
+
className: theme.card.wrapper,
|
|
50
|
+
children: [
|
|
51
|
+
/*#__PURE__*/ _jsx("header", {
|
|
52
|
+
className: theme.card.header,
|
|
53
|
+
children: /*#__PURE__*/ _jsx("h2", {
|
|
54
|
+
className: theme.card.title,
|
|
55
|
+
children: draft.id ? draft.name || 'Mapping' : 'New mapping'
|
|
56
|
+
})
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
59
|
+
className: theme.card.body,
|
|
60
|
+
children: [
|
|
61
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
62
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
63
|
+
children: [
|
|
64
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
65
|
+
htmlFor: `${uid}-name`,
|
|
66
|
+
label: "Name",
|
|
67
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
68
|
+
className: theme.field.input,
|
|
69
|
+
disabled: busy,
|
|
70
|
+
id: `${uid}-name`,
|
|
71
|
+
onChange: (event)=>patch({
|
|
72
|
+
name: event.target.value
|
|
73
|
+
}),
|
|
74
|
+
placeholder: "Staff directory import",
|
|
75
|
+
value: draft.name
|
|
76
|
+
})
|
|
77
|
+
}),
|
|
78
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
79
|
+
hint: targets.length === 0 ? 'This app exposes no writable entities.' : undefined,
|
|
80
|
+
htmlFor: `${uid}-target`,
|
|
81
|
+
label: "Target entity",
|
|
82
|
+
children: /*#__PURE__*/ _jsxs("select", {
|
|
83
|
+
className: theme.field.input,
|
|
84
|
+
disabled: busy || targets.length === 0,
|
|
85
|
+
id: `${uid}-target`,
|
|
86
|
+
onChange: (event)=>chooseTarget(event.target.value),
|
|
87
|
+
value: draft.targetEntity,
|
|
88
|
+
children: [
|
|
89
|
+
/*#__PURE__*/ _jsx("option", {
|
|
90
|
+
value: "",
|
|
91
|
+
children: "Not set"
|
|
92
|
+
}),
|
|
93
|
+
targets.map((option)=>/*#__PURE__*/ _jsx("option", {
|
|
94
|
+
value: option.entity,
|
|
95
|
+
children: option.label
|
|
96
|
+
}, option.entity))
|
|
97
|
+
]
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
]
|
|
101
|
+
}),
|
|
102
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
103
|
+
hint: targetFields.length === 0 ? 'Choose a target entity to list its columns.' : 'The column an incoming record is matched on. Without it nothing can be updated.',
|
|
104
|
+
htmlFor: `${uid}-dedup`,
|
|
105
|
+
label: "Deduplication column",
|
|
106
|
+
children: /*#__PURE__*/ _jsxs("select", {
|
|
107
|
+
"aria-describedby": `${uid}-dedup-hint`,
|
|
108
|
+
className: theme.field.input,
|
|
109
|
+
disabled: busy || targetFields.length === 0,
|
|
110
|
+
id: `${uid}-dedup`,
|
|
111
|
+
onChange: (event)=>patch({
|
|
112
|
+
dedupTargetField: event.target.value || null
|
|
113
|
+
}),
|
|
114
|
+
value: draft.dedupTargetField ?? '',
|
|
115
|
+
children: [
|
|
116
|
+
/*#__PURE__*/ _jsx("option", {
|
|
117
|
+
value: "",
|
|
118
|
+
children: "Not set"
|
|
119
|
+
}),
|
|
120
|
+
targetFields.map((field)=>/*#__PURE__*/ _jsx("option", {
|
|
121
|
+
value: field,
|
|
122
|
+
children: field
|
|
123
|
+
}, field))
|
|
124
|
+
]
|
|
125
|
+
})
|
|
126
|
+
}),
|
|
127
|
+
/*#__PURE__*/ _jsx(MappingWriteModeFields, {
|
|
128
|
+
dedupConfigured: (draft.dedupTargetField ?? '').trim() !== '',
|
|
129
|
+
disabled: busy,
|
|
130
|
+
idPrefix: uid,
|
|
131
|
+
missingRecordColumn: draft.missingRecordColumn ?? '',
|
|
132
|
+
missingRecordPolicy: draft.missingRecordPolicy,
|
|
133
|
+
missingRecordValue: draft.missingRecordValue ?? '',
|
|
134
|
+
onMissingRecordColumnChange: (value)=>patch({
|
|
135
|
+
missingRecordColumn: value
|
|
136
|
+
}),
|
|
137
|
+
onMissingRecordPolicyChange: (policy)=>patch({
|
|
138
|
+
missingRecordPolicy: policy
|
|
139
|
+
}),
|
|
140
|
+
onMissingRecordValueChange: (value)=>patch({
|
|
141
|
+
missingRecordValue: value
|
|
142
|
+
}),
|
|
143
|
+
onWriteModeChange: (mode)=>patch({
|
|
144
|
+
writeMode: mode
|
|
145
|
+
}),
|
|
146
|
+
writeMode: draft.writeMode
|
|
147
|
+
}),
|
|
148
|
+
/*#__PURE__*/ _jsx(FieldMappingRows, {
|
|
149
|
+
disabled: busy,
|
|
150
|
+
onChange: (rows)=>patch({
|
|
151
|
+
fieldMappings: rows
|
|
152
|
+
}),
|
|
153
|
+
rows: draft.fieldMappings,
|
|
154
|
+
sourceFields: sourceFields,
|
|
155
|
+
targetFields: targetFields
|
|
156
|
+
}),
|
|
157
|
+
issues.length > 0 ? /*#__PURE__*/ _jsx("ul", {
|
|
158
|
+
className: theme.field.error,
|
|
159
|
+
children: issues.map((issue)=>/*#__PURE__*/ _jsx("li", {
|
|
160
|
+
children: issue
|
|
161
|
+
}, issue))
|
|
162
|
+
}) : null
|
|
163
|
+
]
|
|
164
|
+
}),
|
|
165
|
+
/*#__PURE__*/ _jsxs("footer", {
|
|
166
|
+
className: theme.card.footer,
|
|
167
|
+
children: [
|
|
168
|
+
draft.id ? /*#__PURE__*/ _jsx("button", {
|
|
169
|
+
className: theme.button.danger,
|
|
170
|
+
disabled: busy,
|
|
171
|
+
onClick: ()=>onDelete(draft.id ?? ''),
|
|
172
|
+
type: "button",
|
|
173
|
+
children: deleting ? 'Deleting…' : 'Delete'
|
|
174
|
+
}) : null,
|
|
175
|
+
/*#__PURE__*/ _jsx("button", {
|
|
176
|
+
className: theme.button.secondary,
|
|
177
|
+
disabled: busy,
|
|
178
|
+
onClick: onCancel,
|
|
179
|
+
type: "button",
|
|
180
|
+
children: "Cancel"
|
|
181
|
+
}),
|
|
182
|
+
/*#__PURE__*/ _jsx("button", {
|
|
183
|
+
className: theme.button.primary,
|
|
184
|
+
disabled: busy || issues.length > 0,
|
|
185
|
+
onClick: ()=>onSave(draft),
|
|
186
|
+
type: "button",
|
|
187
|
+
children: saving ? 'Saving…' : 'Save mapping'
|
|
188
|
+
})
|
|
189
|
+
]
|
|
190
|
+
})
|
|
191
|
+
]
|
|
192
|
+
});
|
|
193
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IntegrationMapping, IntegrationsActions } from '../types';
|
|
2
|
+
export type MappingListProps = {
|
|
3
|
+
actions: IntegrationsActions;
|
|
4
|
+
/** The connection whose mappings these are. */
|
|
5
|
+
sourceId: string;
|
|
6
|
+
selectedId: string | null;
|
|
7
|
+
/** Bumped by the tab after a save or a delete so page one is fetched again. */
|
|
8
|
+
reloadToken: number;
|
|
9
|
+
onSelect: (mapping: IntegrationMapping) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare function MappingList({ actions, sourceId, selectedId, reloadToken, onSelect, }: MappingListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useEffectEvent, useId, useState } from 'react';
|
|
4
|
+
import { cn } from '../../../utils/cn';
|
|
5
|
+
import { useIntegrationsStore } from '../store';
|
|
6
|
+
import { integrationsPageTheme } from '../theme';
|
|
7
|
+
import { Badge, Field, Notice } from './Primitives';
|
|
8
|
+
import { ListSkeleton } from './Skeletons';
|
|
9
|
+
import { useInfiniteList } from './useInfiniteList';
|
|
10
|
+
/**
|
|
11
|
+
* The mappings of the selected connection, one page at a time.
|
|
12
|
+
*
|
|
13
|
+
* Searching and paging are the server's job; this list only shows what came
|
|
14
|
+
* back. The write mode is on the row rather than only inside the editor because
|
|
15
|
+
* `replace` is the one setting worth spotting before opening anything.
|
|
16
|
+
*/ const theme = integrationsPageTheme;
|
|
17
|
+
const PAGE_SIZE = 20;
|
|
18
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
19
|
+
const WRITE_MODE_TONE = {
|
|
20
|
+
upsert: 'info',
|
|
21
|
+
append: 'neutral',
|
|
22
|
+
replace: 'danger'
|
|
23
|
+
};
|
|
24
|
+
function describeLastRun(value) {
|
|
25
|
+
if (!value) return 'Never run';
|
|
26
|
+
const when = new Date(value);
|
|
27
|
+
return Number.isNaN(when.getTime()) ? 'Never run' : `Last run ${when.toLocaleString()}`;
|
|
28
|
+
}
|
|
29
|
+
export function MappingList({ actions, sourceId, selectedId, reloadToken, onSelect }) {
|
|
30
|
+
const store = useIntegrationsStore();
|
|
31
|
+
const uid = useId();
|
|
32
|
+
const search = store.mappingsSearch;
|
|
33
|
+
const [term, setTerm] = useState(search);
|
|
34
|
+
// Typed characters become a request only once the typing stops; a fetch per
|
|
35
|
+
// keystroke would put page-1 answers in a race with each other.
|
|
36
|
+
const commitSearch = useEffectEvent(()=>{
|
|
37
|
+
store.setMappingsSearch(term);
|
|
38
|
+
});
|
|
39
|
+
useEffect(()=>{
|
|
40
|
+
const timer = setTimeout(commitSearch, SEARCH_DEBOUNCE_MS);
|
|
41
|
+
return ()=>clearTimeout(timer);
|
|
42
|
+
}, [
|
|
43
|
+
term
|
|
44
|
+
]);
|
|
45
|
+
const list = useInfiniteList({
|
|
46
|
+
load: (page)=>{
|
|
47
|
+
// Without a connection there is nothing to scope by, and asking anyway
|
|
48
|
+
// would answer with every mapping in the deployment under this
|
|
49
|
+
// connection's name.
|
|
50
|
+
if (!sourceId) return Promise.resolve({
|
|
51
|
+
items: [],
|
|
52
|
+
total: 0
|
|
53
|
+
});
|
|
54
|
+
return actions.listMappings(sourceId, {
|
|
55
|
+
page,
|
|
56
|
+
limit: PAGE_SIZE,
|
|
57
|
+
search: search.length > 0 ? search : undefined,
|
|
58
|
+
sort: [
|
|
59
|
+
{
|
|
60
|
+
field: 'name',
|
|
61
|
+
direction: 'asc'
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}).then((result)=>({
|
|
65
|
+
items: result.items,
|
|
66
|
+
total: result.meta?.totalItems ?? result.items.length
|
|
67
|
+
}));
|
|
68
|
+
},
|
|
69
|
+
deps: [
|
|
70
|
+
sourceId,
|
|
71
|
+
search,
|
|
72
|
+
reloadToken
|
|
73
|
+
],
|
|
74
|
+
pageSize: PAGE_SIZE
|
|
75
|
+
});
|
|
76
|
+
// Mirrored so the runs screen can name the selected mapping without fetching
|
|
77
|
+
// it again. The rows still come from, and only from, the server.
|
|
78
|
+
const publishItems = useEffectEvent(()=>{
|
|
79
|
+
store.setMappings(list.items, list.total);
|
|
80
|
+
});
|
|
81
|
+
const publishLoading = useEffectEvent(()=>{
|
|
82
|
+
store.setMappingsLoading(list.loading);
|
|
83
|
+
});
|
|
84
|
+
useEffect(()=>{
|
|
85
|
+
publishItems();
|
|
86
|
+
}, [
|
|
87
|
+
list.items,
|
|
88
|
+
list.total
|
|
89
|
+
]);
|
|
90
|
+
useEffect(()=>{
|
|
91
|
+
publishLoading();
|
|
92
|
+
}, [
|
|
93
|
+
list.loading
|
|
94
|
+
]);
|
|
95
|
+
const showSkeleton = list.loading && list.items.length === 0;
|
|
96
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
97
|
+
className: "flex flex-col gap-3",
|
|
98
|
+
children: [
|
|
99
|
+
/*#__PURE__*/ _jsx(Field, {
|
|
100
|
+
htmlFor: `${uid}-search`,
|
|
101
|
+
label: "Search mappings",
|
|
102
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
103
|
+
className: theme.field.input,
|
|
104
|
+
id: `${uid}-search`,
|
|
105
|
+
onChange: (event)=>setTerm(event.target.value),
|
|
106
|
+
placeholder: "Name or target entity",
|
|
107
|
+
type: "search",
|
|
108
|
+
value: term
|
|
109
|
+
})
|
|
110
|
+
}),
|
|
111
|
+
list.error ? /*#__PURE__*/ _jsxs(Notice, {
|
|
112
|
+
title: "This list could not be loaded",
|
|
113
|
+
tone: "danger",
|
|
114
|
+
children: [
|
|
115
|
+
list.error,
|
|
116
|
+
' ',
|
|
117
|
+
/*#__PURE__*/ _jsx("button", {
|
|
118
|
+
className: theme.button.ghost,
|
|
119
|
+
onClick: list.reload,
|
|
120
|
+
type: "button",
|
|
121
|
+
children: "Try again"
|
|
122
|
+
})
|
|
123
|
+
]
|
|
124
|
+
}) : null,
|
|
125
|
+
showSkeleton ? /*#__PURE__*/ _jsx(ListSkeleton, {
|
|
126
|
+
rows: 5
|
|
127
|
+
}) : list.items.length === 0 && !list.error ? /*#__PURE__*/ _jsx("p", {
|
|
128
|
+
className: theme.list.empty,
|
|
129
|
+
children: store.endpointsTotal === 0 ? 'No endpoints on this connection yet. A mapping reads from one, so add an endpoint first.' : 'No mappings match. A mapping is what turns a response into rows.'
|
|
130
|
+
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
131
|
+
children: [
|
|
132
|
+
/*#__PURE__*/ _jsxs("p", {
|
|
133
|
+
className: theme.list.itemMeta,
|
|
134
|
+
children: [
|
|
135
|
+
list.items.length,
|
|
136
|
+
" of ",
|
|
137
|
+
list.total,
|
|
138
|
+
" shown"
|
|
139
|
+
]
|
|
140
|
+
}),
|
|
141
|
+
/*#__PURE__*/ _jsx("ul", {
|
|
142
|
+
className: theme.list.wrapper,
|
|
143
|
+
children: list.items.map((mapping)=>/*#__PURE__*/ _jsx("li", {
|
|
144
|
+
children: /*#__PURE__*/ _jsxs("button", {
|
|
145
|
+
"aria-current": mapping.id === selectedId ? 'true' : undefined,
|
|
146
|
+
className: cn(theme.list.item, mapping.id === selectedId ? theme.list.itemSelected : undefined),
|
|
147
|
+
onClick: ()=>onSelect(mapping),
|
|
148
|
+
type: "button",
|
|
149
|
+
children: [
|
|
150
|
+
/*#__PURE__*/ _jsx("span", {
|
|
151
|
+
className: theme.list.itemTitle,
|
|
152
|
+
children: mapping.name
|
|
153
|
+
}),
|
|
154
|
+
/*#__PURE__*/ _jsx("span", {
|
|
155
|
+
className: theme.list.itemMeta,
|
|
156
|
+
children: mapping.targetEntity || 'No target entity'
|
|
157
|
+
}),
|
|
158
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
159
|
+
className: "mt-1 flex flex-wrap items-center gap-1.5",
|
|
160
|
+
children: [
|
|
161
|
+
/*#__PURE__*/ _jsx(Badge, {
|
|
162
|
+
tone: WRITE_MODE_TONE[mapping.writeMode],
|
|
163
|
+
children: mapping.writeMode
|
|
164
|
+
}),
|
|
165
|
+
mapping.enabled === false ? /*#__PURE__*/ _jsx(Badge, {
|
|
166
|
+
tone: "neutral",
|
|
167
|
+
children: "disabled"
|
|
168
|
+
}) : null,
|
|
169
|
+
/*#__PURE__*/ _jsx("span", {
|
|
170
|
+
className: theme.list.itemMeta,
|
|
171
|
+
children: describeLastRun(mapping.lastRunAt)
|
|
172
|
+
})
|
|
173
|
+
]
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
})
|
|
177
|
+
}, mapping.id))
|
|
178
|
+
})
|
|
179
|
+
]
|
|
180
|
+
}),
|
|
181
|
+
/*#__PURE__*/ _jsx("div", {
|
|
182
|
+
className: theme.list.sentinel,
|
|
183
|
+
ref: list.sentinelRef
|
|
184
|
+
}),
|
|
185
|
+
list.loading && list.items.length > 0 ? /*#__PURE__*/ _jsx("p", {
|
|
186
|
+
className: theme.list.itemMeta,
|
|
187
|
+
children: "Loading more…"
|
|
188
|
+
}) : null
|
|
189
|
+
]
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MissingRecordPolicyValue, WriteModeValue } from '../types';
|
|
2
|
+
/** Deactivate has nowhere to write its flag without both of these. */
|
|
3
|
+
export declare function missingRecordFieldsIncomplete(policy: MissingRecordPolicyValue, column: string, value: string): boolean;
|
|
4
|
+
export type MappingWriteModeFieldsProps = {
|
|
5
|
+
/** Namespaces the control ids so two editors on one screen cannot collide. */
|
|
6
|
+
idPrefix: string;
|
|
7
|
+
writeMode: WriteModeValue;
|
|
8
|
+
missingRecordPolicy: MissingRecordPolicyValue;
|
|
9
|
+
missingRecordColumn: string;
|
|
10
|
+
missingRecordValue: string;
|
|
11
|
+
/** Upsert is only the calmer option when there is something to match on. */
|
|
12
|
+
dedupConfigured: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
onWriteModeChange: (mode: WriteModeValue) => void;
|
|
15
|
+
onMissingRecordPolicyChange: (policy: MissingRecordPolicyValue) => void;
|
|
16
|
+
onMissingRecordColumnChange: (value: string) => void;
|
|
17
|
+
onMissingRecordValueChange: (value: string) => void;
|
|
18
|
+
};
|
|
19
|
+
export declare function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolicy, missingRecordColumn, missingRecordValue, dedupConfigured, disabled, onWriteModeChange, onMissingRecordPolicyChange, onMissingRecordColumnChange, onMissingRecordValueChange, }: MappingWriteModeFieldsProps): import("react/jsx-runtime").JSX.Element;
|