nucleus-core-ts 0.9.793 → 0.9.795

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.
@@ -107,6 +107,21 @@ export function FieldMappingRows({ rows, sourceFields, targetFields, disabled, o
107
107
  }),
108
108
  " to take the value from the parent endpoint's record instead of this one."
109
109
  ]
110
+ }),
111
+ /*#__PURE__*/ _jsxs("p", {
112
+ className: theme.field.hint,
113
+ children: [
114
+ "Name several fields in ",
115
+ /*#__PURE__*/ _jsx("strong", {
116
+ children: '{braces}'
117
+ }),
118
+ " to build one value from them —",
119
+ ' ',
120
+ /*#__PURE__*/ _jsx("strong", {
121
+ children: '{companyCode}-{organizationCode}'
122
+ }),
123
+ ". Use it when no single field is unique on its own, which is what the deduplication column needs."
124
+ ]
110
125
  })
111
126
  ]
112
127
  })
@@ -1,15 +1,30 @@
1
+ import type { IntegrationEndpoint } from '../types';
1
2
  import { type MappingDraft, type MappingTarget } from './mappingDraft';
2
3
  export type MappingEditorProps = {
3
4
  /** Starting values. Read once — remount with a new `key` to load another. */
4
5
  mapping: MappingDraft;
5
6
  targets: MappingTarget[];
6
7
  targetsLoading: boolean;
7
- /** Field names the selected endpoint's last sample reported. */
8
- sourceFields: string[];
8
+ /**
9
+ * The connection's endpoints. A mapping reads from exactly one, and which one
10
+ * is half of what a mapping IS — so it is chosen here rather than inherited
11
+ * from whatever happens to be selected on another tab.
12
+ */
13
+ endpoints: IntegrationEndpoint[];
14
+ /**
15
+ * The sample taken in this session and the endpoint it describes.
16
+ *
17
+ * Fresher than the endpoint's stored sample, but only for that one endpoint —
18
+ * offering it against any other would suggest columns that never arrive.
19
+ */
20
+ freshSample: {
21
+ endpointId: string;
22
+ fields: string[];
23
+ } | null;
9
24
  saving: boolean;
10
25
  deleting: boolean;
11
26
  onSave: (draft: MappingDraft) => void;
12
27
  onDelete: (id: string) => void;
13
28
  onCancel: () => void;
14
29
  };
15
- export declare function MappingEditor({ mapping, targets, targetsLoading, sourceFields, saving, deleting, onSave, onDelete, onCancel, }: MappingEditorProps): import("react/jsx-runtime").JSX.Element;
30
+ export declare function MappingEditor({ mapping, targets, targetsLoading, endpoints, freshSample, saving, deleting, onSave, onDelete, onCancel, }: MappingEditorProps): import("react/jsx-runtime").JSX.Element;
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useId, useState } from 'react';
4
4
  import { integrationsPageTheme } from '../theme';
5
5
  import { FieldMappingRows } from './FieldMappingRows';
6
+ import { endpointLabel, fieldsOfStoredSample } from './formSupport';
6
7
  import { MappingWriteModeFields } from './MappingWriteModeFields';
7
8
  import { mappingDraftIssues } from './mappingDraft';
8
9
  import { Field } from './Primitives';
@@ -17,7 +18,7 @@ import { CardSkeleton } from './Skeletons';
17
18
  * Mount it with a `key` of the mapping id so switching rows starts a fresh
18
19
  * draft instead of pouring one mapping's edits into another.
19
20
  */ const theme = integrationsPageTheme;
