nucleus-core-ts 0.9.815 → 0.9.816

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.
@@ -1,2 +1,2 @@
1
1
  import type { IntegrationsPageProps } from '../types';
2
- export declare function IntegrationsPage({ title, subtitle, className, actions, labels: labelsOverride, onSubscribeProgress, }: IntegrationsPageProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function IntegrationsPage({ title, subtitle, className, actions, labels: labelsOverride, focusTargetEntity, onSubscribeProgress, }: IntegrationsPageProps): import("react/jsx-runtime").JSX.Element;
@@ -25,7 +25,7 @@ import { RunsTab } from './RunsTab';
25
25
  * page from the server rather than being handed a list to slice.
26
26
  */ gsap.registerPlugin(useGSAP);
27
27
  const theme = integrationsPageTheme;
28
- export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect an external service, describe the data you want and import it on your terms.', className, actions, labels: labelsOverride, onSubscribeProgress }) {
28
+ export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect an external service, describe the data you want and import it on your terms.', className, actions, labels: labelsOverride, focusTargetEntity, onSubscribeProgress }) {
29
29
  const store = useIntegrationsStore();
30
30
  // Resolved here rather than read from the context, because this component is
31
31
  // the one that PROVIDES it — and passed down so a control four levels deep
@@ -83,6 +83,37 @@ export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect a
83
83
  store.selectedSourceId,
84
84
  sourcesReloadToken
85
85
  ]);
86
+ /**
87
+ * Somebody arrived from the table they want filled.
88
+ *
89
+ * They have already said which table; being shown a connection chooser and
90
+ * four mappings is asking the same question again. So the entity is handed to
91
+ * the mappings step, and — when there is only one connection to be meant — it
92
+ * is chosen for them. With several, the choice is real and stays theirs.
93
+ */ const takeFocus = useEffectEvent(()=>{
94
+ if (!focusTargetEntity) return;
95
+ store.setFocusTargetEntity(focusTargetEntity);
96
+ });
97
+ useEffect(()=>{
98
+ takeFocus();
99
+ }, [
100
+ focusTargetEntity
101
+ ]);
102
+ const followFocus = useEffectEvent(()=>{
103
+ if (!store.focusTargetEntity) return;
104
+ if (!store.selectedSourceId) {
105
+ if (store.sources.length === 1 && store.sources[0]) store.selectSource(store.sources[0].id);
106
+ return;
107
+ }
108
+ if (store.tab !== 'mappings') store.setTab('mappings');
109
+ });
110
+ useEffect(()=>{
111
+ followFocus();
112
+ }, [
113
+ store.focusTargetEntity,
114
+ store.selectedSourceId,
115
+ store.sources.length
116
+ ]);
86
117
  // One measured entrance. Anything longer makes an operator wait to read counts
87
118
  // they came here to check.
