nucleus-core-ts 0.9.885 → 0.9.887

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.
Files changed (43) hide show
  1. package/dist/.build-ok +1 -1
  2. package/dist/fe/components/Captcha/components/Captcha.d.ts +1 -1
  3. package/dist/fe/components/Captcha/components/Captcha.js +26 -24
  4. package/dist/fe/components/Captcha/index.d.ts +1 -0
  5. package/dist/fe/components/Captcha/index.js +1 -0
  6. package/dist/fe/components/Captcha/labels.d.ts +44 -0
  7. package/dist/fe/components/Captcha/labels.js +33 -0
  8. package/dist/fe/components/Captcha/types.d.ts +4 -0
  9. package/dist/fe/components/IntegrationsPage/components/ConnectionDelete.js +2 -1
  10. package/dist/fe/components/IntegrationsPage/components/ConnectionExport.js +7 -5
  11. package/dist/fe/components/IntegrationsPage/components/ConnectionForm.js +3 -2
  12. package/dist/fe/components/IntegrationsPage/components/ConnectionList.js +2 -1
  13. package/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +3 -2
  14. package/dist/fe/components/IntegrationsPage/components/ConnectionTransfer.js +7 -5
  15. package/dist/fe/components/IntegrationsPage/components/CredentialFields.js +6 -5
  16. package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.js +2 -1
  17. package/dist/fe/components/IntegrationsPage/components/EndpointForm.js +5 -4
  18. package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +2 -1
  19. package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +12 -75
  20. package/dist/fe/components/IntegrationsPage/components/MappingAccountFields.js +19 -17
  21. package/dist/fe/components/IntegrationsPage/components/MappingEditor.js +4 -4
  22. package/dist/fe/components/IntegrationsPage/components/MappingList.js +3 -2
  23. package/dist/fe/components/IntegrationsPage/components/MappingPasswordField.js +8 -5
  24. package/dist/fe/components/IntegrationsPage/components/MappingScheduleFields.js +37 -29
  25. package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +3 -3
  26. package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +9 -9
  27. package/dist/fe/components/IntegrationsPage/components/PaginationFields.js +137 -124
  28. package/dist/fe/components/IntegrationsPage/components/ReferenceCheckRows.js +42 -39
  29. package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.js +2 -2
  30. package/dist/fe/components/IntegrationsPage/components/RunHistory.js +1 -1
  31. package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.js +4 -2
  32. package/dist/fe/components/IntegrationsPage/components/RunPanel.js +6 -6
  33. package/dist/fe/components/IntegrationsPage/components/RunPasswordField.js +4 -2
  34. package/dist/fe/components/IntegrationsPage/components/RunPlanSuggestions.js +9 -33
  35. package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.js +1 -1
  36. package/dist/fe/components/IntegrationsPage/components/RunProgressBar.js +12 -10
  37. package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.js +3 -1
  38. package/dist/fe/components/IntegrationsPage/components/Skeletons.js +4 -3
  39. package/dist/fe/components/IntegrationsPage/text/index.d.ts +193 -0
  40. package/dist/fe/components/IntegrationsPage/text/index.js +171 -0
  41. package/dist/fe/index.d.ts +2 -2
  42. package/dist/fe/index.js +1 -1
  43. package/package.json +1 -1
package/dist/.build-ok CHANGED
@@ -1 +1 @@
1
- 0.9.885
1
+ 0.9.887
@@ -1,3 +1,3 @@
1
1
  import type { ReactElement } from 'react';
2
2
  import type { CaptchaProps } from '../types';
3
- export declare function Captcha({ generateAction, config, onChallengeLoad, onAnswerChange, disabled, autoGenerate, className, label, errorMessage, themeOverride, }: CaptchaProps): ReactElement;
3
+ export declare function Captcha({ generateAction, config, onChallengeLoad, onAnswerChange, disabled, autoGenerate, className, label, labels: labelOverrides, errorMessage, themeOverride, }: CaptchaProps): ReactElement;
@@ -4,6 +4,7 @@ import { useGSAP } from '@gsap/react';
4
4
  import gsap from 'gsap';
