nucleus-core-ts 0.9.804 → 0.9.806
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/fe/components/IntegrationsPage/components/ConnectionForm.js +4 -2
- package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.js +32 -23
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.js +38 -32
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.js +11 -9
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +4 -2
- package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.js +9 -1
- package/dist/fe/components/IntegrationsPage/components/MappingEditor.js +14 -12
- package/dist/fe/components/IntegrationsPage/components/MappingList.js +6 -4
- package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +14 -12
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +15 -19
- package/dist/fe/components/IntegrationsPage/text/index.d.ts +75 -0
- package/dist/fe/components/IntegrationsPage/text/index.js +74 -1
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useEffectEvent, useState } from 'react';
|
|
4
4
|
import { useIntegrationsStore } from '../store';
|
|
5
|
+
import { useLabels } from '../text/context';
|
|
5
6
|
import { integrationsPageTheme } from '../theme';
|
|
6
7
|
import { ConnectionAuthFields } from './ConnectionAuthFields';
|
|
7
8
|
import { ConnectionBasicFields } from './ConnectionBasicFields';
|
|
@@ -17,6 +18,7 @@ import { CardSkeleton } from './Skeletons';
|
|
|
17
18
|
* of what was stored and what is being typed.
|
|
18
19
|
*/ const theme = integrationsPageTheme;
|
|
19
20
|
export function ConnectionForm({ actions, source, loading = false, onSaved }) {
|
|
21
|
+
const labels = useLabels();
|
|
20
22
|
const store = useIntegrationsStore();
|
|
21
23
|
const [draft, setDraft] = useState(()=>source ? draftFromSource(source) : emptyConnectionDraft);
|
|
22
24
|
const [errors, setErrors] = useState({});
|
|
@@ -73,11 +75,11 @@ export function ConnectionForm({ actions, source, loading = false, onSaved }) {
|
|
|
73
75
|
children: [
|
|
74
76
|
/*#__PURE__*/ _jsx("h2", {
|
|
75
77
|
className: theme.card.title,
|
|
76
|
-
children: source ?
|
|
78
|
+
children: source ? labels.connections.editTitle : labels.connections.newTitle
|
|
77
79
|
}),
|
|
78
80
|
source ? /*#__PURE__*/ _jsx(Badge, {
|
|
79
81
|
tone: source.enabled === false ? 'neutral' : 'success',
|
|
80
|
-
children: source.enabled === false ?
|
|
82
|
+
children: source.enabled === false ? labels.connections.disabledBadge : labels.connections.enabledBadge
|
|
81
83
|
}) : null
|
|
82
84
|
]
|
|
83
85
|
}),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
3
4
|
import { cn } from '../../../utils/cn';
|
|
4
5
|
import { useLabels } from '../text/context';
|
|
5
6
|
import { integrationsPageTheme } from '../theme';
|
|
@@ -20,6 +21,14 @@ const METHODS = [
|
|
|
20
21
|
];
|
|
21
22
|
export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
22
23
|
const labels = useLabels();
|
|
24
|
+
/**
|
|
25
|
+
* Unique per instance.
|
|
26
|
+
*
|
|
27
|
+
* These ids were literals — `endpoint-tag`, `endpoint-name` — which is fine
|
|
28
|
+
* for one form on a page and wrong the moment there are two: every `<label
|
|
29
|
+
* htmlFor>` binds to whichever rendered first, so clicking a label focuses the
|
|
30
|
+
* OTHER record's field. `useId` makes that structurally impossible.
|
|
31
|
+
*/ const uid = useId();
|
|
23
32
|
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
24
33
|
children: [
|
|
25
34
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -27,13 +36,13 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
27
36
|
children: [
|
|
28
37
|
/*#__PURE__*/ _jsx(Field, {
|
|
29
38
|
hint: labels.endpoints.tagHint,
|
|
30
|
-
htmlFor:
|
|
39
|
+
htmlFor: `${uid}-tag`,
|
|
31
40
|
label: labels.endpoints.tag,
|
|
32
41
|
children: /*#__PURE__*/ _jsx("input", {
|
|
33
|
-
"aria-describedby":
|
|
42
|
+
"aria-describedby": `${uid}-tag-hint`,
|
|
34
43
|
className: theme.field.input,
|
|
35
44
|
disabled: disabled,
|
|
36
|
-
id:
|
|
45
|
+
id: `${uid}-tag`,
|
|
37
46
|
onChange: (event)=>onChange({
|
|
38
47
|
tag: event.target.value
|
|
39
48
|
}),
|
|
@@ -43,12 +52,12 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
43
52
|
})
|
|
44
53
|
}),
|
|
45
54
|
/*#__PURE__*/ _jsx(Field, {
|
|
46
|
-
htmlFor:
|
|
55
|
+
htmlFor: `${uid}-name`,
|
|
47
56
|
label: labels.endpoints.name,
|
|
48
57
|
children: /*#__PURE__*/ _jsx("input", {
|
|
49
58
|
className: theme.field.input,
|
|
50
59
|
disabled: disabled,
|
|
51
|
-
id:
|
|
60
|
+
id: `${uid}-name`,
|
|
52
61
|
onChange: (event)=>onChange({
|
|
53
62
|
name: event.target.value
|
|
54
63
|
}),
|
|
@@ -65,12 +74,12 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
65
74
|
/*#__PURE__*/ _jsx("div", {
|
|
66
75
|
className: "sm:w-32",
|
|
67
76
|
children: /*#__PURE__*/ _jsx(Field, {
|
|
68
|
-
htmlFor:
|
|
77
|
+
htmlFor: `${uid}-method`,
|
|
69
78
|
label: labels.endpoints.method,
|
|
70
79
|
children: /*#__PURE__*/ _jsx("select", {
|
|
71
80
|
className: theme.field.input,
|
|
72
81
|
disabled: disabled,
|
|
73
|
-
id:
|
|
82
|
+
id: `${uid}-method`,
|
|
74
83
|
onChange: (event)=>{
|
|
75
84
|
for (const method of METHODS){
|
|
76
85
|
if (method === event.target.value) onChange({
|
|
@@ -90,13 +99,13 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
90
99
|
className: "min-w-0 flex-1",
|
|
91
100
|
children: /*#__PURE__*/ _jsx(Field, {
|
|
92
101
|
hint: labels.endpoints.pathHint,
|
|
93
|
-
htmlFor:
|
|
102
|
+
htmlFor: `${uid}-path`,
|
|
94
103
|
label: labels.endpoints.path,
|
|
95
104
|
children: /*#__PURE__*/ _jsx("input", {
|
|
96
|
-
"aria-describedby":
|
|
105
|
+
"aria-describedby": `${uid}-path-hint`,
|
|
97
106
|
className: theme.field.input,
|
|
98
107
|
disabled: disabled,
|
|
99
|
-
id:
|
|
108
|
+
id: `${uid}-path`,
|
|
100
109
|
onChange: (event)=>onChange({
|
|
101
110
|
path: event.target.value
|
|
102
111
|
}),
|
|
@@ -110,13 +119,13 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
110
119
|
}),
|
|
111
120
|
/*#__PURE__*/ _jsx(Field, {
|
|
112
121
|
hint: labels.endpoints.rootPathHint,
|
|
113
|
-
htmlFor:
|
|
122
|
+
htmlFor: `${uid}-root`,
|
|
114
123
|
label: labels.endpoints.rootPath,
|
|
115
124
|
children: /*#__PURE__*/ _jsx("input", {
|
|
116
|
-
"aria-describedby":
|
|
125
|
+
"aria-describedby": `${uid}-root-hint`,
|
|
117
126
|
className: theme.field.input,
|
|
118
127
|
disabled: disabled,
|
|
119
|
-
id:
|
|
128
|
+
id: `${uid}-root`,
|
|
120
129
|
onChange: (event)=>onChange({
|
|
121
130
|
responseRootPath: event.target.value
|
|
122
131
|
}),
|
|
@@ -128,13 +137,13 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
128
137
|
/*#__PURE__*/ _jsx(Field, {
|
|
129
138
|
error: errors?.queryParams,
|
|
130
139
|
hint: labels.endpoints.queryParamsHint,
|
|
131
|
-
htmlFor:
|
|
140
|
+
htmlFor: `${uid}-query`,
|
|
132
141
|
label: labels.endpoints.queryParams,
|
|
133
142
|
children: /*#__PURE__*/ _jsx("textarea", {
|
|
134
|
-
"aria-describedby": describedBy(
|
|
143
|
+
"aria-describedby": describedBy(`${uid}-query`, true, Boolean(errors?.queryParams)),
|
|
135
144
|
className: cn(theme.field.input, 'h-auto min-h-16 py-2 font-mono text-xs'),
|
|
136
145
|
disabled: disabled,
|
|
137
|
-
id:
|
|
146
|
+
id: `${uid}-query`,
|
|
138
147
|
onChange: (event)=>onChange({
|
|
139
148
|
queryParams: event.target.value
|
|
140
149
|
}),
|
|
@@ -146,13 +155,13 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
146
155
|
draft.method === 'GET' ? null : /*#__PURE__*/ _jsx(Field, {
|
|
147
156
|
error: errors?.requestBody,
|
|
148
157
|
hint: labels.endpoints.requestBodyHint,
|
|
149
|
-
htmlFor:
|
|
158
|
+
htmlFor: `${uid}-body`,
|
|
150
159
|
label: labels.endpoints.requestBody,
|
|
151
160
|
children: /*#__PURE__*/ _jsx("textarea", {
|
|
152
|
-
"aria-describedby": describedBy(
|
|
161
|
+
"aria-describedby": describedBy(`${uid}-body`, true, Boolean(errors?.requestBody)),
|
|
153
162
|
className: cn(theme.field.input, 'h-auto min-h-20 py-2 font-mono text-xs'),
|
|
154
163
|
disabled: disabled,
|
|
155
|
-
id:
|
|
164
|
+
id: `${uid}-body`,
|
|
156
165
|
onChange: (event)=>onChange({
|
|
157
166
|
requestBody: event.target.value
|
|
158
167
|
}),
|
|
@@ -162,12 +171,12 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
162
171
|
})
|
|
163
172
|
}),
|
|
164
173
|
/*#__PURE__*/ _jsx(Field, {
|
|
165
|
-
htmlFor:
|
|
174
|
+
htmlFor: `${uid}-description`,
|
|
166
175
|
label: labels.endpoints.description,
|
|
167
176
|
children: /*#__PURE__*/ _jsx("textarea", {
|
|
168
177
|
className: cn(theme.field.input, 'h-auto min-h-20 py-2'),
|
|
169
178
|
disabled: disabled,
|
|
170
|
-
id:
|
|
179
|
+
id: `${uid}-description`,
|
|
171
180
|
onChange: (event)=>onChange({
|
|
172
181
|
description: event.target.value
|
|
173
182
|
}),
|
|
@@ -178,13 +187,13 @@ export function EndpointFormFields({ draft, disabled, errors, onChange }) {
|
|
|
178
187
|
}),
|
|
179
188
|
/*#__PURE__*/ _jsxs("label", {
|
|
180
189
|
className: "flex items-center gap-2",
|
|
181
|
-
htmlFor:
|
|
190
|
+
htmlFor: `${uid}-enabled`,
|
|
182
191
|
children: [
|
|
183
192
|
/*#__PURE__*/ _jsx("input", {
|
|
184
193
|
checked: draft.enabled,
|
|
185
194
|
className: "size-4 shrink-0 accent-sky-600 dark:accent-sky-500",
|
|
186
195
|
disabled: disabled,
|
|
187
|
-
id:
|
|
196
|
+
id: `${uid}-enabled`,
|
|
188
197
|
onChange: (event)=>onChange({
|
|
189
198
|
enabled: event.target.checked
|
|
190
199
|
}),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useEffectEvent, useState } from 'react';
|
|
4
4
|
import { useIntegrationsStore } from '../store';
|
|
5
5
|
import { useLabels } from '../text/context';
|
|
@@ -18,14 +18,24 @@ export function EndpointsTab({ actions }) {
|
|
|
18
18
|
const labels = useLabels();
|
|
19
19
|
const store = useIntegrationsStore();
|
|
20
20
|
const sourceId = store.selectedSourceId;
|
|
21
|
-
const [selected, setSelected] = useState(null);
|
|
22
21
|
const [creating, setCreating] = useState(false);
|
|
23
22
|
const [reloadToken, setReloadToken] = useState(0);
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The selection is the STORE's, and only the store's.
|
|
25
|
+
*
|
|
26
|
+
* It used to be kept here as well, and the two were reconciled by comparing
|
|
27
|
+
* them on every render. Clicking a second call then left the first form on
|
|
28
|
+
* screen and added the new one under it — two live editors of two different
|
|
29
|
+
* records, both writing to the same hard-coded field ids, growing by one card
|
|
30
|
+
* per click. One source of truth, and one keyed container below, is what makes
|
|
31
|
+
* that impossible rather than unlikely.
|
|
32
|
+
*/ const selectedId = store.selectedEndpointId;
|
|
33
|
+
const editing = selectedId ? store.endpoints.find((e)=>e.id === selectedId) ?? null : null;
|
|
34
|
+
// Another connection is another set of calls. A form left open across the
|
|
25
35
|
// change would be editing a record this connection does not own.
|
|
26
36
|
const reset = useEffectEvent(()=>{
|
|
27
|
-
setSelected(null);
|
|
28
37
|
setCreating(false);
|
|
38
|
+
store.selectEndpoint(null);
|
|
29
39
|
});
|
|
30
40
|
useEffect(()=>{
|
|
31
41
|
reset();
|
|
@@ -33,29 +43,26 @@ export function EndpointsTab({ actions }) {
|
|
|
33
43
|
sourceId
|
|
34
44
|
]);
|
|
35
45
|
const handleNew = ()=>{
|
|
36
|
-
|
|
46
|
+
store.selectEndpoint(null);
|
|
37
47
|
setCreating(true);
|
|
38
48
|
};
|
|
39
49
|
const handleSelect = (endpoint)=>{
|
|
40
|
-
setSelected(endpoint);
|
|
41
50
|
setCreating(false);
|
|
51
|
+
store.selectEndpoint(endpoint.id);
|
|
42
52
|
};
|
|
43
53
|
const handleSaved = (saved)=>{
|
|
44
|
-
setSelected(saved);
|
|
45
54
|
setCreating(false);
|
|
46
|
-
// Re-selecting drops the old sample: it described the
|
|
55
|
+
// Re-selecting drops the old sample: it described the call as it was
|
|
47
56
|
// configured a moment ago, and the mapping would be built from it.
|
|
48
57
|
store.selectEndpoint(saved.id);
|
|
49
58
|
setReloadToken((token)=>token + 1);
|
|
50
59
|
};
|
|
51
60
|
const handleDeleted = ()=>{
|
|
52
|
-
setSelected(null);
|
|
53
61
|
setCreating(false);
|
|
54
62
|
store.selectEndpoint(null);
|
|
55
63
|
setReloadToken((token)=>token + 1);
|
|
56
64
|
};
|
|
57
65
|
const handleCancel = ()=>{
|
|
58
|
-
setSelected(null);
|
|
59
66
|
setCreating(false);
|
|
60
67
|
store.selectEndpoint(null);
|
|
61
68
|
};
|
|
@@ -65,9 +72,6 @@ export function EndpointsTab({ actions }) {
|
|
|
65
72
|
children: labels.endpoints.selectConnection
|
|
66
73
|
});
|
|
67
74
|
}
|
|
68
|
-
// Guarded against the store: the selection is the store's, and a record left
|
|
69
|
-
// over from the previous one must not be what the form shows.
|
|
70
|
-
const editing = selected && selected.id === store.selectedEndpointId ? selected : null;
|
|
71
75
|
return /*#__PURE__*/ _jsxs("div", {
|
|
72
76
|
className: "grid grid-cols-1 gap-4 xl:grid-cols-[minmax(0,18rem)_minmax(0,1fr)] xl:gap-5",
|
|
73
77
|
children: [
|
|
@@ -78,26 +82,28 @@ export function EndpointsTab({ actions }) {
|
|
|
78
82
|
reloadToken: reloadToken,
|
|
79
83
|
sourceId: sourceId
|
|
80
84
|
}, sourceId),
|
|
81
|
-
/*#__PURE__*/
|
|
85
|
+
/*#__PURE__*/ _jsx("div", {
|
|
82
86
|
className: theme.layout.main,
|
|
83
|
-
children:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
children: editing || creating ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
88
|
+
children: [
|
|
89
|
+
/*#__PURE__*/ _jsx(EndpointForm, {
|
|
90
|
+
actions: actions,
|
|
91
|
+
endpoint: editing,
|
|
92
|
+
onCancel: handleCancel,
|
|
93
|
+
onDeleted: handleDeleted,
|
|
94
|
+
onSaved: handleSaved,
|
|
95
|
+
sourceId: sourceId
|
|
96
|
+
}),
|
|
97
|
+
editing ? /*#__PURE__*/ _jsx(EndpointSample, {
|
|
98
|
+
actions: actions,
|
|
99
|
+
endpoint: editing
|
|
100
|
+
}) : null
|
|
101
|
+
]
|
|
102
|
+
}) : /*#__PURE__*/ _jsx("p", {
|
|
103
|
+
className: theme.list.empty,
|
|
104
|
+
children: labels.endpoints.pickOne
|
|
105
|
+
})
|
|
106
|
+
}, editing?.id ?? (creating ? 'new' : 'none'))
|
|
101
107
|
]
|
|
102
108
|
});
|
|
103
109
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { cn } from '../../../utils/cn';
|
|
4
|
+
import { useLabels } from '../text/context';
|
|
4
5
|
import { integrationsPageTheme } from '../theme';
|
|
5
6
|
import { FIELD_MAPPING_GRID, PARENT_PREFIX, TRANSFORM_LABEL, TRANSFORMS, toTransform } from './fieldMappingOptions';
|
|
6
7
|
/**
|
|
@@ -25,6 +26,7 @@ import { FIELD_MAPPING_GRID, PARENT_PREFIX, TRANSFORM_LABEL, TRANSFORMS, toTrans
|
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirst, isLast, disabled, onPatch, onMove, onRemove }) {
|
|
29
|
+
const labels = useLabels();
|
|
28
30
|
const base = `${sourceListId}-${index}`;
|
|
29
31
|
return /*#__PURE__*/ _jsxs("li", {
|
|
30
32
|
className: cn(theme.card.wrapper, 'p-3'),
|
|
@@ -34,7 +36,7 @@ export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirs
|
|
|
34
36
|
children: [
|
|
35
37
|
/*#__PURE__*/ _jsx(Cell, {
|
|
36
38
|
id: `${base}-source`,
|
|
37
|
-
label:
|
|
39
|
+
label: labels.mappings.source,
|
|
38
40
|
children: /*#__PURE__*/ _jsx("input", {
|
|
39
41
|
className: theme.field.input,
|
|
40
42
|
disabled: disabled,
|
|
@@ -43,13 +45,13 @@ export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirs
|
|
|
43
45
|
onChange: (event)=>onPatch({
|
|
44
46
|
source: event.target.value
|
|
45
47
|
}),
|
|
46
|
-
placeholder:
|
|
48
|
+
placeholder: labels.mappings.sourcePlaceholder,
|
|
47
49
|
value: row.source
|
|
48
50
|
})
|
|
49
51
|
}),
|
|
50
52
|
/*#__PURE__*/ _jsx(Cell, {
|
|
51
53
|
id: `${base}-target`,
|
|
52
|
-
label:
|
|
54
|
+
label: labels.mappings.targetColumn,
|
|
53
55
|
children: targetFields.length > 0 ? /*#__PURE__*/ _jsxs("select", {
|
|
54
56
|
className: theme.field.input,
|
|
55
57
|
disabled: disabled,
|
|
@@ -75,13 +77,13 @@ export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirs
|
|
|
75
77
|
onChange: (event)=>onPatch({
|
|
76
78
|
target: event.target.value
|
|
77
79
|
}),
|
|
78
|
-
placeholder:
|
|
80
|
+
placeholder: labels.mappings.sourcePlaceholder,
|
|
79
81
|
value: row.target
|
|
80
82
|
})
|
|
81
83
|
}),
|
|
82
84
|
/*#__PURE__*/ _jsx(Cell, {
|
|
83
85
|
id: `${base}-transform`,
|
|
84
|
-
label:
|
|
86
|
+
label: labels.mappings.transform,
|
|
85
87
|
children: /*#__PURE__*/ _jsx("select", {
|
|
86
88
|
className: theme.field.input,
|
|
87
89
|
disabled: disabled,
|
|
@@ -98,7 +100,7 @@ export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirs
|
|
|
98
100
|
}),
|
|
99
101
|
/*#__PURE__*/ _jsx(Cell, {
|
|
100
102
|
id: `${base}-constant`,
|
|
101
|
-
label:
|
|
103
|
+
label: labels.mappings.constant,
|
|
102
104
|
children: /*#__PURE__*/ _jsx("input", {
|
|
103
105
|
className: theme.field.input,
|
|
104
106
|
disabled: disabled,
|
|
@@ -106,13 +108,13 @@ export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirs
|
|
|
106
108
|
onChange: (event)=>onPatch({
|
|
107
109
|
constant: event.target.value
|
|
108
110
|
}),
|
|
109
|
-
placeholder:
|
|
111
|
+
placeholder: labels.mappings.constantPlaceholder,
|
|
110
112
|
value: row.constant ?? ''
|
|
111
113
|
})
|
|
112
114
|
}),
|
|
113
115
|
/*#__PURE__*/ _jsx(Cell, {
|
|
114
116
|
id: `${base}-fallback`,
|
|
115
|
-
label:
|
|
117
|
+
label: labels.mappings.fallback,
|
|
116
118
|
children: /*#__PURE__*/ _jsx("input", {
|
|
117
119
|
className: theme.field.input,
|
|
118
120
|
disabled: disabled,
|
|
@@ -120,7 +122,7 @@ export function FieldMappingRow({ row, index, sourceListId, targetFields, isFirs
|
|
|
120
122
|
onChange: (event)=>onPatch({
|
|
121
123
|
fallback: event.target.value
|
|
122
124
|
}),
|
|
123
|
-
placeholder:
|
|
125
|
+
placeholder: labels.mappings.fallbackPlaceholder,
|
|
124
126
|
value: row.fallback ?? ''
|
|
125
127
|
})
|
|
126
128
|
}),
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useId } from 'react';
|
|
4
4
|
import { cn } from '../../../utils/cn';
|
|
5
|
+
import { useLabels } from '../text/context';
|
|
5
6
|
import { integrationsPageTheme } from '../theme';
|
|
6
7
|
import { FieldMappingRow } from './FieldMappingRow';
|
|
7
8
|
import { EMPTY_FIELD_MAPPING, FIELD_MAPPING_COLUMNS, FIELD_MAPPING_GRID, PARENT_PREFIX } from './fieldMappingOptions';
|
|
@@ -14,6 +15,7 @@ import { Notice } from './Primitives';
|
|
|
14
15
|
* operator can see the order and change it.
|
|
15
16
|
*/ const theme = integrationsPageTheme;
|
|
16
17
|
export function FieldMappingRows({ rows, sourceFields, targetFields, disabled, onChange }) {
|
|
18
|
+
const labels = useLabels();
|
|
17
19
|
const uid = useId();
|
|
18
20
|
const listId = `${uid}-source-fields`;
|
|
19
21
|
const patchRow = (index, patch)=>{
|
|
@@ -39,7 +41,7 @@ export function FieldMappingRows({ rows, sourceFields, targetFields, disabled, o
|
|
|
39
41
|
children: [
|
|
40
42
|
/*#__PURE__*/ _jsx("h3", {
|
|
41
43
|
className: theme.card.title,
|
|
42
|
-
children:
|
|
44
|
+
children: labels.mappings.fieldsHeading
|
|
43
45
|
}),
|
|
44
46
|
/*#__PURE__*/ _jsx("button", {
|
|
45
47
|
className: theme.button.secondary,
|
|
@@ -54,7 +56,7 @@ export function FieldMappingRows({ rows, sourceFields, targetFields, disabled, o
|
|
|
54
56
|
]
|
|
55
57
|
}),
|
|
56
58
|
sourceFields.length === 0 ? /*#__PURE__*/ _jsx(Notice, {
|
|
57
|
-
title:
|
|
59
|
+
title: labels.mappings.sampleFirstTitle,
|
|
58
60
|
tone: "warning",
|
|
59
61
|
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
62
|
}) : null,
|
|
@@ -60,7 +60,15 @@ export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect a
|
|
|
60
60
|
limit: 1
|
|
61
61
|
};
|
|
62
62
|
const total = (result)=>result.meta?.totalItems ?? 0;
|
|
63
|
-
|
|
63
|
+
// A PAGE of endpoints, not a count. The strip says how many calls have been
|
|
64
|
+
// tried, and "tried" lives on the row — counting alone reported "5 calls,
|
|
65
|
+
// 0 tried" for five endpoints that had all been sampled. It also warms the
|
|
66
|
+
// store for every tab, which is what made the mappings tab depend on having
|
|
67
|
+
// visited the endpoints tab first.
|
|
68
|
+
actions.listEndpoints(sourceId, {
|
|
69
|
+
page: 1,
|
|
70
|
+
limit: 50
|
|
71
|
+
}).then((r)=>store.setEndpoints(r.items, r.meta?.totalItems ?? r.items.length)).catch(()=>{});
|
|
64
72
|
actions.listMappings(sourceId, one).then((r)=>store.setMappingsTotal(total(r))).catch(()=>{});
|
|
65
73
|
// Runs are deliberately NOT counted here. A run records the mapping it came
|
|
66
74
|
// from and nothing about the connection, so the only count available is
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useId, useState } from 'react';
|
|
4
|
+
import { useLabels } from '../text/context';
|
|
4
5
|
import { integrationsPageTheme } from '../theme';
|
|
5
6
|
import { FieldMappingRows } from './FieldMappingRows';
|
|
6
7
|
import { endpointLabel, fieldsOfStoredSample } from './formSupport';
|
|
@@ -19,6 +20,7 @@ import { CardSkeleton } from './Skeletons';
|
|
|
19
20
|
* draft instead of pouring one mapping's edits into another.
|
|
20
21
|
*/ const theme = integrationsPageTheme;
|
|
21
22
|
export function MappingEditor({ mapping, targets, targetsLoading, endpoints, freshSample, saving, deleting, onSave, onDelete, onCancel }) {
|
|
23
|
+
const labels = useLabels();
|
|
22
24
|
const uid = useId();
|
|
23
25
|
const [draft, setDraft] = useState(mapping);
|
|
24
26
|
const patch = (next)=>{
|
|
@@ -57,16 +59,16 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
57
59
|
className: theme.card.header,
|
|
58
60
|
children: /*#__PURE__*/ _jsx("h2", {
|
|
59
61
|
className: theme.card.title,
|
|
60
|
-
children: draft.id ? draft.name ||
|
|
62
|
+
children: draft.id ? draft.name || labels.mappings.editTitle : labels.mappings.newTitle
|
|
61
63
|
})
|
|
62
64
|
}),
|
|
63
65
|
/*#__PURE__*/ _jsxs("div", {
|
|
64
66
|
className: theme.card.body,
|
|
65
67
|
children: [
|
|
66
68
|
/*#__PURE__*/ _jsx(Field, {
|
|
67
|
-
hint: endpoints.length === 0 ?
|
|
69
|
+
hint: endpoints.length === 0 ? labels.mappings.noEndpointsBody : readsFrom?.path ?? labels.mappings.readsFromHint,
|
|
68
70
|
htmlFor: `${uid}-endpoint`,
|
|
69
|
-
label:
|
|
71
|
+
label: labels.mappings.readsFrom,
|
|
70
72
|
children: /*#__PURE__*/ _jsxs("select", {
|
|
71
73
|
className: theme.field.input,
|
|
72
74
|
disabled: busy || endpoints.length === 0,
|
|
@@ -78,7 +80,7 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
78
80
|
children: [
|
|
79
81
|
/*#__PURE__*/ _jsx("option", {
|
|
80
82
|
value: "",
|
|
81
|
-
children:
|
|
83
|
+
children: labels.mappings.notSet
|
|
82
84
|
}),
|
|
83
85
|
endpoints.map((endpoint)=>/*#__PURE__*/ _jsx("option", {
|
|
84
86
|
value: endpoint.id,
|
|
@@ -92,7 +94,7 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
92
94
|
children: [
|
|
93
95
|
/*#__PURE__*/ _jsx(Field, {
|
|
94
96
|
htmlFor: `${uid}-name`,
|
|
95
|
-
label:
|
|
97
|
+
label: labels.mappings.name,
|
|
96
98
|
children: /*#__PURE__*/ _jsx("input", {
|
|
97
99
|
className: theme.field.input,
|
|
98
100
|
disabled: busy,
|
|
@@ -100,14 +102,14 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
100
102
|
onChange: (event)=>patch({
|
|
101
103
|
name: event.target.value
|
|
102
104
|
}),
|
|
103
|
-
placeholder:
|
|
105
|
+
placeholder: labels.mappings.namePlaceholder,
|
|
104
106
|
value: draft.name
|
|
105
107
|
})
|
|
106
108
|
}),
|
|
107
109
|
/*#__PURE__*/ _jsx(Field, {
|
|
108
|
-
hint: targets.length === 0 ?
|
|
110
|
+
hint: targets.length === 0 ? labels.mappings.targetNone : undefined,
|
|
109
111
|
htmlFor: `${uid}-target`,
|
|
110
|
-
label:
|
|
112
|
+
label: labels.mappings.target,
|
|
111
113
|
children: /*#__PURE__*/ _jsxs("select", {
|
|
112
114
|
className: theme.field.input,
|
|
113
115
|
disabled: busy || targets.length === 0,
|
|
@@ -117,7 +119,7 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
117
119
|
children: [
|
|
118
120
|
/*#__PURE__*/ _jsx("option", {
|
|
119
121
|
value: "",
|
|
120
|
-
children:
|
|
122
|
+
children: labels.mappings.notSet
|
|
121
123
|
}),
|
|
122
124
|
targets.map((option)=>/*#__PURE__*/ _jsx("option", {
|
|
123
125
|
value: option.entity,
|
|
@@ -129,9 +131,9 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
129
131
|
]
|
|
130
132
|
}),
|
|
131
133
|
/*#__PURE__*/ _jsx(Field, {
|
|
132
|
-
hint: targetFields.length === 0 ?
|
|
134
|
+
hint: targetFields.length === 0 ? labels.mappings.dedupHintNoTarget : labels.mappings.dedupHint,
|
|
133
135
|
htmlFor: `${uid}-dedup`,
|
|
134
|
-
label:
|
|
136
|
+
label: labels.mappings.dedup,
|
|
135
137
|
children: /*#__PURE__*/ _jsxs("select", {
|
|
136
138
|
"aria-describedby": `${uid}-dedup-hint`,
|
|
137
139
|
className: theme.field.input,
|
|
@@ -144,7 +146,7 @@ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, fre
|
|
|
144
146
|
children: [
|
|
145
147
|
/*#__PURE__*/ _jsx("option", {
|
|
146
148
|
value: "",
|
|
147
|
-
children:
|
|
149
|
+
children: labels.mappings.notSet
|
|
148
150
|
}),
|
|
149
151
|
targetFields.map((field)=>/*#__PURE__*/ _jsx("option", {
|
|
150
152
|
value: field,
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useEffect, useEffectEvent, useId, useState } from 'react';
|
|
4
4
|
import { cn } from '../../../utils/cn';
|
|
5
5
|
import { useIntegrationsStore } from '../store';
|
|
6
|
+
import { useLabels } from '../text/context';
|
|
6
7
|
import { integrationsPageTheme } from '../theme';
|
|
7
8
|
import { Badge, Field, Notice } from './Primitives';
|
|
8
9
|
import { ListSkeleton } from './Skeletons';
|
|
@@ -27,6 +28,7 @@ function describeLastRun(value) {
|
|
|
27
28
|
return Number.isNaN(when.getTime()) ? 'Never run' : `Last run ${when.toLocaleString()}`;
|
|
28
29
|
}
|
|
29
30
|
export function MappingList({ actions, sourceId, selectedId, reloadToken, onSelect }) {
|
|
31
|
+
const labels = useLabels();
|
|
30
32
|
const store = useIntegrationsStore();
|
|
31
33
|
const uid = useId();
|
|
32
34
|
const search = store.mappingsSearch;
|
|
@@ -98,18 +100,18 @@ export function MappingList({ actions, sourceId, selectedId, reloadToken, onSele
|
|
|
98
100
|
children: [
|
|
99
101
|
/*#__PURE__*/ _jsx(Field, {
|
|
100
102
|
htmlFor: `${uid}-search`,
|
|
101
|
-
label:
|
|
103
|
+
label: labels.mappings.searchLabel,
|
|
102
104
|
children: /*#__PURE__*/ _jsx("input", {
|
|
103
105
|
className: theme.field.input,
|
|
104
106
|
id: `${uid}-search`,
|
|
105
107
|
onChange: (event)=>setTerm(event.target.value),
|
|
106
|
-
placeholder:
|
|
108
|
+
placeholder: labels.mappings.searchPlaceholder,
|
|
107
109
|
type: "search",
|
|
108
110
|
value: term
|
|
109
111
|
})
|
|
110
112
|
}),
|
|
111
113
|
list.error ? /*#__PURE__*/ _jsxs(Notice, {
|
|
112
|
-
title:
|
|
114
|
+
title: labels.mappings.loadFailed,
|
|
113
115
|
tone: "danger",
|
|
114
116
|
children: [
|
|
115
117
|
list.error,
|
|
@@ -184,7 +186,7 @@ export function MappingList({ actions, sourceId, selectedId, reloadToken, onSele
|
|
|
184
186
|
}),
|
|
185
187
|
list.loading && list.items.length > 0 ? /*#__PURE__*/ _jsx("p", {
|
|
186
188
|
className: theme.list.itemMeta,
|
|
187
|
-
children:
|
|
189
|
+
children: labels.mappings.loadingMore
|
|
188
190
|
}) : null
|
|
189
191
|
]
|
|
190
192
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useLabels } from '../text/context';
|
|
3
4
|
import { integrationsPageTheme } from '../theme';
|
|
4
5
|
import { Field, Notice } from './Primitives';
|
|
5
6
|
/**
|
|
@@ -49,6 +50,7 @@ function toMissingPolicy(value) {
|
|
|
49
50
|
return policy === 'deactivate' && (column.trim() === '' || value.trim() === '');
|
|
50
51
|
}
|
|
51
52
|
export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolicy, missingRecordColumn, missingRecordValue, dedupConfigured, disabled, onWriteModeChange, onMissingRecordPolicyChange, onMissingRecordColumnChange, onMissingRecordValueChange }) {
|
|
53
|
+
const labels = useLabels();
|
|
52
54
|
const modeId = `${idPrefix}-write-mode`;
|
|
53
55
|
const policyId = `${idPrefix}-missing-policy`;
|
|
54
56
|
const columnId = `${idPrefix}-missing-column`;
|
|
@@ -60,8 +62,8 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
|
|
|
60
62
|
children: [
|
|
61
63
|
/*#__PURE__*/ _jsx(Field, {
|
|
62
64
|
htmlFor: modeId,
|
|
63
|
-
hint:
|
|
64
|
-
label:
|
|
65
|
+
hint: labels.writeModeHint[writeMode],
|
|
66
|
+
label: labels.mappings.writeModeLabel,
|
|
65
67
|
children: /*#__PURE__*/ _jsx("select", {
|
|
66
68
|
"aria-describedby": `${modeId}-hint`,
|
|
67
69
|
className: theme.field.input,
|
|
@@ -71,12 +73,12 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
|
|
|
71
73
|
value: writeMode,
|
|
72
74
|
children: WRITE_MODES.map((mode)=>/*#__PURE__*/ _jsx("option", {
|
|
73
75
|
value: mode,
|
|
74
|
-
children:
|
|
76
|
+
children: labels.writeModeFull[mode]
|
|
75
77
|
}, mode))
|
|
76
78
|
})
|
|
77
79
|
}),
|
|
78
80
|
writeMode === 'replace' ? /*#__PURE__*/ _jsxs(Notice, {
|
|
79
|
-
title:
|
|
81
|
+
title: labels.mappings.replaceWarnTitle,
|
|
80
82
|
tone: "danger",
|
|
81
83
|
children: [
|
|
82
84
|
/*#__PURE__*/ _jsx("p", {
|
|
@@ -90,8 +92,8 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
|
|
|
90
92
|
}) : null,
|
|
91
93
|
/*#__PURE__*/ _jsx(Field, {
|
|
92
94
|
htmlFor: policyId,
|
|
93
|
-
hint:
|
|
94
|
-
label:
|
|
95
|
+
hint: labels.missingPolicyHint[missingRecordPolicy],
|
|
96
|
+
label: labels.mappings.missingPolicyLabel,
|
|
95
97
|
children: /*#__PURE__*/ _jsx("select", {
|
|
96
98
|
"aria-describedby": `${policyId}-hint`,
|
|
97
99
|
className: theme.field.input,
|
|
@@ -101,12 +103,12 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
|
|
|
101
103
|
value: missingRecordPolicy,
|
|
102
104
|
children: MISSING_POLICIES.map((policy)=>/*#__PURE__*/ _jsx("option", {
|
|
103
105
|
value: policy,
|
|
104
|
-
children:
|
|
106
|
+
children: labels.missingPolicyFull[policy]
|
|
105
107
|
}, policy))
|
|
106
108
|
})
|
|
107
109
|
}),
|
|
108
110
|
missingRecordPolicy === 'delete' ? /*#__PURE__*/ _jsx(Notice, {
|
|
109
|
-
title:
|
|
111
|
+
title: labels.mappings.deleteWarnTitle,
|
|
110
112
|
tone: "warning",
|
|
111
113
|
children: "A source that answers with a short page — a filter left on, a timeout, an expired token honoured with an empty list — looks exactly like records that went away."
|
|
112
114
|
}) : null,
|
|
@@ -115,9 +117,9 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
|
|
|
115
117
|
children: [
|
|
116
118
|
/*#__PURE__*/ _jsx(Field, {
|
|
117
119
|
error: columnMissing ? 'Required while Deactivate is selected.' : null,
|
|
118
|
-
hint:
|
|
120
|
+
hint: labels.mappings.deactivateColumnHint,
|
|
119
121
|
htmlFor: columnId,
|
|
120
|
-
label:
|
|
122
|
+
label: labels.mappings.deactivateColumn,
|
|
121
123
|
children: /*#__PURE__*/ _jsx("input", {
|
|
122
124
|
"aria-describedby": `${columnId}-hint`,
|
|
123
125
|
className: theme.field.input,
|
|
@@ -130,9 +132,9 @@ export function MappingWriteModeFields({ idPrefix, writeMode, missingRecordPolic
|
|
|
130
132
|
}),
|
|
131
133
|
/*#__PURE__*/ _jsx(Field, {
|
|
132
134
|
error: valueMissing ? 'Required while Deactivate is selected.' : null,
|
|
133
|
-
hint:
|
|
135
|
+
hint: labels.mappings.deactivateValueHint,
|
|
134
136
|
htmlFor: valueId,
|
|
135
|
-
label:
|
|
137
|
+
label: labels.mappings.deactivateValue,
|
|
136
138
|
children: /*#__PURE__*/ _jsx("input", {
|
|
137
139
|
"aria-describedby": `${valueId}-hint`,
|
|
138
140
|
className: theme.field.input,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useEffectEvent, useState } from 'react';
|
|
4
4
|
import { useIntegrationsStore } from '../store';
|
|
5
|
+
import { useLabels } from '../text/context';
|
|
5
6
|
import { integrationsPageTheme } from '../theme';
|
|
6
7
|
import { MappingEditor } from './MappingEditor';
|
|
7
8
|
import { MappingList } from './MappingList';
|
|
@@ -19,6 +20,7 @@ function messageOf(error) {
|
|
|
19
20
|
return error instanceof Error ? error.message : 'The request failed.';
|
|
20
21
|
}
|
|
21
22
|
export function MappingsTab({ actions }) {
|
|
23
|
+
const labels = useLabels();
|
|
22
24
|
const store = useIntegrationsStore();
|
|
23
25
|
const [targets, setTargets] = useState([]);
|
|
24
26
|
const [targetsLoading, setTargetsLoading] = useState(true);
|
|
@@ -124,29 +126,23 @@ export function MappingsTab({ actions }) {
|
|
|
124
126
|
return /*#__PURE__*/ _jsxs("section", {
|
|
125
127
|
className: "flex flex-col gap-4",
|
|
126
128
|
children: [
|
|
127
|
-
/*#__PURE__*/
|
|
129
|
+
/*#__PURE__*/ _jsx("div", {
|
|
128
130
|
className: theme.header.wrapper,
|
|
129
|
-
children:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
children:
|
|
137
|
-
className: theme.button.primary,
|
|
138
|
-
disabled: newMappingEndpointId === '',
|
|
139
|
-
onClick: startNew,
|
|
140
|
-
type: "button",
|
|
141
|
-
children: "New mapping"
|
|
142
|
-
})
|
|
131
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
132
|
+
className: theme.header.actions,
|
|
133
|
+
children: /*#__PURE__*/ _jsx("button", {
|
|
134
|
+
className: theme.button.primary,
|
|
135
|
+
disabled: newMappingEndpointId === '',
|
|
136
|
+
onClick: startNew,
|
|
137
|
+
type: "button",
|
|
138
|
+
children: labels.mappings.create
|
|
143
139
|
})
|
|
144
|
-
|
|
140
|
+
})
|
|
145
141
|
}),
|
|
146
142
|
newMappingEndpointId === '' ? /*#__PURE__*/ _jsx(Notice, {
|
|
147
|
-
title:
|
|
143
|
+
title: labels.mappings.noEndpointsTitle,
|
|
148
144
|
tone: "info",
|
|
149
|
-
children:
|
|
145
|
+
children: labels.mappings.noEndpointsBody
|
|
150
146
|
}) : null,
|
|
151
147
|
/*#__PURE__*/ _jsxs("div", {
|
|
152
148
|
className: theme.layout.split,
|
|
@@ -176,7 +172,7 @@ export function MappingsTab({ actions }) {
|
|
|
176
172
|
targetsLoading: targetsLoading
|
|
177
173
|
}, draft.id ?? 'new-mapping') : /*#__PURE__*/ _jsx("p", {
|
|
178
174
|
className: theme.list.empty,
|
|
179
|
-
children:
|
|
175
|
+
children: labels.mappings.pickOne
|
|
180
176
|
})
|
|
181
177
|
})
|
|
182
178
|
]
|
|
@@ -58,6 +58,10 @@ export type IntegrationsLabels = {
|
|
|
58
58
|
off: string;
|
|
59
59
|
/** Returns to the chooser once a connection is being worked in. */
|
|
60
60
|
change: string;
|
|
61
|
+
editTitle: string;
|
|
62
|
+
newTitle: string;
|
|
63
|
+
enabledBadge: string;
|
|
64
|
+
disabledBadge: string;
|
|
61
65
|
};
|
|
62
66
|
runs: {
|
|
63
67
|
/** The one-line summary above the buttons. */
|
|
@@ -95,6 +99,7 @@ export type IntegrationsLabels = {
|
|
|
95
99
|
loadFailed: string;
|
|
96
100
|
loadingMore: string;
|
|
97
101
|
selectConnection: string;
|
|
102
|
+
pickOne: string;
|
|
98
103
|
editTitle: string;
|
|
99
104
|
newTitle: string;
|
|
100
105
|
tag: string;
|
|
@@ -171,6 +176,76 @@ export type IntegrationsLabels = {
|
|
|
171
176
|
timeout: string;
|
|
172
177
|
timeoutHint: string;
|
|
173
178
|
};
|
|
179
|
+
/** Step three: which field lands in which column. */
|
|
180
|
+
mappings: {
|
|
181
|
+
heading: string;
|
|
182
|
+
searchLabel: string;
|
|
183
|
+
searchPlaceholder: string;
|
|
184
|
+
create: string;
|
|
185
|
+
loadFailed: string;
|
|
186
|
+
loadingMore: string;
|
|
187
|
+
empty: string;
|
|
188
|
+
noEndpointsTitle: string;
|
|
189
|
+
noEndpointsBody: string;
|
|
190
|
+
pickOne: string;
|
|
191
|
+
editTitle: string;
|
|
192
|
+
newTitle: string;
|
|
193
|
+
readsFrom: string;
|
|
194
|
+
readsFromHint: string;
|
|
195
|
+
notSet: string;
|
|
196
|
+
name: string;
|
|
197
|
+
namePlaceholder: string;
|
|
198
|
+
target: string;
|
|
199
|
+
targetNone: string;
|
|
200
|
+
dedup: string;
|
|
201
|
+
dedupHintNoTarget: string;
|
|
202
|
+
dedupHint: string;
|
|
203
|
+
writeModeLabel: string;
|
|
204
|
+
missingPolicyLabel: string;
|
|
205
|
+
replaceWarnTitle: string;
|
|
206
|
+
deleteWarnTitle: string;
|
|
207
|
+
deactivateColumn: string;
|
|
208
|
+
deactivateColumnHint: string;
|
|
209
|
+
deactivateValue: string;
|
|
210
|
+
deactivateValueHint: string;
|
|
211
|
+
fieldsHeading: string;
|
|
212
|
+
addField: string;
|
|
213
|
+
sampleFirstTitle: string;
|
|
214
|
+
sampleFirstBody: string;
|
|
215
|
+
nothingMapped: string;
|
|
216
|
+
parentPrefixHint: string;
|
|
217
|
+
templateHint: string;
|
|
218
|
+
source: string;
|
|
219
|
+
sourcePlaceholder: string;
|
|
220
|
+
targetColumn: string;
|
|
221
|
+
transform: string;
|
|
222
|
+
constant: string;
|
|
223
|
+
constantPlaceholder: string;
|
|
224
|
+
fallback: string;
|
|
225
|
+
fallbackPlaceholder: string;
|
|
226
|
+
save: string;
|
|
227
|
+
};
|
|
228
|
+
/** The long form of each write mode, for the picker. */
|
|
229
|
+
writeModeFull: {
|
|
230
|
+
upsert: string;
|
|
231
|
+
append: string;
|
|
232
|
+
replace: string;
|
|
233
|
+
};
|
|
234
|
+
writeModeHint: {
|
|
235
|
+
upsert: string;
|
|
236
|
+
append: string;
|
|
237
|
+
replace: string;
|
|
238
|
+
};
|
|
239
|
+
missingPolicyFull: {
|
|
240
|
+
ignore: string;
|
|
241
|
+
deactivate: string;
|
|
242
|
+
delete: string;
|
|
243
|
+
};
|
|
244
|
+
missingPolicyHint: {
|
|
245
|
+
ignore: string;
|
|
246
|
+
deactivate: string;
|
|
247
|
+
delete: string;
|
|
248
|
+
};
|
|
174
249
|
writeMode: {
|
|
175
250
|
upsert: string;
|
|
176
251
|
append: string;
|
|
@@ -51,7 +51,11 @@
|
|
|
51
51
|
loadFailed: 'This list could not be loaded',
|
|
52
52
|
retry: 'Try again',
|
|
53
53
|
off: 'Off',
|
|
54
|
-
change: 'Change'
|
|
54
|
+
change: 'Change',
|
|
55
|
+
editTitle: 'Connection',
|
|
56
|
+
newTitle: 'New connection',
|
|
57
|
+
enabledBadge: 'Enabled',
|
|
58
|
+
disabledBadge: 'Off'
|
|
55
59
|
},
|
|
56
60
|
runs: {
|
|
57
61
|
summary: (target, writeMode, missing)=>`Writes into ${target} · ${writeMode} · missing rows: ${missing}`,
|
|
@@ -87,6 +91,7 @@
|
|
|
87
91
|
loadFailed: 'This list could not be loaded',
|
|
88
92
|
loadingMore: 'Loading more…',
|
|
89
93
|
selectConnection: 'Select a connection to see its calls.',
|
|
94
|
+
pickOne: 'Select a call to edit it, or add one. A call is a single request — a method, a path, and where in the answer the records sit.',
|
|
90
95
|
editTitle: 'Edit call',
|
|
91
96
|
newTitle: 'New call',
|
|
92
97
|
tag: 'Tag',
|
|
@@ -162,6 +167,74 @@
|
|
|
162
167
|
timeout: 'Timeout (ms)',
|
|
163
168
|
timeoutHint: 'How long one request may take before it is given up on. Empty means the server default.'
|
|
164
169
|
},
|
|
170
|
+
mappings: {
|
|
171
|
+
heading: 'Mappings',
|
|
172
|
+
searchLabel: 'Search',
|
|
173
|
+
searchPlaceholder: 'Name or target table',
|
|
174
|
+
create: 'New mapping',
|
|
175
|
+
loadFailed: 'This list could not be loaded',
|
|
176
|
+
loadingMore: 'Loading more…',
|
|
177
|
+
empty: 'No mappings match. A mapping is what turns a response into rows.',
|
|
178
|
+
noEndpointsTitle: 'No calls to read from',
|
|
179
|
+
noEndpointsBody: "A mapping turns one call's response into rows, so there has to be a call first. Add one on the calls step and it becomes selectable here.",
|
|
180
|
+
pickOne: 'Pick a mapping to edit it, or create one to describe what an import writes.',
|
|
181
|
+
editTitle: 'Mapping',
|
|
182
|
+
newTitle: 'New mapping',
|
|
183
|
+
readsFrom: 'Reads from',
|
|
184
|
+
readsFromHint: 'Every mapping reads from exactly one call.',
|
|
185
|
+
notSet: 'Not set',
|
|
186
|
+
name: 'Name',
|
|
187
|
+
namePlaceholder: 'Staff directory import',
|
|
188
|
+
target: 'Writes into',
|
|
189
|
+
targetNone: 'This app exposes no writable tables.',
|
|
190
|
+
dedup: 'Matched on',
|
|
191
|
+
dedupHintNoTarget: 'Choose a target table to list its columns.',
|
|
192
|
+
dedupHint: 'The column an incoming record is matched on. Without it nothing can be updated.',
|
|
193
|
+
writeModeLabel: 'What a run does',
|
|
194
|
+
missingPolicyLabel: 'Records the source no longer returns',
|
|
195
|
+
replaceWarnTitle: 'Replace rebuilds the table on every run',
|
|
196
|
+
deleteWarnTitle: 'Deleting is not reversible',
|
|
197
|
+
deactivateColumn: 'Deactivation column',
|
|
198
|
+
deactivateColumnHint: 'A column on the target table.',
|
|
199
|
+
deactivateValue: 'Deactivation value',
|
|
200
|
+
deactivateValueHint: 'Written into that column.',
|
|
201
|
+
fieldsHeading: 'Field mappings',
|
|
202
|
+
addField: 'Add field',
|
|
203
|
+
sampleFirstTitle: 'Try the call first',
|
|
204
|
+
sampleFirstBody: 'No field names are known yet. Trying the selected call 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.',
|
|
205
|
+
nothingMapped: 'Nothing is mapped yet, so a run would write empty records. Add a field to begin.',
|
|
206
|
+
parentPrefixHint: 'to take the value from the parent call’s record instead of this one.',
|
|
207
|
+
templateHint: 'to build one value from several — use it when no single field is unique on its own, which is what the matching column needs.',
|
|
208
|
+
source: 'Source',
|
|
209
|
+
sourcePlaceholder: 'employeeNo',
|
|
210
|
+
targetColumn: 'Target',
|
|
211
|
+
transform: 'Transform',
|
|
212
|
+
constant: 'Constant',
|
|
213
|
+
constantPlaceholder: 'Same value on every record',
|
|
214
|
+
fallback: 'Fallback',
|
|
215
|
+
fallbackPlaceholder: 'Used when the source is empty',
|
|
216
|
+
save: 'Save mapping'
|
|
217
|
+
},
|
|
218
|
+
writeModeFull: {
|
|
219
|
+
upsert: 'Upsert — update what matches, insert the rest',
|
|
220
|
+
append: 'Append — insert every fetched record',
|
|
221
|
+
replace: 'Replace — rebuild the table from this response'
|
|
222
|
+
},
|
|
223
|
+
writeModeHint: {
|
|
224
|
+
upsert: 'Rows keep their identity, so whatever points at them keeps working.',
|
|
225
|
+
append: 'Nothing is matched, so running this import twice stores every record twice.',
|
|
226
|
+
replace: 'The table is emptied and refilled on every run.'
|
|
227
|
+
},
|
|
228
|
+
missingPolicyFull: {
|
|
229
|
+
ignore: 'Ignore — leave them exactly as they are',
|
|
230
|
+
deactivate: 'Deactivate — mark them with a column value',
|
|
231
|
+
delete: 'Delete — remove them from the table'
|
|
232
|
+
},
|
|
233
|
+
missingPolicyHint: {
|
|
234
|
+
ignore: 'Safest. Records removed at the source stay in the table until someone acts.',
|
|
235
|
+
deactivate: 'Reversible. The row survives and history that refers to it stays readable.',
|
|
236
|
+
delete: 'Permanent. A source that returns a short page for any reason takes rows with it.'
|
|
237
|
+
},
|
|
165
238
|
writeMode: {
|
|
166
239
|
upsert: 'update what matches, insert the rest',
|
|
167
240
|
append: 'always insert, never match',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.806",
|
|
4
4
|
"description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
|
|
5
5
|
"author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|