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,204 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useState } from 'react';
|
|
4
|
+
import { useIntegrationsStore } from '../store';
|
|
5
|
+
import { integrationsPageTheme } from '../theme';
|
|
6
|
+
import { Badge, CodeBlock, Field, Notice, Stat } from './Primitives';
|
|
7
|
+
import { pathParamNames } from './pathParams';
|
|
8
|
+
import { CardSkeleton, StatGridSkeleton } from './Skeletons';
|
|
9
|
+
/**
|
|
10
|
+
* One real request, shown in full.
|
|
11
|
+
*
|
|
12
|
+
* The interesting case is zero records, because that is when a configuration
|
|
13
|
+
* looks finished and imports nothing. The server already answers it — it returns
|
|
14
|
+
* the top-level keys of the body it actually received — so those keys are given
|
|
15
|
+
* their own block here rather than being folded into a generic "no data".
|
|
16
|
+
*/ const theme = integrationsPageTheme;
|
|
17
|
+
export function EndpointSample({ endpoint, actions }) {
|
|
18
|
+
const store = useIntegrationsStore();
|
|
19
|
+
const [values, setValues] = useState({});
|
|
20
|
+
// Kept in step with the render so an in-flight reply can ask what is selected
|
|
21
|
+
// NOW, not what was selected when it was sent.
|
|
22
|
+
const selectedRef = useRef(store.selectedEndpointId);
|
|
23
|
+
selectedRef.current = store.selectedEndpointId;
|
|
24
|
+
const params = pathParamNames(endpoint.path);
|
|
25
|
+
const sample = store.sample;
|
|
26
|
+
let unfilled = 0;
|
|
27
|
+
for (const name of params){
|
|
28
|
+
if (!(values[name] ?? '').trim()) unfilled += 1;
|
|
29
|
+
}
|
|
30
|
+
const run = ()=>{
|
|
31
|
+
// The endpoint this request is FOR. A sample can take seconds against a slow
|
|
32
|
+
// source, and an operator who clicks another endpoint meanwhile would
|
|
33
|
+
// otherwise get the first one's answer written in as the second one's field
|
|
34
|
+
// list — and then map against fields the endpoint does not have.
|
|
35
|
+
const requestedFor = endpoint.id;
|
|
36
|
+
store.setSampling(true);
|
|
37
|
+
store.setError(null);
|
|
38
|
+
actions.sampleEndpoint(requestedFor, params.length > 0 ? values : undefined).then((result)=>{
|
|
39
|
+
if (selectedRef.current !== requestedFor) return;
|
|
40
|
+
store.setSample(result);
|
|
41
|
+
if (!result.ok && result.error) store.setError(result.error);
|
|
42
|
+
}).catch((error)=>{
|
|
43
|
+
if (selectedRef.current !== requestedFor) return;
|
|
44
|
+
store.setSample(null);
|
|
45
|
+
store.setError(error instanceof Error ? error.message : 'Sampling the endpoint failed.');
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
49
|
+
className: theme.card.wrapper,
|
|
50
|
+
children: [
|
|
51
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
52
|
+
className: theme.card.header,
|
|
53
|
+
children: [
|
|
54
|
+
/*#__PURE__*/ _jsx("h3", {
|
|
55
|
+
className: theme.card.title,
|
|
56
|
+
children: "Response sample"
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ _jsx("button", {
|
|
59
|
+
className: theme.button.secondary,
|
|
60
|
+
disabled: store.sampling || unfilled > 0,
|
|
61
|
+
onClick: run,
|
|
62
|
+
type: "button",
|
|
63
|
+
children: store.sampling ? 'Sampling…' : 'Send one request'
|
|
64
|
+
})
|
|
65
|
+
]
|
|
66
|
+
}),
|
|
67
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
68
|
+
className: theme.card.body,
|
|
69
|
+
children: [
|
|
70
|
+
params.length > 0 ? /*#__PURE__*/ _jsxs("fieldset", {
|
|
71
|
+
className: "flex flex-col gap-2 border-0 p-0",
|
|
72
|
+
children: [
|
|
73
|
+
/*#__PURE__*/ _jsx("legend", {
|
|
74
|
+
className: theme.field.label,
|
|
75
|
+
children: "Example path parameter values"
|
|
76
|
+
}),
|
|
77
|
+
/*#__PURE__*/ _jsx("p", {
|
|
78
|
+
className: theme.field.hint,
|
|
79
|
+
children: "Used for this one request only. During a run each parameter takes its value from the binding configured for it."
|
|
80
|
+
}),
|
|
81
|
+
/*#__PURE__*/ _jsx("div", {
|
|
82
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
83
|
+
children: params.map((name)=>/*#__PURE__*/ _jsx(Field, {
|
|
84
|
+
htmlFor: `sample-param-${name}`,
|
|
85
|
+
label: name,
|
|
86
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
87
|
+
className: theme.field.input,
|
|
88
|
+
id: `sample-param-${name}`,
|
|
89
|
+
onChange: (event)=>setValues((previous)=>({
|
|
90
|
+
...previous,
|
|
91
|
+
[name]: event.target.value
|
|
92
|
+
})),
|
|
93
|
+
placeholder: "example value",
|
|
94
|
+
type: "text",
|
|
95
|
+
value: values[name] ?? ''
|
|
96
|
+
})
|
|
97
|
+
}, name))
|
|
98
|
+
})
|
|
99
|
+
]
|
|
100
|
+
}) : null,
|
|
101
|
+
store.sampling ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
102
|
+
children: [
|
|
103
|
+
/*#__PURE__*/ _jsx(StatGridSkeleton, {}),
|
|
104
|
+
/*#__PURE__*/ _jsx(CardSkeleton, {})
|
|
105
|
+
]
|
|
106
|
+
}) : null,
|
|
107
|
+
!store.sampling && sample ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
108
|
+
children: [
|
|
109
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
110
|
+
className: theme.stat.grid,
|
|
111
|
+
children: [
|
|
112
|
+
/*#__PURE__*/ _jsx(Stat, {
|
|
113
|
+
label: "HTTP status",
|
|
114
|
+
tone: sample.ok ? 'success' : 'danger',
|
|
115
|
+
value: sample.status
|
|
116
|
+
}),
|
|
117
|
+
/*#__PURE__*/ _jsx(Stat, {
|
|
118
|
+
label: "Records read",
|
|
119
|
+
tone: sample.recordCount === 0 ? 'warning' : undefined,
|
|
120
|
+
value: sample.recordCount
|
|
121
|
+
}),
|
|
122
|
+
/*#__PURE__*/ _jsx(Stat, {
|
|
123
|
+
label: "Fields",
|
|
124
|
+
value: sample.fields.length
|
|
125
|
+
}),
|
|
126
|
+
/*#__PURE__*/ _jsx(Stat, {
|
|
127
|
+
label: "Body keys",
|
|
128
|
+
value: sample.bodyKeys.length
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
}),
|
|
132
|
+
sample.error ? /*#__PURE__*/ _jsx(Notice, {
|
|
133
|
+
title: "The source answered with an error",
|
|
134
|
+
tone: "danger",
|
|
135
|
+
children: sample.error
|
|
136
|
+
}) : null,
|
|
137
|
+
sample.recordCount === 0 ? /*#__PURE__*/ _jsxs(Notice, {
|
|
138
|
+
title: "The request succeeded and no records were read",
|
|
139
|
+
tone: "warning",
|
|
140
|
+
children: [
|
|
141
|
+
/*#__PURE__*/ _jsx("p", {
|
|
142
|
+
children: "These are the top-level keys of the body the source returned. Set the response root path to whichever holds the list."
|
|
143
|
+
}),
|
|
144
|
+
sample.bodyKeys.length > 0 ? /*#__PURE__*/ _jsx("ul", {
|
|
145
|
+
className: "mt-2 flex flex-wrap gap-1.5",
|
|
146
|
+
children: sample.bodyKeys.map((key)=>/*#__PURE__*/ _jsx("li", {
|
|
147
|
+
children: /*#__PURE__*/ _jsx(Badge, {
|
|
148
|
+
tone: "info",
|
|
149
|
+
children: key
|
|
150
|
+
})
|
|
151
|
+
}, key))
|
|
152
|
+
}) : /*#__PURE__*/ _jsx("p", {
|
|
153
|
+
className: "mt-2",
|
|
154
|
+
children: "The body has no top-level keys at all: it is empty, or it is a bare value rather than an object."
|
|
155
|
+
}),
|
|
156
|
+
sample.bodyPreview ? /*#__PURE__*/ _jsx("div", {
|
|
157
|
+
className: "mt-2",
|
|
158
|
+
children: /*#__PURE__*/ _jsx(CodeBlock, {
|
|
159
|
+
children: sample.bodyPreview
|
|
160
|
+
})
|
|
161
|
+
}) : null
|
|
162
|
+
]
|
|
163
|
+
}) : null,
|
|
164
|
+
sample.fields.length > 0 ? /*#__PURE__*/ _jsxs("section", {
|
|
165
|
+
className: "flex flex-col gap-1.5",
|
|
166
|
+
children: [
|
|
167
|
+
/*#__PURE__*/ _jsx("h4", {
|
|
168
|
+
className: theme.field.label,
|
|
169
|
+
children: "Fields on the first record"
|
|
170
|
+
}),
|
|
171
|
+
/*#__PURE__*/ _jsx("ul", {
|
|
172
|
+
className: "flex flex-wrap gap-1.5",
|
|
173
|
+
children: sample.fields.map((field)=>/*#__PURE__*/ _jsx("li", {
|
|
174
|
+
children: /*#__PURE__*/ _jsx(Badge, {
|
|
175
|
+
tone: "neutral",
|
|
176
|
+
children: field
|
|
177
|
+
})
|
|
178
|
+
}, field))
|
|
179
|
+
})
|
|
180
|
+
]
|
|
181
|
+
}) : null,
|
|
182
|
+
sample.sample.length > 0 ? /*#__PURE__*/ _jsxs("section", {
|
|
183
|
+
className: "flex flex-col gap-1.5",
|
|
184
|
+
children: [
|
|
185
|
+
/*#__PURE__*/ _jsx("h4", {
|
|
186
|
+
className: theme.field.label,
|
|
187
|
+
children: "Sample records"
|
|
188
|
+
}),
|
|
189
|
+
/*#__PURE__*/ _jsx(CodeBlock, {
|
|
190
|
+
children: JSON.stringify(sample.sample, null, 2)
|
|
191
|
+
})
|
|
192
|
+
]
|
|
193
|
+
}) : null
|
|
194
|
+
]
|
|
195
|
+
}) : null,
|
|
196
|
+
!store.sampling && !sample ? /*#__PURE__*/ _jsx("p", {
|
|
197
|
+
className: theme.field.hint,
|
|
198
|
+
children: "Nothing has been sampled yet. One request is sent, read-only, and the shape it comes back with is what the field mapping is built from."
|
|
199
|
+
}) : null
|
|
200
|
+
]
|
|
201
|
+
})
|
|
202
|
+
]
|
|
203
|
+
});
|
|
204
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useEffectEvent, useState } from 'react';
|
|
4
|
+
import { useIntegrationsStore } from '../store';
|
|
5
|
+
import { integrationsPageTheme } from '../theme';
|
|
6
|
+
import { EndpointForm } from './EndpointForm';
|
|
7
|
+
import { EndpointList } from './EndpointList';
|
|
8
|
+
import { EndpointSample } from './EndpointSample';
|
|
9
|
+
/**
|
|
10
|
+
* The endpoints section: the list on one side, the selected call on the other.
|
|
11
|
+
*
|
|
12
|
+
* The record the list hands over is the record the form edits, so opening an
|
|
13
|
+
* endpoint costs no extra request. The form is keyed by that record's id, which
|
|
14
|
+
* is what makes switching selection a fresh form rather than a merged one.
|
|
15
|
+
*/ const theme = integrationsPageTheme;
|
|
16
|
+
export function EndpointsTab({ actions }) {
|
|
17
|
+
const store = useIntegrationsStore();
|
|
18
|
+
const sourceId = store.selectedSourceId;
|
|
19
|
+
const [selected, setSelected] = useState(null);
|
|
20
|
+
const [creating, setCreating] = useState(false);
|
|
21
|
+
const [reloadToken, setReloadToken] = useState(0);
|
|
22
|
+
// Another connection is another set of endpoints. A form left open across the
|
|
23
|
+
// change would be editing a record this connection does not own.
|
|
24
|
+
const reset = useEffectEvent(()=>{
|
|
25
|
+
setSelected(null);
|
|
26
|
+
setCreating(false);
|
|
27
|
+
});
|
|
28
|
+
useEffect(()=>{
|
|
29
|
+
reset();
|
|
30
|
+
}, [
|
|
31
|
+
sourceId
|
|
32
|
+
]);
|
|
33
|
+
const handleNew = ()=>{
|
|
34
|
+
setSelected(null);
|
|
35
|
+
setCreating(true);
|
|
36
|
+
};
|
|
37
|
+
const handleSelect = (endpoint)=>{
|
|
38
|
+
setSelected(endpoint);
|
|
39
|
+
setCreating(false);
|
|
40
|
+
};
|
|
41
|
+
const handleSaved = (saved)=>{
|
|
42
|
+
setSelected(saved);
|
|
43
|
+
setCreating(false);
|
|
44
|
+
// Re-selecting drops the old sample: it described the endpoint as it was
|
|
45
|
+
// configured a moment ago, and the mapping would be built from it.
|
|
46
|
+
store.selectEndpoint(saved.id);
|
|
47
|
+
setReloadToken((token)=>token + 1);
|
|
48
|
+
};
|
|
49
|
+
const handleDeleted = ()=>{
|
|
50
|
+
setSelected(null);
|
|
51
|
+
setCreating(false);
|
|
52
|
+
store.selectEndpoint(null);
|
|
53
|
+
setReloadToken((token)=>token + 1);
|
|
54
|
+
};
|
|
55
|
+
const handleCancel = ()=>{
|
|
56
|
+
setSelected(null);
|
|
57
|
+
setCreating(false);
|
|
58
|
+
store.selectEndpoint(null);
|
|
59
|
+
};
|
|
60
|
+
if (!sourceId) {
|
|
61
|
+
return /*#__PURE__*/ _jsx("p", {
|
|
62
|
+
className: theme.list.empty,
|
|
63
|
+
children: "Select a connection to see its endpoints."
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Guarded against the store: the selection is the store's, and a record left
|
|
67
|
+
// over from the previous one must not be what the form shows.
|
|
68
|
+
const editing = selected && selected.id === store.selectedEndpointId ? selected : null;
|
|
69
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
70
|
+
className: "grid grid-cols-1 gap-4 xl:grid-cols-[minmax(0,18rem)_minmax(0,1fr)] xl:gap-5",
|
|
71
|
+
children: [
|
|
72
|
+
/*#__PURE__*/ _jsx(EndpointList, {
|
|
73
|
+
actions: actions,
|
|
74
|
+
onNewEndpoint: handleNew,
|
|
75
|
+
onSelect: handleSelect,
|
|
76
|
+
reloadToken: reloadToken,
|
|
77
|
+
sourceId: sourceId
|
|
78
|
+
}, sourceId),
|
|
79
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
80
|
+
className: theme.layout.main,
|
|
81
|
+
children: [
|
|
82
|
+
editing || creating ? /*#__PURE__*/ _jsx(EndpointForm, {
|
|
83
|
+
actions: actions,
|
|
84
|
+
endpoint: editing,
|
|
85
|
+
onCancel: handleCancel,
|
|
86
|
+
onDeleted: handleDeleted,
|
|
87
|
+
onSaved: handleSaved,
|
|
88
|
+
sourceId: sourceId
|
|
89
|
+
}, editing?.id ?? 'new') : /*#__PURE__*/ _jsx("p", {
|
|
90
|
+
className: theme.list.empty,
|
|
91
|
+
children: "Select an endpoint to edit it, or add one. An endpoint is a single call — a method, a path, and where in the answer the records sit."
|
|
92
|
+
}),
|
|
93
|
+
editing ? /*#__PURE__*/ _jsx(EndpointSample, {
|
|
94
|
+
actions: actions,
|
|
95
|
+
endpoint: editing
|
|
96
|
+
}, editing.id) : null
|
|
97
|
+
]
|
|
98
|
+
})
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FieldMappingValue } from '../types';
|
|
2
|
+
export type FieldMappingRowProps = {
|
|
3
|
+
row: FieldMappingValue;
|
|
4
|
+
/** Position in the list, from zero. Also what the labels count from. */
|
|
5
|
+
index: number;
|
|
6
|
+
/** Id of the datalist holding the sampled source field names. */
|
|
7
|
+
sourceListId: string;
|
|
8
|
+
/** Columns of the chosen target entity. Empty until an entity is chosen. */
|
|
9
|
+
targetFields: string[];
|
|
10
|
+
isFirst: boolean;
|
|
11
|
+
isLast: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
onPatch: (patch: Partial<FieldMappingValue>) => void;
|
|
14
|
+
onMove: (delta: number) => void;
|
|
15
|
+
onRemove: () => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function FieldMappingRow({ row, index, sourceListId, targetFields, isFirst, isLast, disabled, onPatch, onMove, onRemove, }: FieldMappingRowProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cn } from '../../../utils/cn';
|
|
4
|
+
import { integrationsPageTheme } from '../theme';
|
|
5
|
+
import { FIELD_MAPPING_GRID, PARENT_PREFIX, TRANSFORM_LABEL, TRANSFORMS, toTransform } from './fieldMappingOptions';
|
|
6
|
+
/**
|
|
7
|
+
* One line of a field mapping: where the value comes from, what it becomes, and
|
|
8
|
+
* which column it lands in.
|
|
9
|
+
*
|
|
10
|
+
* The row owns no state. It reports a patch and the editor above decides what
|
|
11
|
+
* the list of rows now is, so ordering and validation live in one place instead
|
|
12
|
+
* of being spread across however many rows are on screen.
|
|
13
|
+
*/ const theme = integrationsPageTheme;
|
|
14
|
+
/** Label above each control on a phone; the header row replaces it from lg up. */ function Cell({ id, label, children }) {
|
|
15
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
16
|
+
className: theme.field.wrapper,
|
|
17
|
+
children: [
|
|
18
|
+
/*#__PURE__*/ _jsx("label", {
|
|
19
|
+
className: cn(theme.field.label, 'lg:sr-only'),
|
|
20
|
+
htmlFor: id,
|
|
21
|
+
children: label
|
|
22
|
+
}),
|
|
23
|
+
children
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirst, isLast, disabled, onPatch, onMove, onRemove }) {
|
|
28
|
+
const base = `${sourceListId}-${index}`;
|
|
29
|
+
return /*#__PURE__*/ _jsxs("li", {
|
|
30
|
+
className: cn(theme.card.wrapper, 'p-3'),
|
|
31
|
+
children: [
|
|
32
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
33
|
+
className: FIELD_MAPPING_GRID,
|
|
34
|
+
children: [
|
|
35
|
+
/*#__PURE__*/ _jsx(Cell, {
|
|
36
|
+
id: `${base}-source`,
|
|
37
|
+
label: "Source",
|
|
38
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
39
|
+
className: theme.field.input,
|
|
40
|
+
disabled: disabled,
|
|
41
|
+
id: `${base}-source`,
|
|
42
|
+
list: sourceListId,
|
|
43
|
+
onChange: (event)=>onPatch({
|
|
44
|
+
source: event.target.value
|
|
45
|
+
}),
|
|
46
|
+
placeholder: "employeeNo",
|
|
47
|
+
value: row.source
|
|
48
|
+
})
|
|
49
|
+
}),
|
|
50
|
+
/*#__PURE__*/ _jsx(Cell, {
|
|
51
|
+
id: `${base}-target`,
|
|
52
|
+
label: "Target",
|
|
53
|
+
children: targetFields.length > 0 ? /*#__PURE__*/ _jsxs("select", {
|
|
54
|
+
className: theme.field.input,
|
|
55
|
+
disabled: disabled,
|
|
56
|
+
id: `${base}-target`,
|
|
57
|
+
onChange: (event)=>onPatch({
|
|
58
|
+
target: event.target.value
|
|
59
|
+
}),
|
|
60
|
+
value: row.target,
|
|
61
|
+
children: [
|
|
62
|
+
/*#__PURE__*/ _jsx("option", {
|
|
63
|
+
value: "",
|
|
64
|
+
children: "Not set"
|
|
65
|
+
}),
|
|
66
|
+
targetFields.map((field)=>/*#__PURE__*/ _jsx("option", {
|
|
67
|
+
value: field,
|
|
68
|
+
children: field
|
|
69
|
+
}, field))
|
|
70
|
+
]
|
|
71
|
+
}) : /*#__PURE__*/ _jsx("input", {
|
|
72
|
+
className: theme.field.input,
|
|
73
|
+
disabled: disabled,
|
|
74
|
+
id: `${base}-target`,
|
|
75
|
+
onChange: (event)=>onPatch({
|
|
76
|
+
target: event.target.value
|
|
77
|
+
}),
|
|
78
|
+
placeholder: "employee_no",
|
|
79
|
+
value: row.target
|
|
80
|
+
})
|
|
81
|
+
}),
|
|
82
|
+
/*#__PURE__*/ _jsx(Cell, {
|
|
83
|
+
id: `${base}-transform`,
|
|
84
|
+
label: "Transform",
|
|
85
|
+
children: /*#__PURE__*/ _jsx("select", {
|
|
86
|
+
className: theme.field.input,
|
|
87
|
+
disabled: disabled,
|
|
88
|
+
id: `${base}-transform`,
|
|
89
|
+
onChange: (event)=>onPatch({
|
|
90
|
+
transform: toTransform(event.target.value)
|
|
91
|
+
}),
|
|
92
|
+
value: row.transform ?? 'none',
|
|
93
|
+
children: TRANSFORMS.map((option)=>/*#__PURE__*/ _jsx("option", {
|
|
94
|
+
value: option,
|
|
95
|
+
children: TRANSFORM_LABEL[option]
|
|
96
|
+
}, option))
|
|
97
|
+
})
|
|
98
|
+
}),
|
|
99
|
+
/*#__PURE__*/ _jsx(Cell, {
|
|
100
|
+
id: `${base}-constant`,
|
|
101
|
+
label: "Constant",
|
|
102
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
103
|
+
className: theme.field.input,
|
|
104
|
+
disabled: disabled,
|
|
105
|
+
id: `${base}-constant`,
|
|
106
|
+
onChange: (event)=>onPatch({
|
|
107
|
+
constant: event.target.value
|
|
108
|
+
}),
|
|
109
|
+
placeholder: "Same value on every record",
|
|
110
|
+
value: row.constant ?? ''
|
|
111
|
+
})
|
|
112
|
+
}),
|
|
113
|
+
/*#__PURE__*/ _jsx(Cell, {
|
|
114
|
+
id: `${base}-fallback`,
|
|
115
|
+
label: "Fallback",
|
|
116
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
117
|
+
className: theme.field.input,
|
|
118
|
+
disabled: disabled,
|
|
119
|
+
id: `${base}-fallback`,
|
|
120
|
+
onChange: (event)=>onPatch({
|
|
121
|
+
fallback: event.target.value
|
|
122
|
+
}),
|
|
123
|
+
placeholder: "Used when the source is empty",
|
|
124
|
+
value: row.fallback ?? ''
|
|
125
|
+
})
|
|
126
|
+
}),
|
|
127
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
128
|
+
className: "flex items-end gap-1",
|
|
129
|
+
children: [
|
|
130
|
+
/*#__PURE__*/ _jsx("button", {
|
|
131
|
+
"aria-label": `Move field ${index + 1} earlier`,
|
|
132
|
+
className: theme.button.ghost,
|
|
133
|
+
disabled: disabled || isFirst,
|
|
134
|
+
onClick: ()=>onMove(-1),
|
|
135
|
+
type: "button",
|
|
136
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
137
|
+
"aria-hidden": "true",
|
|
138
|
+
children: "↑"
|
|
139
|
+
})
|
|
140
|
+
}),
|
|
141
|
+
/*#__PURE__*/ _jsx("button", {
|
|
142
|
+
"aria-label": `Move field ${index + 1} later`,
|
|
143
|
+
className: theme.button.ghost,
|
|
144
|
+
disabled: disabled || isLast,
|
|
145
|
+
onClick: ()=>onMove(1),
|
|
146
|
+
type: "button",
|
|
147
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
148
|
+
"aria-hidden": "true",
|
|
149
|
+
children: "↓"
|
|
150
|
+
})
|
|
151
|
+
}),
|
|
152
|
+
/*#__PURE__*/ _jsx("button", {
|
|
153
|
+
"aria-label": `Remove field ${index + 1}`,
|
|
154
|
+
className: theme.button.ghost,
|
|
155
|
+
disabled: disabled,
|
|
156
|
+
onClick: onRemove,
|
|
157
|
+
type: "button",
|
|
158
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
159
|
+
"aria-hidden": "true",
|
|
160
|
+
children: "✕"
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
]
|
|
164
|
+
})
|
|
165
|
+
]
|
|
166
|
+
}),
|
|
167
|
+
row.source.startsWith(PARENT_PREFIX) ? /*#__PURE__*/ _jsxs("p", {
|
|
168
|
+
className: cn(theme.field.hint, 'mt-2'),
|
|
169
|
+
children: [
|
|
170
|
+
"Reads ",
|
|
171
|
+
/*#__PURE__*/ _jsx("strong", {
|
|
172
|
+
children: row.source.slice(PARENT_PREFIX.length) || 'a field'
|
|
173
|
+
}),
|
|
174
|
+
" from the record the parent endpoint returned — the one this request was built for — not from this response."
|
|
175
|
+
]
|
|
176
|
+
}) : null
|
|
177
|
+
]
|
|
178
|
+
});
|
|
179
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FieldMappingValue } from '../types';
|
|
2
|
+
export type FieldMappingRowsProps = {
|
|
3
|
+
rows: FieldMappingValue[];
|
|
4
|
+
/** Field names the endpoint sample reported. Empty until the endpoint is sampled. */
|
|
5
|
+
sourceFields: string[];
|
|
6
|
+
/** Columns of the chosen target entity. Empty until an entity is chosen. */
|
|
7
|
+
targetFields: string[];
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onChange: (rows: FieldMappingValue[]) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare function FieldMappingRows({ rows, sourceFields, targetFields, disabled, onChange, }: FieldMappingRowsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import { cn } from '../../../utils/cn';
|
|
5
|
+
import { integrationsPageTheme } from '../theme';
|
|
6
|
+
import { FieldMappingRow } from './FieldMappingRow';
|
|
7
|
+
import { EMPTY_FIELD_MAPPING, FIELD_MAPPING_COLUMNS, FIELD_MAPPING_GRID, PARENT_PREFIX } from './fieldMappingOptions';
|
|
8
|
+
import { Notice } from './Primitives';
|
|
9
|
+
/**
|
|
10
|
+
* The rows that say which incoming value becomes which column.
|
|
11
|
+
*
|
|
12
|
+
* The order is kept because the writer applies the rows in it: two rows aimed at
|
|
13
|
+
* the same column mean the later one wins, and that is only predictable if the
|
|
14
|
+
* operator can see the order and change it.
|
|
15
|
+
*/ const theme = integrationsPageTheme;
|
|
16
|
+
export function FieldMappingRows({ rows, sourceFields, targetFields, disabled, onChange }) {
|
|
17
|
+
const uid = useId();
|
|
18
|
+
const listId = `${uid}-source-fields`;
|
|
19
|
+
const patchRow = (index, patch)=>{
|
|
20
|
+
const current = rows[index];
|
|
21
|
+
if (!current) return;
|
|
22
|
+
onChange(rows.with(index, {
|
|
23
|
+
...current,
|
|
24
|
+
...patch
|
|
25
|
+
}));
|
|
26
|
+
};
|
|
27
|
+
const moveRow = (index, delta)=>{
|
|
28
|
+
const target = index + delta;
|
|
29
|
+
const moving = rows[index];
|
|
30
|
+
const displaced = rows[target];
|
|
31
|
+
if (!moving || !displaced) return;
|
|
32
|
+
onChange(rows.with(index, displaced).with(target, moving));
|
|
33
|
+
};
|
|
34
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
35
|
+
className: "flex flex-col gap-3",
|
|
36
|
+
children: [
|
|
37
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
38
|
+
className: "flex flex-wrap items-center justify-between gap-2",
|
|
39
|
+
children: [
|
|
40
|
+
/*#__PURE__*/ _jsx("h3", {
|
|
41
|
+
className: theme.card.title,
|
|
42
|
+
children: "Field mappings"
|
|
43
|
+
}),
|
|
44
|
+
/*#__PURE__*/ _jsx("button", {
|
|
45
|
+
className: theme.button.secondary,
|
|
46
|
+
disabled: disabled,
|
|
47
|
+
onClick: ()=>onChange([
|
|
48
|
+
...rows,
|
|
49
|
+
EMPTY_FIELD_MAPPING
|
|
50
|
+
]),
|
|
51
|
+
type: "button",
|
|
52
|
+
children: "Add field"
|
|
53
|
+
})
|
|
54
|
+
]
|
|
55
|
+
}),
|
|
56
|
+
sourceFields.length === 0 ? /*#__PURE__*/ _jsx(Notice, {
|
|
57
|
+
title: "Sample the endpoint first",
|
|
58
|
+
tone: "warning",
|
|
59
|
+
children: "No field names are known yet. Sampling the selected endpoint lists what the response really returns; until then a source has to be typed from memory, and a typo shows up only as an empty column once the import has already run."
|
|
60
|
+
}) : null,
|
|
61
|
+
/*#__PURE__*/ _jsx("datalist", {
|
|
62
|
+
id: listId,
|
|
63
|
+
children: sourceFields.map((field)=>/*#__PURE__*/ _jsx("option", {
|
|
64
|
+
value: field
|
|
65
|
+
}, field))
|
|
66
|
+
}),
|
|
67
|
+
rows.length === 0 ? /*#__PURE__*/ _jsx("p", {
|
|
68
|
+
className: theme.list.empty,
|
|
69
|
+
children: "Nothing is mapped yet, so a run would write empty records. Add a field to begin."
|
|
70
|
+
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
71
|
+
children: [
|
|
72
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
73
|
+
"aria-hidden": "true",
|
|
74
|
+
className: cn(FIELD_MAPPING_GRID, 'hidden px-3 lg:grid'),
|
|
75
|
+
children: [
|
|
76
|
+
FIELD_MAPPING_COLUMNS.map((column)=>/*#__PURE__*/ _jsx("span", {
|
|
77
|
+
className: theme.field.label,
|
|
78
|
+
children: column
|
|
79
|
+
}, column)),
|
|
80
|
+
/*#__PURE__*/ _jsx("span", {
|
|
81
|
+
className: cn(theme.field.label, 'w-28'),
|
|
82
|
+
children: "Order"
|
|
83
|
+
})
|
|
84
|
+
]
|
|
85
|
+
}),
|
|
86
|
+
/*#__PURE__*/ _jsx("ol", {
|
|
87
|
+
className: "flex flex-col gap-2",
|
|
88
|
+
children: rows.map((row, index)=>/*#__PURE__*/ _jsx(FieldMappingRow, {
|
|
89
|
+
disabled: disabled,
|
|
90
|
+
index: index,
|
|
91
|
+
isFirst: index === 0,
|
|
92
|
+
isLast: index === rows.length - 1,
|
|
93
|
+
onMove: (delta)=>moveRow(index, delta),
|
|
94
|
+
onPatch: (patch)=>patchRow(index, patch),
|
|
95
|
+
onRemove: ()=>onChange(rows.toSpliced(index, 1)),
|
|
96
|
+
row: row,
|
|
97
|
+
sourceListId: listId,
|
|
98
|
+
targetFields: targetFields
|
|
99
|
+
}, `field-mapping-${index}`))
|
|
100
|
+
}),
|
|
101
|
+
/*#__PURE__*/ _jsxs("p", {
|
|
102
|
+
className: theme.field.hint,
|
|
103
|
+
children: [
|
|
104
|
+
"Prefix a source with ",
|
|
105
|
+
/*#__PURE__*/ _jsx("strong", {
|
|
106
|
+
children: PARENT_PREFIX
|
|
107
|
+
}),
|
|
108
|
+
" to take the value from the parent endpoint's record instead of this one."
|
|
109
|
+
]
|
|
110
|
+
})
|
|
111
|
+
]
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
}
|