nucleus-core-ts 0.9.781 → 0.9.783
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/client.js +1 -1
- package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionAuthFields.js +85 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionBasicFields.js +165 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionForm.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionForm.js +112 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionList.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionList.js +184 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionOauthFields.js +131 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.d.ts +21 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionSettingsTab.js +49 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.d.ts +12 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTabs.js +95 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTest.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/ConnectionTest.js +116 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialFields.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialFields.js +171 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/CredentialSlotRow.js +63 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointForm.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointForm.js +166 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointFormFields.js +162 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointList.d.ts +12 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointList.js +205 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointSample.d.ts +6 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointSample.js +204 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/EndpointsTab.js +101 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.d.ts +17 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRow.js +179 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/FieldMappingRows.js +115 -0
- package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/components/IntegrationsPage.js +175 -0
- package/dist/fe/components/IntegrationsPage/components/MappingEditor.d.ts +15 -0
- package/dist/fe/components/IntegrationsPage/components/MappingEditor.js +193 -0
- package/dist/fe/components/IntegrationsPage/components/MappingList.d.ts +11 -0
- package/dist/fe/components/IntegrationsPage/components/MappingList.js +191 -0
- package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/MappingWriteModeFields.js +150 -0
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/MappingsTab.js +154 -0
- package/dist/fe/components/IntegrationsPage/components/PaginationFields.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/PaginationFields.js +218 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.d.ts +24 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRow.js +180 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.d.ts +33 -0
- package/dist/fe/components/IntegrationsPage/components/ParamBindingRows.js +100 -0
- package/dist/fe/components/IntegrationsPage/components/Primitives.d.ts +65 -0
- package/dist/fe/components/IntegrationsPage/components/Primitives.js +94 -0
- package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/RunConfirmStep.js +48 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistory.d.ts +10 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistory.js +124 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.d.ts +7 -0
- package/dist/fe/components/IntegrationsPage/components/RunHistoryRow.js +129 -0
- package/dist/fe/components/IntegrationsPage/components/RunPanel.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/RunPanel.js +160 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanIssues.js +43 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.d.ts +5 -0
- package/dist/fe/components/IntegrationsPage/components/RunPlanSummary.js +92 -0
- package/dist/fe/components/IntegrationsPage/components/RunProgressBar.d.ts +8 -0
- package/dist/fe/components/IntegrationsPage/components/RunProgressBar.js +77 -0
- package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/RunRevertConflicts.js +65 -0
- package/dist/fe/components/IntegrationsPage/components/RunsTab.d.ts +13 -0
- package/dist/fe/components/IntegrationsPage/components/RunsTab.js +39 -0
- package/dist/fe/components/IntegrationsPage/components/Skeletons.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/Skeletons.js +139 -0
- package/dist/fe/components/IntegrationsPage/components/authOptions.d.ts +29 -0
- package/dist/fe/components/IntegrationsPage/components/authOptions.js +71 -0
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.d.ts +64 -0
- package/dist/fe/components/IntegrationsPage/components/connectionDraft.js +112 -0
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.d.ts +25 -0
- package/dist/fe/components/IntegrationsPage/components/credentialSlots.js +67 -0
- package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.d.ts +16 -0
- package/dist/fe/components/IntegrationsPage/components/fieldMappingOptions.js +41 -0
- package/dist/fe/components/IntegrationsPage/components/formSupport.d.ts +19 -0
- package/dist/fe/components/IntegrationsPage/components/formSupport.js +25 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.d.ts +40 -0
- package/dist/fe/components/IntegrationsPage/components/mappingDraft.js +44 -0
- package/dist/fe/components/IntegrationsPage/components/pathParams.d.ts +9 -0
- package/dist/fe/components/IntegrationsPage/components/pathParams.js +15 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.d.ts +33 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.js +85 -0
- package/dist/fe/components/IntegrationsPage/components/useInfiniteList.d.ts +40 -0
- package/dist/fe/components/IntegrationsPage/components/useInfiniteList.js +110 -0
- package/dist/fe/components/IntegrationsPage/index.d.ts +18 -0
- package/dist/fe/components/IntegrationsPage/index.js +11 -0
- package/dist/fe/components/IntegrationsPage/store/index.d.ts +2 -0
- package/dist/fe/components/IntegrationsPage/store/index.js +124 -0
- package/dist/fe/components/IntegrationsPage/store/state.d.ts +80 -0
- package/dist/fe/components/IntegrationsPage/store/state.js +32 -0
- package/dist/fe/components/IntegrationsPage/theme/index.d.ts +103 -0
- package/dist/fe/components/IntegrationsPage/theme/index.js +113 -0
- package/dist/fe/components/IntegrationsPage/types/actions.d.ts +91 -0
- package/dist/fe/components/IntegrationsPage/types/actions.js +1 -0
- package/dist/fe/components/IntegrationsPage/types/index.d.ts +3 -0
- package/dist/fe/components/IntegrationsPage/types/index.js +3 -0
- package/dist/fe/components/IntegrationsPage/types/records.d.ts +139 -0
- package/dist/fe/components/IntegrationsPage/types/records.js +11 -0
- package/dist/fe/components/IntegrationsPage/types/results.d.ts +52 -0
- package/dist/fe/components/IntegrationsPage/types/results.js +4 -0
- package/dist/fe/index.d.ts +2 -0
- package/dist/fe/index.js +1 -0
- package/dist/index.js +14 -14
- package/dist/src/Client/ApiCaller/system-tables.d.ts +505 -0
- package/dist/src/Client/Proxy/wsProxy.js +25 -1
- package/dist/src/ElysiaPlugin/routes/backup/types.d.ts +3 -1
- package/dist/src/ElysiaPlugin/routes/integrations/index.d.ts +49 -0
- package/dist/src/ElysiaPlugin/routes/integrations/inspect.d.ts +87 -0
- package/dist/src/ElysiaPlugin/routes/integrations/inspectRoutes.d.ts +41 -0
- package/dist/src/ElysiaPlugin/routes/integrations/repository.d.ts +88 -0
- package/dist/src/ElysiaPlugin/routes/integrations/types.d.ts +22 -0
- package/dist/src/ElysiaPlugin/routes/pubsub/frame.d.ts +37 -0
- package/dist/src/ElysiaPlugin/routes/pubsub/frame.test.d.ts +1 -0
- package/dist/src/Services/Backup/BackupService.d.ts +16 -8
- package/dist/src/Services/Backup/types.d.ts +25 -0
- package/dist/src/Services/Integrations/bindings.d.ts +116 -0
- package/dist/src/Services/Integrations/bindings.test.d.ts +1 -0
- package/dist/src/Services/Integrations/collect.d.ts +68 -0
- package/dist/src/Services/Integrations/collect.test.d.ts +1 -0
- package/dist/src/Services/Integrations/fetcher.d.ts +100 -0
- package/dist/src/Services/Integrations/fetcher.test.d.ts +1 -0
- package/dist/src/Services/Integrations/lock.d.ts +77 -0
- package/dist/src/Services/Integrations/lock.test.d.ts +1 -0
- package/dist/src/Services/Integrations/mapping.d.ts +63 -0
- package/dist/src/Services/Integrations/mapping.test.d.ts +1 -0
- package/dist/src/Services/Integrations/oauth.d.ts +89 -0
- package/dist/src/Services/Integrations/oauth.test.d.ts +1 -0
- package/dist/src/Services/Integrations/pagination.d.ts +97 -0
- package/dist/src/Services/Integrations/pagination.test.d.ts +1 -0
- package/dist/src/Services/Integrations/plan.d.ts +74 -0
- package/dist/src/Services/Integrations/plan.test.d.ts +1 -0
- package/dist/src/Services/Integrations/revert.d.ts +78 -0
- package/dist/src/Services/Integrations/revert.test.d.ts +1 -0
- package/dist/src/Services/Integrations/runner.d.ts +126 -0
- package/dist/src/Services/Integrations/runner.test.d.ts +1 -0
- package/dist/src/Services/Integrations/types.d.ts +208 -0
- package/dist/src/Services/Integrations/writer.d.ts +105 -0
- package/dist/src/Services/Integrations/writer.test.d.ts +1 -0
- package/dist/src/Utils/cron.d.ts +65 -0
- package/dist/src/Utils/cron.test.d.ts +1 -0
- package/dist/src/types.d.ts +67 -1
- package/package.json +1 -1
- package/schemas/config.nucleus.json +43 -0
- package/schemas/table.schema.json +2 -1
- package/scripts/generate-schema.test.ts +78 -0
- package/scripts/generate-schema.ts +43 -5
- package/src/system.tables.json +362 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { IntegrationMapping } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* The mapping while it is being edited.
|
|
4
|
+
*
|
|
5
|
+
* It is deliberately NOT `IntegrationMapping`: a mapping being written has no id
|
|
6
|
+
* yet, and the fields the server owns — when it last ran, what it wrote — are not
|
|
7
|
+
* the operator's to edit and must not be posted back as if they were.
|
|
8
|
+
*/
|
|
9
|
+
/** One entity an import is allowed to write into, as the app reported it. */
|
|
10
|
+
export type MappingTarget = {
|
|
11
|
+
entity: string;
|
|
12
|
+
label: string;
|
|
13
|
+
fields: string[];
|
|
14
|
+
};
|
|
15
|
+
export type MappingDraft = {
|
|
16
|
+
/** Absent until the mapping has been saved once. */
|
|
17
|
+
id?: string;
|
|
18
|
+
endpointId: string;
|
|
19
|
+
name: string;
|
|
20
|
+
targetEntity: string;
|
|
21
|
+
fieldMappings: IntegrationMapping['fieldMappings'];
|
|
22
|
+
dedupSourceField?: string | null;
|
|
23
|
+
dedupTargetField?: string | null;
|
|
24
|
+
writeMode: IntegrationMapping['writeMode'];
|
|
25
|
+
missingRecordPolicy: IntegrationMapping['missingRecordPolicy'];
|
|
26
|
+
missingRecordColumn?: string | null;
|
|
27
|
+
missingRecordValue?: string | null;
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
};
|
|
30
|
+
/** Upsert and ignore: the two choices that cannot lose a row by themselves. */
|
|
31
|
+
export declare function blankMappingDraft(endpointId: string): MappingDraft;
|
|
32
|
+
/** Copies field by field so a column the server added cannot ride along unseen. */
|
|
33
|
+
export declare function toMappingDraft(mapping: IntegrationMapping): MappingDraft;
|
|
34
|
+
/**
|
|
35
|
+
* Everything standing between this draft and a run that does what it says.
|
|
36
|
+
*
|
|
37
|
+
* Reported as a list rather than a boolean because "Save is greyed out" with no
|
|
38
|
+
* reason is the single most common way a form wastes somebody's afternoon.
|
|
39
|
+
*/
|
|
40
|
+
export declare function mappingDraftIssues(draft: MappingDraft): string[];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { missingRecordFieldsIncomplete } from './MappingWriteModeFields';
|
|
2
|
+
/** Upsert and ignore: the two choices that cannot lose a row by themselves. */ export function blankMappingDraft(endpointId) {
|
|
3
|
+
return {
|
|
4
|
+
endpointId,
|
|
5
|
+
name: '',
|
|
6
|
+
targetEntity: '',
|
|
7
|
+
fieldMappings: [],
|
|
8
|
+
writeMode: 'upsert',
|
|
9
|
+
missingRecordPolicy: 'ignore'
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Copies field by field so a column the server added cannot ride along unseen. */ export function toMappingDraft(mapping) {
|
|
13
|
+
return {
|
|
14
|
+
id: mapping.id,
|
|
15
|
+
endpointId: mapping.endpointId,
|
|
16
|
+
name: mapping.name,
|
|
17
|
+
targetEntity: mapping.targetEntity,
|
|
18
|
+
fieldMappings: mapping.fieldMappings,
|
|
19
|
+
dedupSourceField: mapping.dedupSourceField,
|
|
20
|
+
dedupTargetField: mapping.dedupTargetField,
|
|
21
|
+
writeMode: mapping.writeMode,
|
|
22
|
+
missingRecordPolicy: mapping.missingRecordPolicy,
|
|
23
|
+
missingRecordColumn: mapping.missingRecordColumn,
|
|
24
|
+
missingRecordValue: mapping.missingRecordValue,
|
|
25
|
+
enabled: mapping.enabled
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Everything standing between this draft and a run that does what it says.
|
|
30
|
+
*
|
|
31
|
+
* Reported as a list rather than a boolean because "Save is greyed out" with no
|
|
32
|
+
* reason is the single most common way a form wastes somebody's afternoon.
|
|
33
|
+
*/ export function mappingDraftIssues(draft) {
|
|
34
|
+
const issues = [];
|
|
35
|
+
if (draft.name.trim() === '') issues.push('Give the mapping a name.');
|
|
36
|
+
if (draft.targetEntity.trim() === '') issues.push('Choose a target entity.');
|
|
37
|
+
if (draft.endpointId.trim() === '') issues.push('Select the endpoint this mapping reads from.');
|
|
38
|
+
const usable = draft.fieldMappings.some((row)=>row.target.trim() !== '' && (row.source.trim() !== '' || (row.constant ?? '').trim() !== ''));
|
|
39
|
+
if (!usable) issues.push('Map at least one field to a target column.');
|
|
40
|
+
if (missingRecordFieldsIncomplete(draft.missingRecordPolicy, draft.missingRecordColumn ?? '', draft.missingRecordValue ?? '')) {
|
|
41
|
+
issues.push('Deactivate needs both a column and the value to write into it.');
|
|
42
|
+
}
|
|
43
|
+
return issues;
|
|
44
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the parameter names out of an endpoint path.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the sample screen, which asks for a value by hand, and the binding
|
|
5
|
+
* editor, which asks where the value should come from on a real run. One reader
|
|
6
|
+
* so the two screens can never disagree about what a path needs.
|
|
7
|
+
*/
|
|
8
|
+
/** `{brace}` and `:colon` are both accepted, so both are read back. */
|
|
9
|
+
export declare function pathParamNames(path: string): string[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the parameter names out of an endpoint path.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the sample screen, which asks for a value by hand, and the binding
|
|
5
|
+
* editor, which asks where the value should come from on a real run. One reader
|
|
6
|
+
* so the two screens can never disagree about what a path needs.
|
|
7
|
+
*/ const PARAM_PATTERN = /\{([^{}]+)\}|:([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
8
|
+
/** `{brace}` and `:colon` are both accepted, so both are read back. */ export function pathParamNames(path) {
|
|
9
|
+
const names = [];
|
|
10
|
+
for (const match of path.matchAll(PARAM_PATTERN)){
|
|
11
|
+
const name = match[1] ?? match[2];
|
|
12
|
+
if (name && !names.includes(name)) names.push(name);
|
|
13
|
+
}
|
|
14
|
+
return names;
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The small conversions the run screens share.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here touches the server or React — it turns a record into the words an
|
|
5
|
+
* operator reads, and it does so the SAME way in every place, so a duration on a
|
|
6
|
+
* history row cannot disagree with the one on the panel above it.
|
|
7
|
+
*/
|
|
8
|
+
import type { IntegrationMapping, IntegrationRun, RunPlanValue, RunStatusValue } from '../types';
|
|
9
|
+
import type { BadgeTone } from './Primitives';
|
|
10
|
+
/** A run's outcome, coloured. Reverted is deliberately calm: nothing is wrong with it. */
|
|
11
|
+
export declare const RUN_STATUS_TONE: Record<RunStatusValue, BadgeTone>;
|
|
12
|
+
export declare function errorText(error: unknown, fallback: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* A moment, in UTC.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately not locale-formatted: `toLocaleString` reads the server's zone on
|
|
17
|
+
* the server and the browser's on the client, so the two renders differ and the
|
|
18
|
+
* row tears on hydration. UTC is the same number everywhere, and it is labelled.
|
|
19
|
+
*/
|
|
20
|
+
export declare function formatMoment(value?: string | null): string;
|
|
21
|
+
export declare function formatDuration(startedAt?: string | null, finishedAt?: string | null): string;
|
|
22
|
+
/** Milliseconds of an instant, or null when the field is absent or unparseable. */
|
|
23
|
+
export declare function toEpochMs(value?: string | null): number | null;
|
|
24
|
+
/** The counts of a finished run, in one line. Zeros of the rare kinds stay hidden. */
|
|
25
|
+
export declare function countersLine(run: IntegrationRun): string;
|
|
26
|
+
/**
|
|
27
|
+
* What a destructive run takes away, counted wherever there is a plan to count from.
|
|
28
|
+
*
|
|
29
|
+
* The mapping alone can say WHAT will happen; only the plan can say to how many
|
|
30
|
+
* rows. Both wordings exist because an operator is allowed to skip the preview,
|
|
31
|
+
* and a confirmation that says "some rows" is not a confirmation.
|
|
32
|
+
*/
|
|
33
|
+
export declare function describeLosses(mapping: IntegrationMapping, plan: RunPlanValue | null): string[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The small conversions the run screens share.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here touches the server or React — it turns a record into the words an
|
|
5
|
+
* operator reads, and it does so the SAME way in every place, so a duration on a
|
|
6
|
+
* history row cannot disagree with the one on the panel above it.
|
|
7
|
+
*/ /** A run's outcome, coloured. Reverted is deliberately calm: nothing is wrong with it. */ export const RUN_STATUS_TONE = {
|
|
8
|
+
queued: 'neutral',
|
|
9
|
+
running: 'info',
|
|
10
|
+
succeeded: 'success',
|
|
11
|
+
failed: 'danger',
|
|
12
|
+
cancelled: 'warning',
|
|
13
|
+
reverted: 'neutral'
|
|
14
|
+
};
|
|
15
|
+
export function errorText(error, fallback) {
|
|
16
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
17
|
+
}
|
|
18
|
+
function pad(value) {
|
|
19
|
+
return String(value).padStart(2, '0');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A moment, in UTC.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately not locale-formatted: `toLocaleString` reads the server's zone on
|
|
25
|
+
* the server and the browser's on the client, so the two renders differ and the
|
|
26
|
+
* row tears on hydration. UTC is the same number everywhere, and it is labelled.
|
|
27
|
+
*/ export function formatMoment(value) {
|
|
28
|
+
if (!value) return '—';
|
|
29
|
+
const date = new Date(value);
|
|
30
|
+
if (Number.isNaN(date.getTime())) return '—';
|
|
31
|
+
const day = `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`;
|
|
32
|
+
return `${day} ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())} UTC`;
|
|
33
|
+
}
|
|
34
|
+
export function formatDuration(startedAt, finishedAt) {
|
|
35
|
+
if (!startedAt || !finishedAt) return '—';
|
|
36
|
+
const start = new Date(startedAt).getTime();
|
|
37
|
+
const end = new Date(finishedAt).getTime();
|
|
38
|
+
if (Number.isNaN(start) || Number.isNaN(end) || end < start) return '—';
|
|
39
|
+
const seconds = Math.round((end - start) / 1000);
|
|
40
|
+
if (seconds < 60) return `${seconds}s`;
|
|
41
|
+
const minutes = Math.floor(seconds / 60);
|
|
42
|
+
if (minutes < 60) return `${minutes}m ${seconds % 60}s`;
|
|
43
|
+
return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
44
|
+
}
|
|
45
|
+
/** Milliseconds of an instant, or null when the field is absent or unparseable. */ export function toEpochMs(value) {
|
|
46
|
+
if (!value) return null;
|
|
47
|
+
const ms = new Date(value).getTime();
|
|
48
|
+
return Number.isNaN(ms) ? null : ms;
|
|
49
|
+
}
|
|
50
|
+
/** The counts of a finished run, in one line. Zeros of the rare kinds stay hidden. */ export function countersLine(run) {
|
|
51
|
+
const parts = [
|
|
52
|
+
`${run.createdCount} created`,
|
|
53
|
+
`${run.updatedCount} updated`,
|
|
54
|
+
`${run.skippedCount} skipped`
|
|
55
|
+
];
|
|
56
|
+
if (run.deletedCount > 0) parts.push(`${run.deletedCount} deleted`);
|
|
57
|
+
if (run.deactivatedCount > 0) parts.push(`${run.deactivatedCount} deactivated`);
|
|
58
|
+
if (run.errorCount > 0) parts.push(`${run.errorCount} failed`);
|
|
59
|
+
return parts.join(' · ');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* What a destructive run takes away, counted wherever there is a plan to count from.
|
|
63
|
+
*
|
|
64
|
+
* The mapping alone can say WHAT will happen; only the plan can say to how many
|
|
65
|
+
* rows. Both wordings exist because an operator is allowed to skip the preview,
|
|
66
|
+
* and a confirmation that says "some rows" is not a confirmation.
|
|
67
|
+
*/ export function describeLosses(mapping, plan) {
|
|
68
|
+
const lines = [];
|
|
69
|
+
const target = mapping.targetEntity;
|
|
70
|
+
if (mapping.writeMode === 'replace') {
|
|
71
|
+
lines.push(plan ? `Write mode is "replace": the ${plan.existingCount} row(s) already in ${target} are removed before this import writes.` : `Write mode is "replace": every row already in ${target} is removed before this import writes.`);
|
|
72
|
+
}
|
|
73
|
+
if (mapping.missingRecordPolicy === 'delete') {
|
|
74
|
+
lines.push(plan ? `${plan.missing} row(s) in ${target} are no longer returned by the source and will be DELETED.` : `Rows in ${target} the source no longer returns will be DELETED.`);
|
|
75
|
+
}
|
|
76
|
+
if (mapping.missingRecordPolicy === 'deactivate') {
|
|
77
|
+
const column = mapping.missingRecordColumn ?? 'the configured column';
|
|
78
|
+
const value = mapping.missingRecordValue ?? 'the configured value';
|
|
79
|
+
lines.push(plan ? `${plan.missing} row(s) missing from the source will have ${column} set to ${value}.` : `Rows missing from the source will have ${column} set to ${value}.`);
|
|
80
|
+
}
|
|
81
|
+
if (lines.length === 0) {
|
|
82
|
+
lines.push(`This run writes into ${target}. Rows it overwrites can be restored only while this run's pre-images are kept.`);
|
|
83
|
+
}
|
|
84
|
+
return lines;
|
|
85
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A list the SERVER pages.
|
|
4
|
+
*
|
|
5
|
+
* The hook never filters, sorts or searches what it holds — it asks for page 1,
|
|
6
|
+
* then page 2, and keeps exactly what came back. Anything the operator types
|
|
7
|
+
* belongs in `deps`, and a change there throws the old rows away before the new
|
|
8
|
+
* ones arrive, because a stale list under a new search term is not "still
|
|
9
|
+
* loading", it is wrong.
|
|
10
|
+
*/
|
|
11
|
+
export type InfiniteListPage<T> = {
|
|
12
|
+
items: T[];
|
|
13
|
+
total: number;
|
|
14
|
+
};
|
|
15
|
+
export type UseInfiniteListOptions<T> = {
|
|
16
|
+
/** Fetches one page. Page numbers start at 1. */
|
|
17
|
+
load: (page: number) => Promise<InfiniteListPage<T>>;
|
|
18
|
+
/** Search terms, filters, the selected parent id — anything that redefines the list. */
|
|
19
|
+
deps: unknown[];
|
|
20
|
+
pageSize?: number;
|
|
21
|
+
};
|
|
22
|
+
export type UseInfiniteListResult<T> = {
|
|
23
|
+
items: T[];
|
|
24
|
+
total: number;
|
|
25
|
+
loading: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* What went wrong, when it did.
|
|
28
|
+
*
|
|
29
|
+
* Without this a failed request left an empty list, and every caller rendered
|
|
30
|
+
* its "nothing here yet" state — telling an operator the connection has no
|
|
31
|
+
* endpoints when the truth was that the request was refused. That exact
|
|
32
|
+
* confusion is one of the defects this panel was written to remove, so the
|
|
33
|
+
* hook reports the failure and the caller must show it.
|
|
34
|
+
*/
|
|
35
|
+
error: string | null;
|
|
36
|
+
/** Put this on an element after the last row; seeing it asks for the next page. */
|
|
37
|
+
sentinelRef: RefObject<HTMLDivElement | null>;
|
|
38
|
+
reload: () => void;
|
|
39
|
+
};
|
|
40
|
+
export declare function useInfiniteList<T>(options: UseInfiniteListOptions<T>): UseInfiniteListResult<T>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useEffectEvent, useRef, useState } from 'react';
|
|
3
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
4
|
+
export function useInfiniteList(options) {
|
|
5
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
6
|
+
/**
|
|
7
|
+
* The caller's deps as ONE value.
|
|
8
|
+
*
|
|
9
|
+
* Spreading them into the dependency array made its LENGTH depend on the
|
|
10
|
+
* caller, and React requires that length to be constant across renders — an
|
|
11
|
+
* array that grows or shrinks silently stops the effect re-running when it
|
|
12
|
+
* should. Collapsing to a string keeps one slot no matter how many things the
|
|
13
|
+
* caller watches.
|
|
14
|
+
*/ const depsKey = JSON.stringify(options.deps);
|
|
15
|
+
const [items, setItems] = useState([]);
|
|
16
|
+
const [total, setTotal] = useState(0);
|
|
17
|
+
const [loading, setLoading] = useState(true);
|
|
18
|
+
const [error, setError] = useState(null);
|
|
19
|
+
const [reloadToken, setReloadToken] = useState(0);
|
|
20
|
+
const sentinelRef = useRef(null);
|
|
21
|
+
const nextPageRef = useRef(1);
|
|
22
|
+
const loadedCountRef = useRef(0);
|
|
23
|
+
const inFlightRef = useRef(false);
|
|
24
|
+
const exhaustedRef = useRef(false);
|
|
25
|
+
/** Bumped on every reset; a reply from an older generation is dropped. */ const generationRef = useRef(0);
|
|
26
|
+
const fetchPage = useEffectEvent((page)=>{
|
|
27
|
+
if (inFlightRef.current || exhaustedRef.current) return;
|
|
28
|
+
const generation = generationRef.current;
|
|
29
|
+
inFlightRef.current = true;
|
|
30
|
+
setLoading(true);
|
|
31
|
+
options.load(page).then((result)=>{
|
|
32
|
+
if (generation !== generationRef.current) return;
|
|
33
|
+
inFlightRef.current = false;
|
|
34
|
+
setLoading(false);
|
|
35
|
+
setTotal(result.total);
|
|
36
|
+
setItems((previous)=>page === 1 ? result.items : [
|
|
37
|
+
...previous,
|
|
38
|
+
...result.items
|
|
39
|
+
]);
|
|
40
|
+
const loaded = (page === 1 ? 0 : loadedCountRef.current) + result.items.length;
|
|
41
|
+
loadedCountRef.current = loaded;
|
|
42
|
+
nextPageRef.current = page + 1;
|
|
43
|
+
// A short page means the end. The count check is the belt to that
|
|
44
|
+
// braces: a server that always fills a page still stops at its total.
|
|
45
|
+
exhaustedRef.current = result.items.length < pageSize || result.total > 0 && loaded >= result.total;
|
|
46
|
+
}).catch((reason)=>{
|
|
47
|
+
if (generation !== generationRef.current) return;
|
|
48
|
+
inFlightRef.current = false;
|
|
49
|
+
setLoading(false);
|
|
50
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
51
|
+
// Stop asking. The sentinel stays on screen, so retrying on failure
|
|
52
|
+
// would hammer a broken endpoint for as long as the panel is open;
|
|
53
|
+
// `reload()` is how the operator tries again.
|
|
54
|
+
exhaustedRef.current = true;
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
const requestNextPage = useEffectEvent(()=>{
|
|
58
|
+
fetchPage(nextPageRef.current);
|
|
59
|
+
});
|
|
60
|
+
// Reset. The cleanup retires the generation, so a page still in flight when
|
|
61
|
+
// the search term changes — or when the panel unmounts — writes nothing.
|
|
62
|
+
useEffect(()=>{
|
|
63
|
+
inFlightRef.current = false;
|
|
64
|
+
exhaustedRef.current = false;
|
|
65
|
+
nextPageRef.current = 1;
|
|
66
|
+
loadedCountRef.current = 0;
|
|
67
|
+
setItems([]);
|
|
68
|
+
setTotal(0);
|
|
69
|
+
setLoading(true);
|
|
70
|
+
setError(null);
|
|
71
|
+
fetchPage(1);
|
|
72
|
+
return ()=>{
|
|
73
|
+
generationRef.current += 1;
|
|
74
|
+
};
|
|
75
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: the caller deps are
|
|
76
|
+
// collapsed into depsKey on purpose; see the comment on it.
|
|
77
|
+
}, [
|
|
78
|
+
depsKey,
|
|
79
|
+
reloadToken
|
|
80
|
+
]);
|
|
81
|
+
// Rebinding as the list grows is deliberate: when a page is shorter than the
|
|
82
|
+
// viewport the sentinel never LEAVES the screen, so the observer would have
|
|
83
|
+
// no crossing left to report and the list would stall one page in.
|
|
84
|
+
useEffect(()=>{
|
|
85
|
+
const node = sentinelRef.current;
|
|
86
|
+
if (!node) return;
|
|
87
|
+
const observer = new IntersectionObserver((entries)=>{
|
|
88
|
+
if (entries[0]?.isIntersecting) requestNextPage();
|
|
89
|
+
}, {
|
|
90
|
+
rootMargin: '96px'
|
|
91
|
+
});
|
|
92
|
+
observer.observe(node);
|
|
93
|
+
return ()=>{
|
|
94
|
+
observer.disconnect();
|
|
95
|
+
};
|
|
96
|
+
}, [
|
|
97
|
+
items.length
|
|
98
|
+
]);
|
|
99
|
+
const reload = ()=>{
|
|
100
|
+
setReloadToken((token)=>token + 1);
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
items,
|
|
104
|
+
total,
|
|
105
|
+
loading,
|
|
106
|
+
error,
|
|
107
|
+
sentinelRef,
|
|
108
|
+
reload
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { ConnectionList } from './components/ConnectionList';
|
|
2
|
+
export type { ConnectionTabsProps } from './components/ConnectionTabs';
|
|
3
|
+
export { ConnectionTabs, connectionTabId, connectionTabPanelId } from './components/ConnectionTabs';
|
|
4
|
+
export { EndpointsTab } from './components/EndpointsTab';
|
|
5
|
+
export { IntegrationsPage } from './components/IntegrationsPage';
|
|
6
|
+
export { MappingsTab } from './components/MappingsTab';
|
|
7
|
+
export type { BadgeProps, BadgeTone, CodeBlockProps, FieldProps, NoticeProps, NoticeTone, StatProps, } from './components/Primitives';
|
|
8
|
+
export { Badge, CodeBlock, Field, Notice, Stat } from './components/Primitives';
|
|
9
|
+
export { RunsTab } from './components/RunsTab';
|
|
10
|
+
export type { ListSkeletonProps } from './components/Skeletons';
|
|
11
|
+
export { CardSkeleton, ListSkeleton, StatGridSkeleton } from './components/Skeletons';
|
|
12
|
+
export type { InfiniteListPage, UseInfiniteListOptions, UseInfiniteListResult, } from './components/useInfiniteList';
|
|
13
|
+
export { useInfiniteList } from './components/useInfiniteList';
|
|
14
|
+
export { useIntegrationsStore } from './store';
|
|
15
|
+
export type { ConnectionTab } from './store/state';
|
|
16
|
+
export type { IntegrationsPageTheme } from './theme';
|
|
17
|
+
export { extendIntegrationsTheme, integrationsPageTheme } from './theme';
|
|
18
|
+
export type * from './types';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { ConnectionList } from './components/ConnectionList';
|
|
2
|
+
export { ConnectionTabs, connectionTabId, connectionTabPanelId } from './components/ConnectionTabs';
|
|
3
|
+
export { EndpointsTab } from './components/EndpointsTab';
|
|
4
|
+
export { IntegrationsPage } from './components/IntegrationsPage';
|
|
5
|
+
export { MappingsTab } from './components/MappingsTab';
|
|
6
|
+
export { Badge, CodeBlock, Field, Notice, Stat } from './components/Primitives';
|
|
7
|
+
export { RunsTab } from './components/RunsTab';
|
|
8
|
+
export { CardSkeleton, ListSkeleton, StatGridSkeleton } from './components/Skeletons';
|
|
9
|
+
export { useInfiniteList } from './components/useInfiniteList';
|
|
10
|
+
export { useIntegrationsStore } from './store';
|
|
11
|
+
export { extendIntegrationsTheme, integrationsPageTheme } from './theme';
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { batch, createStore } from 'h-state';
|
|
3
|
+
import { initial } from './state';
|
|
4
|
+
export const { useStore: useIntegrationsStore } = createStore(initial, {
|
|
5
|
+
setSources: (store)=>(items, total)=>batch(()=>{
|
|
6
|
+
store.sources = items;
|
|
7
|
+
store.sourcesTotal = total;
|
|
8
|
+
store.sourcesLoading = false;
|
|
9
|
+
}),
|
|
10
|
+
setSourcesPage: (store)=>(page)=>{
|
|
11
|
+
store.sourcesPage = page;
|
|
12
|
+
},
|
|
13
|
+
setSourcesSearch: (store)=>(search)=>batch(()=>{
|
|
14
|
+
store.sourcesSearch = search;
|
|
15
|
+
// A new search is a new result set; staying on page 4 would show an empty
|
|
16
|
+
// list and read as "no matches".
|
|
17
|
+
store.sourcesPage = 1;
|
|
18
|
+
}),
|
|
19
|
+
setSourcesLoading: (store)=>(loading)=>{
|
|
20
|
+
store.sourcesLoading = loading;
|
|
21
|
+
},
|
|
22
|
+
selectSource: (store)=>(id)=>batch(()=>{
|
|
23
|
+
store.selectedSourceId = id;
|
|
24
|
+
// Everything below a connection belongs to THAT connection. Leaving the
|
|
25
|
+
// previous one's endpoints on screen under a new name is worse than empty.
|
|
26
|
+
store.tab = 'connection';
|
|
27
|
+
store.endpoints = [];
|
|
28
|
+
store.endpointsTotal = 0;
|
|
29
|
+
store.endpointsPage = 1;
|
|
30
|
+
store.endpointsSearch = '';
|
|
31
|
+
store.selectedEndpointId = null;
|
|
32
|
+
store.mappings = [];
|
|
33
|
+
store.mappingsTotal = 0;
|
|
34
|
+
store.mappingsPage = 1;
|
|
35
|
+
store.mappingsSearch = '';
|
|
36
|
+
store.selectedMappingId = null;
|
|
37
|
+
store.runs = [];
|
|
38
|
+
store.runsTotal = 0;
|
|
39
|
+
store.runsPage = 1;
|
|
40
|
+
store.sample = null;
|
|
41
|
+
store.plan = null;
|
|
42
|
+
store.error = null;
|
|
43
|
+
}),
|
|
44
|
+
setTab: (store)=>(tab)=>{
|
|
45
|
+
store.tab = tab;
|
|
46
|
+
},
|
|
47
|
+
setEndpoints: (store)=>(items, total)=>batch(()=>{
|
|
48
|
+
store.endpoints = items;
|
|
49
|
+
store.endpointsTotal = total;
|
|
50
|
+
store.endpointsLoading = false;
|
|
51
|
+
}),
|
|
52
|
+
setEndpointsPage: (store)=>(page)=>{
|
|
53
|
+
store.endpointsPage = page;
|
|
54
|
+
},
|
|
55
|
+
setEndpointsSearch: (store)=>(search)=>batch(()=>{
|
|
56
|
+
store.endpointsSearch = search;
|
|
57
|
+
store.endpointsPage = 1;
|
|
58
|
+
}),
|
|
59
|
+
setEndpointsLoading: (store)=>(loading)=>{
|
|
60
|
+
store.endpointsLoading = loading;
|
|
61
|
+
},
|
|
62
|
+
selectEndpoint: (store)=>(id)=>batch(()=>{
|
|
63
|
+
store.selectedEndpointId = id;
|
|
64
|
+
// The sample describes ONE endpoint; carrying it over would offer the
|
|
65
|
+
// wrong field list to the next mapping.
|
|
66
|
+
store.sample = null;
|
|
67
|
+
}),
|
|
68
|
+
setMappings: (store)=>(items, total)=>batch(()=>{
|
|
69
|
+
store.mappings = items;
|
|
70
|
+
store.mappingsTotal = total;
|
|
71
|
+
store.mappingsLoading = false;
|
|
72
|
+
}),
|
|
73
|
+
setMappingsPage: (store)=>(page)=>{
|
|
74
|
+
store.mappingsPage = page;
|
|
75
|
+
},
|
|
76
|
+
setMappingsSearch: (store)=>(search)=>batch(()=>{
|
|
77
|
+
store.mappingsSearch = search;
|
|
78
|
+
store.mappingsPage = 1;
|
|
79
|
+
}),
|
|
80
|
+
setMappingsLoading: (store)=>(loading)=>{
|
|
81
|
+
store.mappingsLoading = loading;
|
|
82
|
+
},
|
|
83
|
+
selectMapping: (store)=>(id)=>batch(()=>{
|
|
84
|
+
store.selectedMappingId = id;
|
|
85
|
+
// A plan belongs to the mapping it was computed for. Showing it against a
|
|
86
|
+
// different one would have an operator approve counts from another import.
|
|
87
|
+
store.plan = null;
|
|
88
|
+
}),
|
|
89
|
+
setRuns: (store)=>(items, total)=>batch(()=>{
|
|
90
|
+
store.runs = items;
|
|
91
|
+
store.runsTotal = total;
|
|
92
|
+
store.runsLoading = false;
|
|
93
|
+
}),
|
|
94
|
+
setRunsPage: (store)=>(page)=>{
|
|
95
|
+
store.runsPage = page;
|
|
96
|
+
},
|
|
97
|
+
setRunsLoading: (store)=>(loading)=>{
|
|
98
|
+
store.runsLoading = loading;
|
|
99
|
+
},
|
|
100
|
+
setSample: (store)=>(sample)=>batch(()=>{
|
|
101
|
+
store.sample = sample;
|
|
102
|
+
store.sampling = false;
|
|
103
|
+
}),
|
|
104
|
+
setSampling: (store)=>(sampling)=>{
|
|
105
|
+
store.sampling = sampling;
|
|
106
|
+
},
|
|
107
|
+
setPlan: (store)=>(plan)=>batch(()=>{
|
|
108
|
+
store.plan = plan;
|
|
109
|
+
store.planning = false;
|
|
110
|
+
}),
|
|
111
|
+
setPlanning: (store)=>(planning)=>{
|
|
112
|
+
store.planning = planning;
|
|
113
|
+
},
|
|
114
|
+
setProgress: (store)=>(progress)=>{
|
|
115
|
+
store.progress = progress;
|
|
116
|
+
},
|
|
117
|
+
setActiveRun: (store)=>(runId)=>batch(()=>{
|
|
118
|
+
store.activeRunId = runId;
|
|
119
|
+
store.progress = null;
|
|
120
|
+
}),
|
|
121
|
+
setError: (store)=>(error)=>{
|
|
122
|
+
store.error = error;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { EndpointSampleValue, IntegrationEndpoint, IntegrationMapping, IntegrationRun, IntegrationSource, RunPlanValue, RunProgressValue } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Panel state.
|
|
4
|
+
*
|
|
5
|
+
* Lists live here as the CURRENT PAGE only, never the whole table — the server
|
|
6
|
+
* pages, filters, searches and sorts, so holding more would mean the client had
|
|
7
|
+
* fetched more than it shows, which is the thing this design refuses to do.
|
|
8
|
+
*
|
|
9
|
+
* The selection is the spine: choosing a connection is what gives the endpoint,
|
|
10
|
+
* mapping and run lists something to be about. Clearing it clears them, so no
|
|
11
|
+
* screen can show one connection's endpoints under another's name.
|
|
12
|
+
*/
|
|
13
|
+
export type ConnectionTab = 'connection' | 'endpoints' | 'mappings' | 'runs';
|
|
14
|
+
export type State = {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
sources: IntegrationSource[];
|
|
17
|
+
sourcesTotal: number;
|
|
18
|
+
sourcesPage: number;
|
|
19
|
+
sourcesSearch: string;
|
|
20
|
+
sourcesLoading: boolean;
|
|
21
|
+
selectedSourceId: string | null;
|
|
22
|
+
tab: ConnectionTab;
|
|
23
|
+
endpoints: IntegrationEndpoint[];
|
|
24
|
+
endpointsTotal: number;
|
|
25
|
+
endpointsPage: number;
|
|
26
|
+
endpointsSearch: string;
|
|
27
|
+
endpointsLoading: boolean;
|
|
28
|
+
selectedEndpointId: string | null;
|
|
29
|
+
mappings: IntegrationMapping[];
|
|
30
|
+
mappingsTotal: number;
|
|
31
|
+
mappingsPage: number;
|
|
32
|
+
mappingsSearch: string;
|
|
33
|
+
mappingsLoading: boolean;
|
|
34
|
+
selectedMappingId: string | null;
|
|
35
|
+
runs: IntegrationRun[];
|
|
36
|
+
runsTotal: number;
|
|
37
|
+
runsPage: number;
|
|
38
|
+
runsLoading: boolean;
|
|
39
|
+
/** The real shape of the selected endpoint's response, once sampled. */
|
|
40
|
+
sample: EndpointSampleValue | null;
|
|
41
|
+
sampling: boolean;
|
|
42
|
+
/** What a run would do. Cleared whenever the mapping changes underneath it. */
|
|
43
|
+
plan: RunPlanValue | null;
|
|
44
|
+
planning: boolean;
|
|
45
|
+
/** Live progress of the run this panel started. */
|
|
46
|
+
progress: RunProgressValue | null;
|
|
47
|
+
activeRunId: string | null;
|
|
48
|
+
/** Whatever went wrong last, in words an operator can act on. */
|
|
49
|
+
error: string | null;
|
|
50
|
+
};
|
|
51
|
+
export type Actions = {
|
|
52
|
+
[key: string]: unknown;
|
|
53
|
+
setSources: (items: IntegrationSource[], total: number) => void;
|
|
54
|
+
setSourcesPage: (page: number) => void;
|
|
55
|
+
setSourcesSearch: (search: string) => void;
|
|
56
|
+
setSourcesLoading: (loading: boolean) => void;
|
|
57
|
+
selectSource: (id: string | null) => void;
|
|
58
|
+
setTab: (tab: ConnectionTab) => void;
|
|
59
|
+
setEndpoints: (items: IntegrationEndpoint[], total: number) => void;
|
|
60
|
+
setEndpointsPage: (page: number) => void;
|
|
61
|
+
setEndpointsSearch: (search: string) => void;
|
|
62
|
+
setEndpointsLoading: (loading: boolean) => void;
|
|
63
|
+
selectEndpoint: (id: string | null) => void;
|
|
64
|
+
setMappings: (items: IntegrationMapping[], total: number) => void;
|
|
65
|
+
setMappingsPage: (page: number) => void;
|
|
66
|
+
setMappingsSearch: (search: string) => void;
|
|
67
|
+
setMappingsLoading: (loading: boolean) => void;
|
|
68
|
+
selectMapping: (id: string | null) => void;
|
|
69
|
+
setRuns: (items: IntegrationRun[], total: number) => void;
|
|
70
|
+
setRunsPage: (page: number) => void;
|
|
71
|
+
setRunsLoading: (loading: boolean) => void;
|
|
72
|
+
setSample: (sample: EndpointSampleValue | null) => void;
|
|
73
|
+
setSampling: (sampling: boolean) => void;
|
|
74
|
+
setPlan: (plan: RunPlanValue | null) => void;
|
|
75
|
+
setPlanning: (planning: boolean) => void;
|
|
76
|
+
setProgress: (progress: RunProgressValue | null) => void;
|
|
77
|
+
setActiveRun: (runId: string | null) => void;
|
|
78
|
+
setError: (error: string | null) => void;
|
|
79
|
+
};
|
|
80
|
+
export declare const initial: State;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const initial = {
|
|
2
|
+
sources: [],
|
|
3
|
+
sourcesTotal: 0,
|
|
4
|
+
sourcesPage: 1,
|
|
5
|
+
sourcesSearch: '',
|
|
6
|
+
sourcesLoading: false,
|
|
7
|
+
selectedSourceId: null,
|
|
8
|
+
tab: 'connection',
|
|
9
|
+
endpoints: [],
|
|
10
|
+
endpointsTotal: 0,
|
|
11
|
+
endpointsPage: 1,
|
|
12
|
+
endpointsSearch: '',
|
|
13
|
+
endpointsLoading: false,
|
|
14
|
+
selectedEndpointId: null,
|
|
15
|
+
mappings: [],
|
|
16
|
+
mappingsTotal: 0,
|
|
17
|
+
mappingsPage: 1,
|
|
18
|
+
mappingsSearch: '',
|
|
19
|
+
mappingsLoading: false,
|
|
20
|
+
selectedMappingId: null,
|
|
21
|
+
runs: [],
|
|
22
|
+
runsTotal: 0,
|
|
23
|
+
runsPage: 1,
|
|
24
|
+
runsLoading: false,
|
|
25
|
+
sample: null,
|
|
26
|
+
sampling: false,
|
|
27
|
+
plan: null,
|
|
28
|
+
planning: false,
|
|
29
|
+
progress: null,
|
|
30
|
+
activeRunId: null,
|
|
31
|
+
error: null
|
|
32
|
+
};
|