20
- export function MappingEditor({ mapping, targets, targetsLoading, sourceFields, saving, deleting, onSave, onDelete, onCancel }) {
21
+ export function MappingEditor({ mapping, targets, targetsLoading, endpoints, freshSample, saving, deleting, onSave, onDelete, onCancel }) {
21
22
  const uid = useId();
22
23
  const [draft, setDraft] = useState(mapping);
23
24
  const patch = (next)=>{
@@ -45,6 +46,10 @@ export function MappingEditor({ mapping, targets, targetsLoading, sourceFields,
45
46
  const targetFields = targets.find((option)=>option.entity === draft.targetEntity)?.fields ?? [];
46
47
  const issues = mappingDraftIssues(draft);
47
48
  const busy = saving || deleting;
49
+ // Follows the endpoint CHOSEN here, so changing it changes the field list
50
+ // rather than leaving the previous endpoint's names on offer.
51
+ const readsFrom = endpoints.find((endpoint)=>endpoint.id === draft.endpointId);
52
+ const sourceFields = freshSample && freshSample.endpointId === draft.endpointId && freshSample.fields.length > 0 ? freshSample.fields : fieldsOfStoredSample(readsFrom?.sampleResponse);
48
53
  return /*#__PURE__*/ _jsxs("section", {
49
54
  className: theme.card.wrapper,
50
55
  children: [
@@ -58,6 +63,30 @@ export function MappingEditor({ mapping, targets, targetsLoading, sourceFields,
58
63
  /*#__PURE__*/ _jsxs("div", {
59
64
  className: theme.card.body,
60
65
  children: [
66
+ /*#__PURE__*/ _jsx(Field, {
67
+ hint: endpoints.length === 0 ? 'This connection has no endpoints yet. Add one on the Endpoints tab.' : readsFrom?.path ?? 'Every mapping reads from exactly one endpoint.',
68
+ htmlFor: `${uid}-endpoint`,
69
+ label: "Reads from",
70
+ children: /*#__PURE__*/ _jsxs("select", {
71
+ className: theme.field.input,
72
+ disabled: busy || endpoints.length === 0,
73
+ id: `${uid}-endpoint`,
74
+ onChange: (event)=>patch({
75
+ endpointId: event.target.value
76
+ }),
77
+ value: draft.endpointId,
78
+ children: [
79
+ /*#__PURE__*/ _jsx("option", {
80
+ value: "",
81
+ children: "Not set"
82
+ }),
83
+ endpoints.map((endpoint)=>/*#__PURE__*/ _jsx("option", {
84
+ value: endpoint.id,
85
+ children: endpointLabel(endpoint)
86
+ }, endpoint.id))
87
+ ]
88
+ })
89
+ }),
61
90
  /*#__PURE__*/ _jsxs("div", {
62
91
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
63
92
  children: [
@@ -3,7 +3,6 @@ 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
5
  import { integrationsPageTheme } from '../theme';
6
- import { fieldsOfStoredSample } from './formSupport';
7
6
  import { MappingEditor } from './MappingEditor';
8
7
  import { MappingList } from './MappingList';
9
8
  import { blankMappingDraft, toMappingDraft } from './mappingDraft';
@@ -114,16 +113,14 @@ export function MappingsTab({ actions }) {
114
113
  });
115
114
  };
116
115
  const draft = creating ? blankMappingDraft(newMappingEndpointId) : selected ? toMappingDraft(selected) : null;
117
- // A sample describes ONE endpoint. Offering its field names against a mapping
118
- // that reads from a different one would suggest columns that never arrive
119
- // so both the in-session sample and the stored one are matched to the
120
- // mapping's OWN endpoint, never to whatever happens to be selected elsewhere.
121
- //
122
- // The stored sample is the fallback rather than an afterthought: it is why the
123
- // sample is persisted at all. Reading only the in-session one meant a
124
- // connection revisited tomorrow claimed it had never been sampled.
125
- const draftEndpoint = draft ? store.endpoints.find((e)=>e.id === draft.endpointId) : undefined;
126
- const sourceFields = draft && store.selectedEndpointId === draft.endpointId && store.sample?.fields?.length ? store.sample.fields : fieldsOfStoredSample(draftEndpoint?.sampleResponse);
116
+ // A sample describes ONE endpoint, so it travels with the endpoint it belongs
117
+ // to and the editor matches it against the mapping's own choice. The editor
118
+ // falls back to the endpoint's STORED sample which is why the sample is
119
+ // persisted at all, and what a connection revisited tomorrow has.
120
+ const freshSample = store.selectedEndpointId && store.sample ? {
121
+ endpointId: store.selectedEndpointId,
122
+ fields: store.sample.fields ?? []
123
+ } : null;
127
124
  return /*#__PURE__*/ _jsxs("section", {
128
125
  className: "flex flex-col gap-4",
129
126
  children: [
@@ -147,9 +144,9 @@ export function MappingsTab({ actions }) {
147
144
  ]
148
145
  }),
149
146
  newMappingEndpointId === '' ? /*#__PURE__*/ _jsx(Notice, {
150
- title: "Select an endpoint first",
147
+ title: "No endpoints to read from",
151
148
  tone: "info",
152
- children: "Every mapping reads from one endpoint. Choose one on the Endpoints tab and it becomes the source of the next mapping created here."
149
+ children: "A mapping turns one endpoint's response into rows, so there has to be an endpoint first. Add one on the Endpoints tab and it becomes selectable here."
153
150
  }) : null,
154
151
  /*#__PURE__*/ _jsxs("div", {
155
152
  className: theme.layout.split,
@@ -168,12 +165,13 @@ export function MappingsTab({ actions }) {
168
165
  className: theme.layout.main,
169
166
  children: draft ? /*#__PURE__*/ _jsx(MappingEditor, {
170
167
  deleting: deleting,
168
+ endpoints: store.endpoints,
169
+ freshSample: freshSample,
171
170
  mapping: draft,
172
171
  onCancel: closeEditor,
173
172
  onDelete: remove,
174
173
  onSave: save,
175
174
  saving: saving,
176
- sourceFields: sourceFields,
177
175
  targets: targets,
178
176
  targetsLoading: targetsLoading
179
177
  }, draft.id ?? 'new-mapping') : /*#__PURE__*/ _jsx("p", {