88
119
  useGSAP(()=>{
@@ -29,6 +29,7 @@ export function MappingsTab({ actions }) {
29
29
  const [saving, setSaving] = useState(false);
30
30
  const [deleting, setDeleting] = useState(false);
31
31
  const [reloadToken, setReloadToken] = useState(0);
32
+ /** Set only when a new mapping is being started for a table named on arrival. */ const [pendingTarget, setPendingTarget] = useState('');
32
33
  const sourceId = store.selectedSourceId ?? '';
33
34
  const newMappingEndpointId = store.selectedEndpointId ?? store.endpoints[0]?.id ?? '';
34
35
  const loadTargets = useEffectEvent(()=>{
@@ -72,6 +73,7 @@ export function MappingsTab({ actions }) {
72
73
  ]);
73
74
  const startNew = ()=>{
74
75
  setSelected(null);
76
+ setPendingTarget('');
75
77
  setCreating(true);
76
78
  store.selectMapping(null);
77
79
  };
@@ -80,8 +82,53 @@ export function MappingsTab({ actions }) {
80
82
  setSelected(mapping);
81
83
  store.selectMapping(mapping.id);
82
84
  };
85
+ /**
86
+ * Opens the mapping for the table somebody arrived asking about.
87
+ *
88
+ * Asked of the SERVER by target entity rather than searched in the page this
89
+ * tab happens to be holding: the list is paged, so the mapping being looked
90
+ * for is frequently not in it. If there is none yet, a new one is started
91
+ * already aimed at that table — which is the other half of the answer.
92
+ *
93
+ * Consumed once. Left set, it would drag the selection back every time the
94
+ * operator clicked a different mapping.
95
+ */ const followFocus = useEffectEvent(()=>{
96
+ const entity = store.focusTargetEntity;
97
+ if (!entity || sourceId === '') return;
98
+ store.setFocusTargetEntity(null);
99
+ actions.listMappings(sourceId, {
100
+ page: 1,
101
+ limit: 1,
102
+ filters: [
103
+ {
104
+ field: 'targetEntity',
105
+ operator: 'eq',
106
+ value: entity
107
+ }
108
+ ]
109
+ }).then((result)=>{
110
+ const found = result.items[0];
111
+ if (found) {
112
+ openMapping(found);
113
+ return;
114
+ }
115
+ setSelected(null);
116
+ setPendingTarget(entity);
117
+ setCreating(true);
118
+ store.selectMapping(null);
119
+ }).catch((error)=>{
120
+ store.setError(messageOf(error));
121
+ });
122
+ });
123
+ useEffect(()=>{
124
+ followFocus();
125
+ }, [
126
+ store.focusTargetEntity,
127
+ sourceId
128
+ ]);
83
129
  const closeEditor = ()=>{
84
130
  setCreating(false);
131
+ setPendingTarget('');
85
132
  setSelected(null);
86
133
  store.selectMapping(null);
87
134
  };
@@ -114,7 +161,7 @@ export function MappingsTab({ actions }) {
114
161
  store.setError(messageOf(error));
115
162
  });
116
163
  };
117
- const draft = creating ? blankMappingDraft(newMappingEndpointId) : selected ? toMappingDraft(selected) : null;
164
+ const draft = creating ? blankMappingDraft(newMappingEndpointId, pendingTarget) : selected ? toMappingDraft(selected) : null;
118
165
  // A sample describes ONE endpoint, so it travels with the endpoint it belongs
119
166
  // to and the editor matches it against the mapping's own choice. The editor
120
167
  // falls back to the endpoint's STORED sample — which is why the sample is
@@ -31,8 +31,13 @@ export type MappingDraft = {
31
31
  passwordTargetColumn?: string | null;
32
32
  enabled?: boolean;
33
33
  };
34
- /** Upsert and ignore: the two choices that cannot lose a row by themselves. */
35
- export declare function blankMappingDraft(endpointId: string): MappingDraft;
34
+ /**
35
+ * Upsert and ignore: the two choices that cannot lose a row by themselves.
36
+ *
37
+ * `targetEntity` is filled in when the operator arrived from the table itself —
38
+ * they have already said which one, and asking again is the panel forgetting.
39
+ */
40
+ export declare function blankMappingDraft(endpointId: string, targetEntity?: string): MappingDraft;
36
41
  /** Copies field by field so a column the server added cannot ride along unseen. */
37
42
  export declare function toMappingDraft(mapping: IntegrationMapping): MappingDraft;
38
43
  /**
@@ -1,10 +1,15 @@
1
1
  import { asArray } from './jsonArray';
2
2
  import { missingRecordFieldsIncomplete } from './MappingWriteModeFields';
3
- /** Upsert and ignore: the two choices that cannot lose a row by themselves. */ export function blankMappingDraft(endpointId) {
3
+ /**
4
+ * Upsert and ignore: the two choices that cannot lose a row by themselves.
5
+ *
6
+ * `targetEntity` is filled in when the operator arrived from the table itself —
7
+ * they have already said which one, and asking again is the panel forgetting.
8
+ */ export function blankMappingDraft(endpointId, targetEntity = '') {
4
9
  return {
5
10
  endpointId,
6
11
  name: '',
7
- targetEntity: '',
12
+ targetEntity,
8
13
  fieldMappings: [],
9
14
  referenceChecks: [],
10
15
  writeMode: 'upsert',
@@ -41,6 +41,9 @@ export const { useStore: useIntegrationsStore } = createStore(initial, {
41
41
  store.plan = null;
42
42
  store.error = null;
43
43
  }),
44
+ setFocusTargetEntity: (store)=>(entity)=>{
45
+ store.focusTargetEntity = entity;
46
+ },
44
47
  setTab: (store)=>(tab)=>{
45
48
  store.tab = tab;
46
49
  },
@@ -20,6 +20,15 @@ export type State = {
20
20
  sourcesLoading: boolean;
21
21
  selectedSourceId: string | null;
22
22
  tab: ConnectionTab;
23
+ /**
24
+ * The table somebody arrived here asking about.
25
+ *
26
+ * A management screen links straight in — "fill this table from a source" —
27
+ * and the panel is no use if it then asks which of four mappings was meant.
28
+ * Consumed once, on arrival: it steers the first render and is cleared, so a
29
+ * later click on another mapping is not dragged back.
30
+ */
31
+ focusTargetEntity: string | null;
23
32
  endpoints: IntegrationEndpoint[];
24
33
  endpointsTotal: number;
25
34
  endpointsPage: number;
@@ -56,6 +65,7 @@ export type Actions = {
56
65
  setSourcesLoading: (loading: boolean) => void;
57
66
  selectSource: (id: string | null) => void;
58
67
  setTab: (tab: ConnectionTab) => void;
68
+ setFocusTargetEntity: (entity: string | null) => void;
59
69
  setEndpoints: (items: IntegrationEndpoint[], total: number) => void;
60
70
  /**
61
71
  * Just the count, without the rows.
@@ -6,6 +6,7 @@ export const initial = {
6
6
  sourcesLoading: false,
7
7
  selectedSourceId: null,
8
8
  tab: 'connection',
9
+ focusTargetEntity: null,
9
10
  endpoints: [],
10
11
  endpointsTotal: 0,
11
12
  endpointsPage: 1,
@@ -98,6 +98,14 @@ export type IntegrationsPageProps = {
98
98
  subtitle?: string;
99
99
  className?: string;
100
100
  actions: IntegrationsActions;
101
+ /**
102
+ * The table a caller arrived asking about, e.g. from "fill this from a
103
+ * source" on a management screen.
104
+ *
105
+ * The panel opens on that table's mapping — or on a new one already aimed at
106
+ * it — instead of on a chooser the caller has already answered.
107
+ */
108
+ focusTargetEntity?: string;
101
109
  /**
102
110
  * The panel's own words, in the app's language.
103
111
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.815",
3
+ "version": "0.9.816",
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",