nucleus-core-ts 0.9.800 → 0.9.801

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,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 { describedBy } from './formSupport';
6
7
  import { Field } from './Primitives';
@@ -9,16 +10,16 @@ import { Field } from './Primitives';
9
10
  * decide whether it is used and how carefully.
10
11
  */ const theme = integrationsPageTheme;
11
12
  const CHECKBOX = 'h-4 w-4 shrink-0 accent-sky-600 dark:accent-sky-500';
12
- const BASE_URL_HINT = 'The scheme is free. https:// and plain http:// are both accepted and neither is rewritten — calling a service inside the same cluster over http, for example http://hr-service:8080, is a legitimate setup.';
13
13
  export function ConnectionBasicFields({ draft, errors, disabled, onChange }) {
14
+ const labels = useLabels();
14
15
  return /*#__PURE__*/ _jsxs("section", {
15
- "aria-label": "Connection details",
16
+ "aria-label": labels.connectionForm.sectionLabel,
16
17
  className: "flex flex-col gap-3",
17
18
  children: [
18
19
  /*#__PURE__*/ _jsx(Field, {
19
20
  error: errors.name,
20
21
  htmlFor: "connection-name",
21
- label: "Name",
22
+ label: labels.connectionForm.name,
22
23
  children: /*#__PURE__*/ _jsx("input", {
23
24
  "aria-describedby": describedBy('connection-name', false, Boolean(errors.name)),
24
25
  className: theme.field.input,
@@ -27,16 +28,16 @@ export function ConnectionBasicFields({ draft, errors, disabled, onChange }) {
27
28
  onChange: (event)=>onChange({
28
29
  name: event.target.value
29
30
  }),
30
- placeholder: "Directory service",
31
+ placeholder: labels.connectionForm.namePlaceholder,
31
32
  type: "text",
32
33
  value: draft.name
33
34
  })
34
35
  }),
35
36
  /*#__PURE__*/ _jsx(Field, {
36
37
  error: errors.slug,
37
- hint: "Used to refer to this connection elsewhere. It does not change once things point at it.",
38
+ hint: labels.connectionForm.slugHint,
38
39
  htmlFor: "connection-slug",
39
- label: "Slug",
40
+ label: labels.connectionForm.slug,
40
41
  children: /*#__PURE__*/ _jsx("input", {
41
42
  "aria-describedby": describedBy('connection-slug', true, Boolean(errors.slug)),
42
43
  className: theme.field.input,
@@ -52,9 +53,9 @@ export function ConnectionBasicFields({ draft, errors, disabled, onChange }) {
52
53
  }),
53
54
  /*#__PURE__*/ _jsx(Field, {
54
55
  error: errors.baseUrl,
55
- hint: BASE_URL_HINT,
56
+ hint: labels.connectionForm.baseUrlHint,
56
57
  htmlFor: "connection-base-url",
57
- label: "Base URL",
58
+ label: labels.connectionForm.baseUrl,
58
59
  children: /*#__PURE__*/ _jsx("input", {
59
60
  "aria-describedby": describedBy('connection-base-url', true, Boolean(errors.baseUrl)),
60
61
  className: theme.field.input,
@@ -69,9 +70,9 @@ export function ConnectionBasicFields({ draft, errors, disabled, onChange }) {
69
70
  })
70
71
  }),
71
72
  /*#__PURE__*/ _jsx(Field, {
72
- hint: "For whoever has to work out what this connection is, months from now.",
73
+ hint: labels.connectionForm.descriptionHint,
73
74
  htmlFor: "connection-description",
74
- label: "Description",
75
+ label: labels.connectionForm.description,
75
76
  children: /*#__PURE__*/ _jsx("textarea", {
76
77
  "aria-describedby": describedBy('connection-description', true, false),
77
78
  className: cn(theme.field.input, 'h-20 py-2 sm:h-20'),
@@ -80,7 +81,7 @@ export function ConnectionBasicFields({ draft, errors, disabled, onChange }) {
80
81
  onChange: (event)=>onChange({
81
82
  description: event.target.value
82
83
  }),
83
- placeholder: "Read-only employee directory used by the nightly import.",
84
+ placeholder: labels.connectionForm.descriptionPlaceholder,
84
85
  value: draft.description
85
86
  })
86
87
  }),
@@ -102,13 +103,13 @@ export function ConnectionBasicFields({ draft, errors, disabled, onChange }) {
102
103
  }),
103
104
  type: "checkbox"
104
105
  }),
105
- "Enabled"
106
+ labels.connectionForm.enabled
106
107
  ]
107
108
  }),
108
109
  /*#__PURE__*/ _jsx("span", {
109
110
  className: theme.field.hint,
110
111
  id: "connection-enabled-hint",
111
- children: "Turning this off stops every endpoint and import that goes through this connection. The settings are kept."
112
+ children: labels.connectionForm.enabledHint
112
113
  })
113
114
  ]
114
115
  }),
@@ -130,21 +131,21 @@ export function ConnectionBasicFields({ draft, errors, disabled, onChange }) {
130
131
  }),
