nucleus-core-ts 0.9.818 → 0.9.820
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/MappingList.d.ts +9 -1
- package/dist/fe/components/IntegrationsPage/components/MappingList.js +46 -35
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +4 -0
- package/dist/fe/components/IntegrationsPage/components/RunPanel.js +40 -0
- package/package.json +1 -1
|
@@ -4,8 +4,16 @@ export type MappingListProps = {
|
|
|
4
4
|
/** The connection whose mappings these are. */
|
|
5
5
|
sourceId: string;
|
|
6
6
|
selectedId: string | null;
|
|
7
|
+
/**
|
|
8
|
+
* Run THIS one.
|
|
9
|
+
*
|
|
10
|
+
* The import step imports whichever mapping happens to be selected, which is
|
|
11
|
+
* an answer to "run something" and not to "run this". Somebody looking at a
|
|
12
|
+
* list of four wants the fourth, and the row is where they are looking.
|
|
13
|
+
*/
|
|
14
|
+
onRun?: (mapping: IntegrationMapping) => void;
|
|
7
15
|
/** Bumped by the tab after a save or a delete so page one is fetched again. */
|
|
8
16
|
reloadToken: number;
|
|
9
17
|
onSelect: (mapping: IntegrationMapping) => void;
|
|
10
18
|
};
|
|
11
|
-
export declare function MappingList({ actions, sourceId, selectedId, reloadToken, onSelect, }: MappingListProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function MappingList({ actions, sourceId, selectedId, reloadToken, onSelect, onRun, }: MappingListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -27,7 +27,7 @@ function describeLastRun(value) {
|
|
|
27
27
|
const when = new Date(value);
|
|
28
28
|
return Number.isNaN(when.getTime()) ? 'Never run' : `Last run ${when.toLocaleString()}`;
|
|
29
29
|
}
|
|
30
|
-
export function MappingList({ actions, sourceId, selectedId, reloadToken, onSelect }) {
|
|
30
|
+
export function MappingList({ actions, sourceId, selectedId, reloadToken, onSelect, onRun }) {
|
|
31
31
|
const labels = useLabels();
|
|
32
32
|
const store = useIntegrationsStore();
|
|
33
33
|
const uid = useId();
|
|
@@ -142,40 +142,51 @@ export function MappingList({ actions, sourceId, selectedId, reloadToken, onSele
|
|
|
142
142
|
}),
|
|
143
143
|
/*#__PURE__*/ _jsx("ul", {
|
|
144
144
|
className: theme.list.wrapper,
|
|
145
|
-
children: list.items.map((mapping)=>/*#__PURE__*/
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
145
|
+
children: list.items.map((mapping)=>/*#__PURE__*/ _jsxs("li", {
|
|
146
|
+
className: "flex items-stretch gap-1",
|
|
147
|
+
children: [
|
|
148
|
+
/*#__PURE__*/ _jsxs("button", {
|
|
149
|
+
"aria-current": mapping.id === selectedId ? 'true' : undefined,
|
|
150
|
+
className: cn(theme.list.item, 'flex-1', mapping.id === selectedId ? theme.list.itemSelected : undefined),
|
|
151
|
+
onClick: ()=>onSelect(mapping),
|
|
152
|
+
type: "button",
|
|
153
|
+
children: [
|
|
154
|
+
/*#__PURE__*/ _jsx("span", {
|
|
155
|
+
className: theme.list.itemTitle,
|
|
156
|
+
children: mapping.name
|
|
157
|
+
}),
|
|
158
|
+
/*#__PURE__*/ _jsx("span", {
|
|
159
|
+
className: theme.list.itemMeta,
|
|
160
|
+
children: mapping.targetEntity || 'No target entity'
|
|
161
|
+
}),
|
|
162
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
163
|
+
className: "mt-1 flex flex-wrap items-center gap-1.5",
|
|
164
|
+
children: [
|
|
165
|
+
/*#__PURE__*/ _jsx(Badge, {
|
|
166
|
+
tone: WRITE_MODE_TONE[mapping.writeMode],
|
|
167
|
+
children: mapping.writeMode
|
|
168
|
+
}),
|
|
169
|
+
mapping.enabled === false ? /*#__PURE__*/ _jsx(Badge, {
|
|
170
|
+
tone: "neutral",
|
|
171
|
+
children: "disabled"
|
|
172
|
+
}) : null,
|
|
173
|
+
/*#__PURE__*/ _jsx("span", {
|
|
174
|
+
className: theme.list.itemMeta,
|
|
175
|
+
children: describeLastRun(mapping.lastRunAt)
|
|
176
|
+
})
|
|
177
|
+
]
|
|
178
|
+
})
|
|
179
|
+
]
|
|
180
|
+
}),
|
|
181
|
+
onRun ? /*#__PURE__*/ _jsx("button", {
|
|
182
|
+
"aria-label": `Import ${mapping.name}`,
|
|
183
|
+
className: cn(theme.button.secondary, 'self-center whitespace-nowrap'),
|
|
184
|
+
onClick: ()=>onRun(mapping),
|
|
185
|
+
title: `Preview and import ${mapping.name}`,
|
|
186
|
+
type: "button",
|
|
187
|
+
children: "Import"
|
|
188
|
+
}) : null
|
|
189
|
+
]
|
|
179
190
|
}, mapping.id))
|
|
180
191
|
})
|
|
181
192
|
]
|
|
@@ -223,6 +223,10 @@ export function MappingsTab({ actions }) {
|
|
|
223
223
|
children: /*#__PURE__*/ _jsx(MappingList, {
|
|
224
224
|
actions: actions,
|
|
225
225
|
sourceId: sourceId,
|
|
226
|
+
onRun: (mapping)=>{
|
|
227
|
+
openMapping(mapping);
|
|
228
|
+
store.setTab('runs');
|
|
229
|
+
},
|
|
226
230
|
onSelect: openMapping,
|
|
227
231
|
reloadToken: reloadToken,
|
|
228
232
|
selectedId: selected?.id ?? null
|
|
@@ -93,6 +93,46 @@ export function RunPanel({ mapping, actions, onSubscribeProgress, onRunSettled }
|
|
|
93
93
|
onRunSettled?.();
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
|
+
/**
|
|
97
|
+
* Ask the server whether the run has finished.
|
|
98
|
+
*
|
|
99
|
+
* Progress frames are PUSHED — over whatever realtime channel the app wires
|
|
100
|
+
* up — and an app that wires none, or a socket that drops, leaves this panel
|
|
101
|
+
* saying "waiting for the first update" over an import that finished minutes
|
|
102
|
+
* ago, with a Cancel button for a run that is not running. The bar was the
|
|
103
|
+
* only thing on screen and it was lying.
|
|
104
|
+
*
|
|
105
|
+
* So the run's own row is the authority: it is polled while one is active, it
|
|
106
|
+
* settles the panel when the status is terminal, and it says what happened.
|
|
107
|
+
* Realtime stays the fast path and is no longer the only one.
|
|
108
|
+
*/ const pollActiveRun = useEffectEvent(()=>{
|
|
109
|
+
const runId = store.activeRunId;
|
|
110
|
+
if (!runId) return;
|
|
111
|
+
actions.listRuns({
|
|
112
|
+
mappingId: mapping.id,
|
|
113
|
+
page: 1,
|
|
114
|
+
limit: 5
|
|
115
|
+
}).then((result)=>{
|
|
116
|
+
if (store.activeRunId !== runId) return;
|
|
117
|
+
const run = result.items.find((item)=>item.id === runId);
|
|
118
|
+
if (!run || run.status === 'running' || run.status === 'queued') return;
|
|
119
|
+
store.setActiveRun(null);
|
|
120
|
+
store.setProgress(null);
|
|
121
|
+
if (run.status === 'failed') {
|
|
122
|
+
store.setError(run.errors?.[0] ?? 'The import failed. Its history entry has the detail.');
|
|
123
|
+
}
|
|
124
|
+
onRunSettled?.();
|
|
125
|
+
}).catch(()=>{
|
|
126
|
+
// A failed poll is not a failed run. The next tick asks again.
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
useEffect(()=>{
|
|
130
|
+
if (!activeRunId) return;
|
|
131
|
+
const timer = setInterval(pollActiveRun, 2000);
|
|
132
|
+
return ()=>clearInterval(timer);
|
|
133
|
+
}, [
|
|
134
|
+
activeRunId
|
|
135
|
+
]);
|
|
96
136
|
const openProgressStream = useEffectEvent(()=>onSubscribeProgress?.(applyProgress));
|
|
97
137
|
useEffect(()=>{
|
|
98
138
|
// The app hands back its own unsubscribe; returning it is the cleanup.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.820",
|
|
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",
|