5
5
  import { useEffect, useEffectEvent, useRef, useState } from 'react';
6
6
  import { cn } from '../../../utils/cn';
7
+ import { DEFAULT_CAPTCHA_LABELS } from '../labels';
7
8
  import { captchaTheme } from '../theme';
8
9
  gsap.registerPlugin(useGSAP);
9
10
  function mergeTheme(base, override) {
@@ -62,7 +63,14 @@ const RefreshIcon = ({ className, iconRef })=>/*#__PURE__*/ _jsxs("svg", {
62
63
  })
63
64
  ]
64
65
  });
65
- export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChange, disabled = false, autoGenerate = true, className, label = 'Security Check', errorMessage, themeOverride }) {
66
+ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChange, disabled = false, autoGenerate = true, className, label, labels: labelOverrides, errorMessage, themeOverride }) {
67
+ // `label` came first and some installs pass it; it still wins for the
68
+ // heading so nothing that already works changes.
69
+ const labels = {
70
+ ...DEFAULT_CAPTCHA_LABELS,
71
+ ...labelOverrides
72
+ };
73
+ const heading = label ?? labels.heading;
66
74
  const theme = themeOverride ? mergeTheme(captchaTheme, themeOverride) : captchaTheme;
67
75
  const containerRef = useRef(null);
68
76
  const challengeRef = useRef(null);
@@ -89,9 +97,8 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
89
97
  },
90
98
  onAfterHandle: (response)=>{
91
99
  const { data } = response;
92
- console.log('[DEBUG]:', response);
93
100
  if (!data || data.rateLimited) {
94
- setRateLimitError(response.message ?? 'Too many requests. Please try again later.');
101
+ setRateLimitError(response.message ?? labels.tooManyRequests);
95
102
  setChallengeData(null);
96
103
  return;
97
104
  }
@@ -111,7 +118,7 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
111
118
  },
112
119
  onErrorHandle: (_error, code)=>{
113
120
  if (code === 429) {
114
- setRateLimitError('Too many requests. Please try again later.');
121
+ setRateLimitError(labels.tooManyRequests);
115
122
  }
116
123
  setChallengeData(null);
117
124
  }
@@ -186,17 +193,17 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
186
193
  return `${mins}:${secs.toString().padStart(2, '0')}`;
187
194
  };
188
195
  const getPlaceholder = ()=>{
189
- if (!challenge) return 'Enter answer';
196
+ if (!challenge) return labels.enterAnswer;
190
197
  switch(challenge.type){
191
198
  case 'math':
192
- return 'Enter the result';
199
+ return labels.enterResult;
193
200
  case 'image':
194
201
  case 'text':
195
- return 'Enter the text shown';
202
+ return labels.enterTextShown;
196
203
  case 'puzzle':
197
- return 'Enter piece order (e.g., 0,1,2,3)';
204
+ return labels.enterPieceOrder;
198
205
  default:
199
- return 'Enter answer';
206
+ return labels.enterAnswer;
200
207
  }
201
208
  };