131
132
  type: "checkbox"
132
133
  }),
133
- "Verify TLS certificate"
134
+ labels.connectionForm.tlsVerify
134
135
  ]
135
136
  }),
136
137
  /*#__PURE__*/ _jsx("span", {
137
138
  className: theme.field.hint,
138
139
  id: "connection-tls-verify-hint",
139
- children: draft.tlsVerify ? 'On. Unchecking it applies to requests made through THIS connection only — no other connection and nothing else in the app is affected.' : 'Off. Certificates are not checked for requests made through THIS connection only. Every other connection still verifies. Use it for an internal service with its own certificate authority.'
140
+ children: draft.tlsVerify ? labels.connectionForm.tlsVerifyOnHint : labels.connectionForm.tlsVerifyOffHint
140
141
  })
141
142
  ]
142
143
  }),
143
144
  /*#__PURE__*/ _jsx(Field, {
144
145
  error: errors.timeoutMs,
145
- hint: "How long one request may take before it is given up on. Empty means the server default.",
146
+ hint: labels.connectionForm.timeoutHint,
146
147
  htmlFor: "connection-timeout",
147
- label: "Timeout (ms)",
148
+ label: labels.connectionForm.timeout,
148
149
  children: /*#__PURE__*/ _jsx("input", {
149
150
  "aria-describedby": describedBy('connection-timeout', true, Boolean(errors.timeoutMs)),
150
151
  className: theme.field.input,
@@ -45,6 +45,35 @@ export function IntegrationsPage({ title = 'Integrations', subtitle = 'Connect a
45
45
  const handleSourceSaved = ()=>{
46
46
  setSourcesReloadToken((token)=>token + 1);
47
47
  };
48
+ /**
49
+ * How far this connection has got, asked for directly.
50
+ *
51
+ * The step strip promises to say where you are, so it cannot depend on having
52
+ * VISITED the step that counts — on a fresh load every total was zero and the
53
+ * strip told an operator with five calls, three mappings and two finished
54
+ * imports to "finish the previous step first". Three counting requests, one
55
+ * row each, are the price of a strip that is telling the truth.
56
+ */ const loadProgress = useEffectEvent((sourceId)=>{
57
+ const one = {
58
+ page: 1,
59
+ limit: 1
60
+ };
61
+ const total = (result)=>result.meta?.totalItems ?? 0;
62
+ actions.listEndpoints(sourceId, one).then((r)=>store.setEndpointsTotal(total(r))).catch(()=>{});
63
+ actions.listMappings(sourceId, one).then((r)=>store.setMappingsTotal(total(r))).catch(()=>{});
64
+ // Runs are deliberately NOT counted here. A run records the mapping it came
65
+ // from and nothing about the connection, so the only count available is
66
+ // every run of every connection — which on the second connection would be
67
+ // a number that looks like this one's and is not. The Runs tab sets the
68
+ // real figure for the mapping it is showing; until then the step says
69
+ // nothing rather than something wrong.
70
+ });
71
+ useEffect(()=>{
72
+ if (store.selectedSourceId) loadProgress(store.selectedSourceId);
73
+ }, [
74
+ store.selectedSourceId,
75
+ sourcesReloadToken
76
+ ]);
48
77
  // One measured entrance. Anything longer makes an operator wait to read counts
49
78
  // they came here to check.
50
79
  useGSAP(()=>{
@@ -49,6 +49,9 @@ export const { useStore: useIntegrationsStore } = createStore(initial, {
49
49
  store.endpointsTotal = total;
50
50
  store.endpointsLoading = false;
51
51
  }),
52
+ setEndpointsTotal: (store)=>(total)=>{
53
+ store.endpointsTotal = total;
54
+ },
52
55
  setEndpointsPage: (store)=>(page)=>{
53
56
  store.endpointsPage = page;
54
57
  },
@@ -70,6 +73,9 @@ export const { useStore: useIntegrationsStore } = createStore(initial, {
70
73
  store.mappingsTotal = total;
71
74
  store.mappingsLoading = false;
72
75
  }),
76
+ setMappingsTotal: (store)=>(total)=>{
77
+ store.mappingsTotal = total;
78
+ },
73
79
  setMappingsPage: (store)=>(page)=>{
74
80
  store.mappingsPage = page;
75
81
  },
@@ -97,6 +103,9 @@ export const { useStore: useIntegrationsStore } = createStore(initial, {
97
103
  store.runsTotal = total;
98
104
  store.runsLoading = false;
99
105
  }),
106
+ setRunsTotal: (store)=>(total)=>{
107
+ store.runsTotal = total;
108
+ },
100
109
  setRunsPage: (store)=>(page)=>{
101
110
  store.runsPage = page;
102
111
  },
@@ -57,16 +57,25 @@ export type Actions = {
57
57
  selectSource: (id: string | null) => void;
58
58
  setTab: (tab: ConnectionTab) => void;
59
59
  setEndpoints: (items: IntegrationEndpoint[], total: number) => void;
60
+ /**
61
+ * Just the count, without the rows.
62
+ *
63
+ * The step strip reports how far a connection has got, and it cannot wait for
64
+ * the tab that lists the rows to have been opened — so it counts directly.
65
+ */
66
+ setEndpointsTotal: (total: number) => void;
60
67
  setEndpointsPage: (page: number) => void;
61
68
  setEndpointsSearch: (search: string) => void;
62
69
  setEndpointsLoading: (loading: boolean) => void;
63
70
  selectEndpoint: (id: string | null) => void;
64
71
  setMappings: (items: IntegrationMapping[], total: number) => void;
72
+ setMappingsTotal: (total: number) => void;
65
73
  setMappingsPage: (page: number) => void;
66
74
  setMappingsSearch: (search: string) => void;
67
75
  setMappingsLoading: (loading: boolean) => void;
68
76
  selectMapping: (id: string | null) => void;
69
77
  setRuns: (items: IntegrationRun[], total: number) => void;
78
+ setRunsTotal: (total: number) => void;
70
79
  setRunsPage: (page: number) => void;
71
80
  setRunsLoading: (loading: boolean) => void;
72
81
  setSample: (sample: EndpointSampleValue | null) => void;
@@ -80,6 +80,26 @@ export type IntegrationsLabels = {
80
80
  skipped: string;
81
81
  dedupKey: string;
82
82
  };
83
+ /** The connection form — the first screen anyone meets. */
84
+ connectionForm: {
85
+ sectionLabel: string;
86
+ name: string;
87
+ namePlaceholder: string;
88
+ slug: string;
89
+ slugHint: string;
90
+ baseUrl: string;
91
+ baseUrlHint: string;
92
+ description: string;
93
+ descriptionHint: string;
94
+ descriptionPlaceholder: string;
95
+ enabled: string;
96
+ enabledHint: string;
97
+ tlsVerify: string;
98
+ tlsVerifyOnHint: string;
99
+ tlsVerifyOffHint: string;
100
+ timeout: string;
101
+ timeoutHint: string;
102
+ };
83
103
  writeMode: {
84
104
  upsert: string;
85
105
  append: string;
@@ -74,6 +74,25 @@
74
74
  skipped: 'Skipped',
75
75
  dedupKey: 'Dedup key'
76
76
  },
77
+ connectionForm: {
78
+ sectionLabel: 'Connection details',
79
+ name: 'Name',
80
+ namePlaceholder: 'Directory service',
81
+ slug: 'Slug',
82
+ slugHint: 'Used to refer to this connection elsewhere. It does not change once things point at it.',
83
+ baseUrl: 'Base URL',
84
+ baseUrlHint: 'The scheme is free. https:// and plain http:// are both accepted and neither is rewritten — calling a service inside the same cluster over http, for example http://hr-service:8080, is a legitimate setup.',
85
+ description: 'Description',
86
+ descriptionHint: 'For whoever has to work out what this connection is, months from now.',
87
+ descriptionPlaceholder: 'Read-only employee directory used by the nightly import.',
88
+ enabled: 'Enabled',
89
+ enabledHint: 'Turning this off stops every endpoint and import that goes through this connection. The settings are kept.',
90
+ tlsVerify: 'Verify TLS certificate',
91
+ tlsVerifyOnHint: 'On. Unchecking it applies to requests made through THIS connection only — no other connection and nothing else in the app is affected.',
92
+ tlsVerifyOffHint: 'Off. Certificates are not checked for requests made through THIS connection only. Every other connection still verifies. Use it for an internal service with its own certificate authority.',
93
+ timeout: 'Timeout (ms)',
94
+ timeoutHint: 'How long one request may take before it is given up on. Empty means the server default.'
95
+ },
77
96
  writeMode: {
78
97
  upsert: 'update what matches, insert the rest',
79
98
  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.800",
3
+ "version": "0.9.801",
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",