202
209
  if (config?.enabled === false) {
@@ -210,7 +217,7 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
210
217
  children: [
211
218
  /*#__PURE__*/ _jsx("span", {
212
219
  className: theme.label.base,
213
- children: label
220
+ children: heading
214
221
  }),
215
222
  /*#__PURE__*/ _jsx("div", {
216
223
  ref: challengeRef,
@@ -234,7 +241,7 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
234
241
  className: theme.challenge.image.wrapper,
235
242
  children: /*#__PURE__*/ _jsx("img", {
236
243
  src: challenge.imageData,
237
- alt: "Captcha",
244
+ alt: labels.imageAlt,
238
245
  className: theme.challenge.image.img,
239
246
  draggable: false
240
247
  })
@@ -242,14 +249,9 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
242
249
  challenge.type === 'puzzle' && challenge.puzzleData && /*#__PURE__*/ _jsxs("div", {
243
250
  className: theme.challenge.puzzle.wrapper,
244
251
  children: [
245
- /*#__PURE__*/ _jsxs("p", {
252
+ /*#__PURE__*/ _jsx("p", {
246
253
  className: theme.challenge.puzzle.instruction,
247
- children: [
248
- "Click the pieces in the correct order (0 to",
249
- ' ',
250
- challenge.puzzleData.pieces.length - 1,
251
- ")"
252
- ]
254
+ children: labels.puzzleInstruction.replace('{last}', String(challenge.puzzleData.pieces.length - 1))
253
255
  }),
254
256
  /*#__PURE__*/ _jsx("div", {
255
257
  className: theme.challenge.puzzle.grid,
@@ -272,7 +274,7 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
272
274
  type: "button",
273
275
  onClick: handleClearPuzzleOrder,
274
276
  className: theme.challenge.puzzle.clearButton,
275
- children: "Clear"
277
+ children: labels.clearOrder
276
278
  })
277
279
  ]
278
280
  })
@@ -283,7 +285,7 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
283
285
  className: theme.loading.wrapper,
284
286
  children: /*#__PURE__*/ _jsx("p", {
285
287
  className: "text-sm text-zinc-500",
286
- children: "Click refresh to load captcha"
288
+ children: labels.clickRefresh
287
289
  })
288
290
  })
289
291
  }),
@@ -293,7 +295,7 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
293
295
  /*#__PURE__*/ _jsx("label", {
294
296
  htmlFor: "captcha-answer",
295
297
  className: "sr-only",
296
- children: label
298
+ children: heading
297
299
  }),
298
300
  /*#__PURE__*/ _jsx("input", {
299
301
  id: "captcha-answer",
@@ -320,13 +322,13 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
320
322
  iconRef: iconRef
321
323
  }),
322
324
  /*#__PURE__*/ _jsx("span", {
323
- children: "New challenge"
325
+ children: labels.newChallenge
324
326
  })
325
327
  ]
326
328
  }),
327
329
  timeRemaining !== null && /*#__PURE__*/ _jsx("span", {
328
330
  className: isExpired ? theme.actions.timerExpired : theme.actions.timer,
329
- children: isExpired ? 'Expired' : `Expires in ${formatTime(timeRemaining)}`
331
+ children: isExpired ? labels.expired : labels.expiresIn.replace('{time}', formatTime(timeRemaining))
330
332
  })
331
333
  ]
332
334
  }),
@@ -334,7 +336,7 @@ export function Captcha({ generateAction, config, onChallengeLoad, onAnswerChang
334
336
  className: theme.error.container,
335
337
  children: /*#__PURE__*/ _jsx("p", {
336
338
  className: theme.error.text,
337
- children: rateLimitError ? rateLimitError : isExpired ? 'Captcha expired. Please refresh.' : errorMessage
339
+ children: rateLimitError ? rateLimitError : isExpired ? labels.expiredMessage : errorMessage
338
340
  })
339
341
  })
340
342
  ]
@@ -1,3 +1,4 @@
1
1
  export { Captcha } from './components/Captcha';
2
+ export { type CaptchaLabels, DEFAULT_CAPTCHA_LABELS } from './labels';
2
3
  export { captchaTheme } from './theme';
3
4
  export type { CaptchaAction, CaptchaChallenge, CaptchaChallengeData, CaptchaConfig, CaptchaDifficulty, CaptchaProps, CaptchaState, CaptchaThemeOverride, CaptchaType, } from './types';
@@ -1,2 +1,3 @@
1
1
  export { Captcha } from './components/Captcha';
2
+ export { DEFAULT_CAPTCHA_LABELS } from './labels';
2
3
  export { captchaTheme } from './theme';
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Every word the security check says.
3
+ *
4
+ * This is the most-read component in the kit and the one nobody thought to
5
+ * translate: it sits on the sign-in form, so on an installation of 879 people
6
+ * it is shown several hundred times a morning. A portal could pass `label` and
7
+ * change the heading — and the box under the heading still said "Enter the
8
+ * result", "New challenge", "Expires in 1:30" and, when somebody was slow,
9
+ * "Captcha expired. Please refresh." Four sentences of English on the first
10
+ * screen of a portal that is otherwise entirely in its own language.
11
+ *
12
+ * The placeholders are per challenge type because the instruction is different
13
+ * for each: a sum wants its result, an image wants the letters in it, and the
14
+ * puzzle wants an order. `expiresIn` takes the formatted clock as `{time}` so a
15
+ * translation can put it where its own grammar wants it.
16
+ *
17
+ * Defaults stay English, so an installation that passes nothing is unchanged.
18
+ */
19
+ export type CaptchaLabels = {
20
+ /** The heading over the box, and the input's screen-reader label. */
21
+ heading: string;
22
+ /** Before a challenge has been asked for. */
23
+ clickRefresh: string;
24
+ /** The answer box, by challenge type. */
25
+ enterAnswer: string;
26
+ enterResult: string;
27
+ enterTextShown: string;
28
+ enterPieceOrder: string;
29
+ /** The picture challenge's alternative text. */
30
+ imageAlt: string;
31
+ /** The puzzle: its instruction takes the highest piece number as `{last}`. */
32
+ puzzleInstruction: string;
33
+ clearOrder: string;
34
+ /** Asking for a different challenge. */
35
+ newChallenge: string;
36
+ /** The countdown. `expiresIn` takes the clock as `{time}`. */
37
+ expiresIn: string;
38
+ expired: string;
39
+ /** What the box says once the clock has run out. */
40
+ expiredMessage: string;
41
+ /** Asked for too many challenges too quickly. */
42
+ tooManyRequests: string;
43
+ };
44
+ export declare const DEFAULT_CAPTCHA_LABELS: CaptchaLabels;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Every word the security check says.
3
+ *
4
+ * This is the most-read component in the kit and the one nobody thought to
5
+ * translate: it sits on the sign-in form, so on an installation of 879 people
6
+ * it is shown several hundred times a morning. A portal could pass `label` and
7
+ * change the heading — and the box under the heading still said "Enter the
8
+ * result", "New challenge", "Expires in 1:30" and, when somebody was slow,
9
+ * "Captcha expired. Please refresh." Four sentences of English on the first
10
+ * screen of a portal that is otherwise entirely in its own language.
11
+ *
12
+ * The placeholders are per challenge type because the instruction is different
13
+ * for each: a sum wants its result, an image wants the letters in it, and the
14
+ * puzzle wants an order. `expiresIn` takes the formatted clock as `{time}` so a
15
+ * translation can put it where its own grammar wants it.
16
+ *
17
+ * Defaults stay English, so an installation that passes nothing is unchanged.
18
+ */ export const DEFAULT_CAPTCHA_LABELS = {
19
+ heading: 'Security Check',
20
+ clickRefresh: 'Click refresh to load captcha',
21
+ enterAnswer: 'Enter answer',
22
+ enterResult: 'Enter the result',
23
+ enterTextShown: 'Enter the text shown',
24
+ enterPieceOrder: 'Enter piece order (e.g., 0,1,2,3)',
25
+ imageAlt: 'Captcha',
26
+ puzzleInstruction: 'Click the pieces in the correct order (0 to {last})',
27
+ clearOrder: 'Clear',
28
+ newChallenge: 'New challenge',
29
+ expiresIn: 'Expires in {time}',
30
+ expired: 'Expired',
31
+ expiredMessage: 'Captcha expired. Please refresh.',
32
+ tooManyRequests: 'Too many requests. Please try again later.'
33
+ };
@@ -1,3 +1,4 @@
1
+ import type { CaptchaLabels } from './labels';
1
2
  export type CaptchaType = 'math' | 'image' | 'puzzle' | 'text';
2
3
  export type CaptchaDifficulty = 'easy' | 'medium' | 'hard';
3
4
  export interface CaptchaChallengeData {
@@ -110,7 +111,10 @@ export interface CaptchaProps {
110
111
  disabled?: boolean;
111
112
  autoGenerate?: boolean;
112
113
  className?: string;
114
+ /** The heading only. Kept because installs already pass it; `labels` covers the rest. */
113
115
  label?: string;
116
+ /** Every other word the box says — see `../labels`. */
117
+ labels?: Partial<CaptchaLabels>;
114
118
  errorMessage?: string;
115
119
  themeOverride?: CaptchaThemeOverride;
116
120
  }
@@ -7,6 +7,7 @@ import { integrationsPageTheme as theme } from '../theme';
7
7
  import { deleteIsConfirmed } from './deleteIsConfirmed';
8
8
  import { Notice } from './Primitives';
9
9
  export function ConnectionDelete({ actions, source, onDeleted }) {
10
+ const t = useLabels().errors;
10
11
  const labels = useLabels();
11
12
  const store = useIntegrationsStore();
12
13
  const [asking, setAsking] = useState(false);
@@ -30,7 +31,7 @@ export function ConnectionDelete({ actions, source, onDeleted }) {
30
31
  onDeleted?.();
31
32
  }).catch((error)=>{
32
33
  setBusy(false);
33
- store.setError(error instanceof Error ? error.message : 'The request failed.');
34
+ store.setError(error instanceof Error ? error.message : t.requestFailed);
34
35
  });
35
36
  };
36
37
  return /*#__PURE__*/ _jsxs("section", {
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from 'react';
4
4
  import { useIntegrationsStore } from '../store';
5
+ import { useLabels } from '../text/context';
5
6
  import { integrationsPageTheme as theme } from '../theme';
6
7
  import { Notice } from './Primitives';
7
8
  /**
@@ -29,6 +30,7 @@ import { Notice } from './Primitives';
29
30
  * is a confusing half hour otherwise.
30
31
  */ /** A filename somebody can still recognise a week later. */ const fileNameFor = (slug)=>`integration-${slug || 'connection'}.json`;
31
32
  export function ConnectionExport({ actions, source }) {
33
+ const t = useLabels().transfer;
32
34
  const store = useIntegrationsStore();
33
35
  const [busy, setBusy] = useState(false);
34
36
  const [note, setNote] = useState(null);
@@ -40,7 +42,7 @@ export function ConnectionExport({ actions, source }) {
40
42
  actions.exportConnection(source.id).then((document)=>{
41
43
  setBusy(false);
42
44
  if (!document) {
43
- store.setError('The connection could not be exported.');
45
+ store.setError(t.exportFailed);
44
46
  return;
45
47
  }
46
48
  // Written from the browser rather than served as a download, so the file
@@ -56,10 +58,10 @@ export function ConnectionExport({ actions, source }) {
56
58
  link.download = fileNameFor(source.slug);
57
59
  link.click();
58
60
  URL.revokeObjectURL(url);
59
- setNote('Exported. Credentials are not in the file — enter them wherever it lands.');
61
+ setNote(t.exported);
60
62
  }).catch((error)=>{
61
63
  setBusy(false);
62
- store.setError(error instanceof Error ? error.message : 'The request failed.');
64
+ store.setError(error instanceof Error ? error.message : t.requestFailed);
63
65
  });
64
66
  };
65
67
  return /*#__PURE__*/ _jsxs("section", {
@@ -70,7 +72,7 @@ export function ConnectionExport({ actions, source }) {
70
72
  children: [
71
73
  /*#__PURE__*/ _jsx("h3", {
72
74
  className: theme.card.title,
73
- children: "Take this connection elsewhere"
75
+ children: t.exportTitle
74
76
  }),
75
77
  /*#__PURE__*/ _jsx("button", {
76
78
  className: theme.button.secondary,
@@ -89,7 +91,7 @@ export function ConnectionExport({ actions, source }) {
89
91
  children: "One file holding this connection, its calls and its mappings — enough to rebuild the whole integration in another installation. Bring it in there with Import, on the connections screen."
90
92
  }),
91
93
  /*#__PURE__*/ _jsx(Notice, {
92
- title: "No secrets travel in the file",
94
+ title: t.noSecretsTitle,
93
95
  tone: "info",
94
96
  children: "Tokens, passwords and keys are left out on purpose. Whatever this connection authenticates with has to be entered again wherever the file lands."
95
97
  }),
@@ -18,6 +18,7 @@ import { CardSkeleton } from './Skeletons';
18
18
  * of what was stored and what is being typed.
19
19
  */ const theme = integrationsPageTheme;
20
20
  export function ConnectionForm({ actions, source, loading = false, onSaved }) {
21
+ const t = useLabels().errors;
21
22
  const labels = useLabels();
22
23
  const store = useIntegrationsStore();
23
24
  const [draft, setDraft] = useState(()=>source ? draftFromSource(source) : emptyConnectionDraft);
@@ -51,7 +52,7 @@ export function ConnectionForm({ actions, source, loading = false, onSaved }) {
51
52
  actions.saveSource(draftToSource(draft, source?.id)).then((saved)=>{
52
53
  setSaving(false);
53
54
  if (!saved) {
54
- store.setError('The connection was not saved. The server did not return the record.');
55
+ store.setError(t.connectionNotSaved);
55
56
  return;
56
57
  }
57
58
  // Told about the record BEFORE it is selected. The caller is what puts
@@ -84,7 +85,7 @@ export function ConnectionForm({ actions, source, loading = false, onSaved }) {
84
85
  if (!source) store.selectSource(saved.id);
85
86
  }).catch((reason)=>{
86
87
  setSaving(false);
87
- store.setError(errorText(reason, 'The connection could not be saved.'));
88
+ store.setError(errorText(reason, t.connectionSaveFailed));
88
89
  });
89
90
  };
90
91
  if (loading) return /*#__PURE__*/ _jsx(CardSkeleton, {});
@@ -20,6 +20,7 @@ import { useInfiniteList } from './useInfiniteList';
20
20
  const PAGE_SIZE = 20;
21
21
  const SEARCH_DEBOUNCE_MS = 300;
22
22
  export function ConnectionList({ actions, onNewConnection, reloadToken = 0 }) {
23
+ const t = useLabels().errors;
23
24
  const store = useIntegrationsStore();
24
25
  const labels = useLabels();
25
26
  const search = store.sourcesSearch;
@@ -200,7 +201,7 @@ export function ConnectionList({ actions, onNewConnection, reloadToken = 0 }) {
200
201
  }),
201
202
  list.loading && list.items.length > 0 ? /*#__PURE__*/ _jsx("p", {
202
203
  className: theme.field.hint,
203
- children: "Loading more…"
204
+ children: t.loadingMore
204
205
  }) : null
205
206
  ]
206
207
  }));
@@ -16,6 +16,7 @@ import { CardSkeleton } from './Skeletons';
16
16
  * sends the operator back to guessing.
17
17
  */ const theme = integrationsPageTheme;
18
18
  export function ConnectionTest({ actions, source, loading = false }) {
19
+ const t = useLabels().errors;
19
20
  const labels = useLabels();
20
21
  const [testing, setTesting] = useState(false);
21
22
  const [result, setResult] = useState(null);
@@ -62,7 +63,7 @@ export function ConnectionTest({ actions, source, loading = false }) {
62
63
  setTesting(false);
63
64
  setResult({
64
65
  ok: false,
65
- error: errorText(reason, 'The test could not be run.')
66
+ error: errorText(reason, t.testFailed)
66
67
  });
67
68
  });
68
69
  };
@@ -115,7 +116,7 @@ export function ConnectionTest({ actions, source, loading = false }) {
115
116
  tone: "danger",
116
117
  children: /*#__PURE__*/ _jsx("p", {
117
118
  className: cn(theme.notice.body, 'whitespace-pre-wrap break-words font-mono'),
118
- children: result.error ?? 'The server reported a failure without a message.'
119
+ children: result.error ?? t.testFailedNoMessage
119
120
  })
120
121
  }) : null
121
122
  ]
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useRef, 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 { Notice } from './Primitives';
8
9
  /**
@@ -24,8 +25,9 @@ import { Notice } from './Primitives';
24
25
  * see ConnectionExport. Importing is the one that belongs here, because
25
26
  * importing is what you do when you have nothing chosen yet.
26
27
  */ const theme = integrationsPageTheme;
27
- const messageOf = (error)=>error instanceof Error ? error.message : 'The request failed.';
28
+ const messageOf = (error, fallback)=>error instanceof Error ? error.message : fallback;
28
29
  export function ConnectionTransfer({ actions, onImported }) {
30
+ const t = useLabels().transfer;
29
31
  const store = useIntegrationsStore();
30
32
  const fileRef = useRef(null);
31
33
  const [busy, setBusy] = useState(false);
@@ -44,7 +46,7 @@ export function ConnectionTransfer({ actions, onImported }) {
44
46
  setProblems([
45
47
  {
46
48
  path: file.name,
47
- message: 'This file is not readable as JSON.'
49
+ message: t.notJson
48
50
  }
49
51
  ]);
50
52
  return;
@@ -65,7 +67,7 @@ export function ConnectionTransfer({ actions, onImported }) {
65
67
  });
66
68
  }).catch((error)=>{
67
69
  setBusy(false);
68
- store.setError(messageOf(error));
70
+ store.setError(messageOf(error, t.requestFailed));
69
71
  });
70
72
  };
71
73
  return /*#__PURE__*/ _jsxs("section", {
@@ -76,7 +78,7 @@ export function ConnectionTransfer({ actions, onImported }) {
76
78
  disabled: busy,
77
79
  onClick: ()=>fileRef.current?.click(),
78
80
  type: "button",
79
- children: busy ? 'Working…' : 'Import a connection file'
81
+ children: busy ? t.working : t.importButton
80
82
  }),
81
83
  /*#__PURE__*/ _jsx("input", {
82
84
  accept: "application/json,.json",
@@ -96,7 +98,7 @@ export function ConnectionTransfer({ actions, onImported }) {
96
98
  children: note
97
99
  }) : null,
98
100
  problems.length > 0 ? /*#__PURE__*/ _jsx(Notice, {
99
- title: "This file cannot be imported as it stands",
101
+ title: t.cannotImportTitle,
100
102
  tone: "danger",
101
103
  children: /*#__PURE__*/ _jsx("ul", {
102
104
  className: "flex flex-col gap-1",
@@ -19,6 +19,7 @@ import { CardSkeleton } from './Skeletons';
19
19
  * button per field.
20
20
  */ const theme = integrationsPageTheme;
21
21
  export function CredentialFields({ actions, source, loading = false }) {
22
+ const t = useLabels().errors;
22
23
  const labels = useLabels();
23
24
  const store = useIntegrationsStore();
24
25
  const [values, setValues] = useState({});
@@ -92,7 +93,7 @@ export function CredentialFields({ actions, source, loading = false }) {
92
93
  }
93
94
  }
94
95
  if (filled === 0) {
95
- setStatus('Nothing to save — every box is empty, so the stored values are untouched.');
96
+ setStatus(t.nothingToSave);
96
97
  return;
97
98
  }
98
99
  setBusy(true);
@@ -104,10 +105,10 @@ export function CredentialFields({ actions, source, loading = false }) {
104
105
  // stored can only ever be looked at as the mask below.
105
106
  setValues({});
106
107
  applyHints(result);
107
- setStatus('Saved. Only the masked hints below come back.');
108
+ setStatus(t.credentialsSaved);
108
109
  }).catch((reason)=>{
109
110
  setBusy(false);
110
- store.setError(errorText(reason, 'The credentials could not be saved.'));
111
+ store.setError(errorText(reason, t.credentialsSaveFailed));
111
112
  });
112
113
  };
113
114
  const handleClear = (slot)=>{
@@ -128,10 +129,10 @@ export function CredentialFields({ actions, source, loading = false }) {
128
129
  remember({
129
130
  [slot]: hintFromResult(result, slot) ?? null
130
131
  });
131
- setStatus('Cleared. Nothing is stored for that field any more.');
132
+ setStatus(t.credentialCleared);
132
133
  }).catch((reason)=>{
133
134
  setBusy(false);
134
- store.setError(errorText(reason, 'The credential could not be cleared.'));
135
+ store.setError(errorText(reason, t.credentialClearFailed));
135
136
  });
136
137
  };
137
138
  return /*#__PURE__*/ _jsxs("section", {
@@ -16,6 +16,7 @@ import { Badge, Field } from './Primitives';
16
16
  * accident, roughly once.
17
17
  */ const theme = integrationsPageTheme;
18
18
  export function CredentialSlotRow({ slot, source, value, maskedHint, disabled, onChange, onClear }) {
19
+ const t = useLabels().errors;
19
20
  const labels = useLabels();
20
21
  const id = `credential-${slot}`;
21
22
  const label = slotLabel(slot, source);
@@ -34,7 +35,7 @@ export function CredentialSlotRow({ slot, source, value, maskedHint, disabled, o
34
35
  disabled: disabled,
35
36
  id: id,
36
37
  onChange: (event)=>onChange(event.target.value),
37
- placeholder: stored ? 'Leave empty to keep the stored value' : 'Nothing stored yet',
38
+ placeholder: stored ? t.keepStored : t.nothingStored,
38
39
  type: "password",
39
40
  value: value
40
41
  })
@@ -19,6 +19,7 @@ import { pathParamNames } from './pathParams';
19
19
  * a list page arriving in the background.
20
20
  */ const theme = integrationsPageTheme;
21
21
  export function EndpointForm({ sourceId, endpoint, actions, onSaved, onDeleted, onCancel }) {
22
+ const t = useLabels().errors;
22
23
  const labels = useLabels();
23
24
  const store = useIntegrationsStore();
24
25
  const [saving, setSaving] = useState(false);
@@ -106,8 +107,8 @@ export function EndpointForm({ sourceId, endpoint, actions, onSaved, onDeleted,
106
107
  }).then((saved)=>{
107
108
  setSaving(false);
108
109
  if (saved) onSaved(saved);
109
- else store.setError('The server did not return the saved endpoint.');
110
- }).catch(fail('Saving the endpoint failed.'));
110
+ else store.setError(t.endpointNotReturned);
111
+ }).catch(fail(t.endpointSaveFailed));
111
112
  };
112
113
  const remove = ()=>{
113
114
  if (!endpoint) return;
@@ -116,8 +117,8 @@ export function EndpointForm({ sourceId, endpoint, actions, onSaved, onDeleted,
116
117
  actions.deleteEndpoint(endpoint.id).then((ok)=>{
117
118
  setDeleting(false);
118
119
  if (ok) onDeleted(endpoint.id);
119
- else store.setError('The endpoint was not deleted.');
120
- }).catch(fail('Deleting the endpoint failed.'));
120
+ else store.setError(t.endpointNotDeleted);
121
+ }).catch(fail(t.endpointDeleteFailed));
121
122
  };
122
123
  return /*#__PURE__*/ _jsxs("form", {
123
124
  className: theme.card.wrapper,
@@ -17,6 +17,7 @@ import { settlementOf } from './settleOwnership';
17
17
  * their own block here rather than being folded into a generic "no data".
18
18
  */ const theme = integrationsPageTheme;
19
19
  export function EndpointSample({ endpoint, actions }) {
20
+ const t = useLabels().errors;
20
21
  const labels = useLabels();
21
22
  const store = useIntegrationsStore();
22
23
  const [values, setValues] = useState({});
@@ -97,7 +98,7 @@ export function EndpointSample({ endpoint, actions }) {
97
98
  }).catch((error)=>{
98
99
  if (settleElsewhere(mine, requestedFor)) return;
99
100
  store.setSample(null, requestedFor);
100
- store.setError(error instanceof Error ? error.message : 'Sampling the endpoint failed.');
101
+ store.setError(error instanceof Error ? error.message : t.sampleFailed);
101
102
  });
102
103
  };
103
104
  return /*#__PURE__*/ _jsxs("section", {