@burdenoff/microfe-movethewheels 2026.1031.6 → 2026.1101.1
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/MoveTheWheelsRoot.js +141 -133
- package/dist/MoveTheWheelsRoutes.js +29 -25
- package/dist/hooks/useCarrierIntegrations.js +141 -0
- package/dist/movethewheels/hooks/useCarrierIntegrations.d.ts +39 -0
- package/dist/movethewheels/pages/IntegrationsPage.d.ts +6 -0
- package/dist/movethewheels/utils/carrierIntegrations.d.ts +88 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/copy.js +16 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/pause.js +19 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/play.js +8 -0
- package/dist/pages/IntegrationsPage.js +616 -0
- package/dist/pages/SettingsPage.js +168 -150
- package/dist/utils/carrierIntegrations.js +105 -0
- package/package.json +3 -2
- package/src/movethewheels/MoveTheWheelsRoot.tsx +8 -0
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +4 -0
- package/src/movethewheels/hooks/useCarrierIntegrations.ts +221 -0
- package/src/movethewheels/pages/IntegrationsPage.tsx +752 -0
- package/src/movethewheels/pages/SettingsPage.tsx +19 -3
- package/src/movethewheels/utils/carrierIntegrations.ts +252 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { resolveGraphqlUrl as e } from "./graphqlClient.js";
|
|
2
|
+
//#region src/movethewheels/utils/carrierIntegrations.ts
|
|
3
|
+
var t = [
|
|
4
|
+
{
|
|
5
|
+
id: "PROJECT44",
|
|
6
|
+
label: "project44"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "FOURKITES",
|
|
10
|
+
label: "FourKites"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "DESCARTES",
|
|
14
|
+
label: "Descartes"
|
|
15
|
+
}
|
|
16
|
+
], n = [
|
|
17
|
+
"ACTIVE",
|
|
18
|
+
"PAUSED",
|
|
19
|
+
"DISABLED"
|
|
20
|
+
], r = [
|
|
21
|
+
"CREATED",
|
|
22
|
+
"PICKED_UP",
|
|
23
|
+
"IN_TRANSIT",
|
|
24
|
+
"ARRIVED",
|
|
25
|
+
"DEPARTED",
|
|
26
|
+
"OUT_FOR_DELIVERY",
|
|
27
|
+
"DELIVERED",
|
|
28
|
+
"FAILED",
|
|
29
|
+
"RETURNED"
|
|
30
|
+
], i = "x-webhook-signature", a = "\n id\n workspaceId\n provider\n name\n status\n lastSyncAt\n lastErrorAt\n lastErrorMessage\n createdAt\n updatedAt\n", o = `
|
|
31
|
+
query MtwCarrierIntegrations($filter: IntegrationFilterInput, $pagination: PaginationInput) {
|
|
32
|
+
integrations(filter: $filter, pagination: $pagination) {
|
|
33
|
+
${a}
|
|
34
|
+
}
|
|
35
|
+
integrationsCount(filter: $filter)
|
|
36
|
+
}
|
|
37
|
+
`, s = "\n query MtwCarrierIntegrationCounts {\n all: integrationsCount\n active: integrationsCount(filter: { status: ACTIVE })\n }\n", c = "\n query MtwCarrierIntegrationEvents($integrationId: ID, $pagination: PaginationInput) {\n integrationEvents(integrationId: $integrationId, pagination: $pagination) {\n id\n integrationId\n externalEventId\n shipmentId\n eventType\n status\n errorMessage\n receivedAt\n processedAt\n }\n }\n", l = `
|
|
38
|
+
mutation CreateMtwCarrierIntegration($input: CreateIntegrationInput!) {
|
|
39
|
+
createIntegration(input: $input) {
|
|
40
|
+
${a}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`, u = `
|
|
44
|
+
mutation UpdateMtwCarrierIntegration($id: ID!, $input: UpdateIntegrationInput!) {
|
|
45
|
+
updateIntegration(id: $id, input: $input) {
|
|
46
|
+
${a}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
`, d = "\n mutation DeleteMtwCarrierIntegration($id: ID!) {\n deleteIntegration(id: $id)\n }\n";
|
|
50
|
+
function f(e = globalThis.crypto) {
|
|
51
|
+
let t = e.getRandomValues(new Uint8Array(32));
|
|
52
|
+
return Array.from(t, (e) => e.toString(16).padStart(2, "0")).join("");
|
|
53
|
+
}
|
|
54
|
+
function p(e) {
|
|
55
|
+
return e.trim().length < 32 ? "Use a signing secret of at least 32 characters." : null;
|
|
56
|
+
}
|
|
57
|
+
function m(e) {
|
|
58
|
+
let t = e.trim();
|
|
59
|
+
return t ? t.length > 120 ? "Keep the name under 120 characters." : null : "Give the integration a name.";
|
|
60
|
+
}
|
|
61
|
+
function h(t, n, r, i) {
|
|
62
|
+
return `${new URL(e(t), i).origin}/api/integrations/webhook/${n}/${r}`;
|
|
63
|
+
}
|
|
64
|
+
function g(e = /* @__PURE__ */ new Date()) {
|
|
65
|
+
return JSON.stringify({
|
|
66
|
+
externalEventId: "evt_0001",
|
|
67
|
+
trackingNumber: "YOUR-TRACKING-NUMBER",
|
|
68
|
+
eventType: "IN_TRANSIT",
|
|
69
|
+
timestamp: e.toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
70
|
+
location: {
|
|
71
|
+
lat: 12.9716,
|
|
72
|
+
lng: 77.5946,
|
|
73
|
+
address: "Bengaluru hub"
|
|
74
|
+
},
|
|
75
|
+
details: "Departed origin hub"
|
|
76
|
+
}, null, 2);
|
|
77
|
+
}
|
|
78
|
+
function _(e) {
|
|
79
|
+
return `'${e.split("'").join("'\\''")}'`;
|
|
80
|
+
}
|
|
81
|
+
function v(e) {
|
|
82
|
+
return [
|
|
83
|
+
"BODY='{\"externalEventId\":\"evt_0001\",\"trackingNumber\":\"YOUR-TRACKING-NUMBER\",\"eventType\":\"IN_TRANSIT\"}'",
|
|
84
|
+
"SIG=$(printf '%s' \"$BODY\" | openssl dgst -sha256 -hmac \"$MTW_WEBHOOK_SECRET\" -hex | sed 's/^.* //')",
|
|
85
|
+
`curl -X POST ${_(e)} \\`,
|
|
86
|
+
" -H 'Content-Type: application/json' \\",
|
|
87
|
+
` -H "${i}: sha256=$SIG" \\`,
|
|
88
|
+
" --data \"$BODY\""
|
|
89
|
+
].join("\n");
|
|
90
|
+
}
|
|
91
|
+
function y(e) {
|
|
92
|
+
let t = e.toLowerCase().split("_").join(" ");
|
|
93
|
+
return t.charAt(0).toUpperCase() + t.slice(1);
|
|
94
|
+
}
|
|
95
|
+
function b(e) {
|
|
96
|
+
return e === "ACTIVE" ? "default" : e === "PAUSED" ? "secondary" : "outline";
|
|
97
|
+
}
|
|
98
|
+
function x(e) {
|
|
99
|
+
return e === "ACCEPTED" ? "default" : e === "DUPLICATE" || e === "PARTIAL" || e === "RECEIVED" ? "secondary" : "destructive";
|
|
100
|
+
}
|
|
101
|
+
function S(e) {
|
|
102
|
+
return e === "ACTIVE" ? "Signed deliveries are accepted and become tracking events." : e === "PAUSED" ? "Deliveries are refused with HTTP 423 so the sender can retry after you resume." : "Deliveries are refused with HTTP 410. Resume to accept them again.";
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { l as CREATE_INTEGRATION_MUTATION, d as DELETE_INTEGRATION_MUTATION, s as INTEGRATION_COUNTS_QUERY, c as INTEGRATION_EVENTS_QUERY, n as INTEGRATION_STATUSES, o as LIST_INTEGRATIONS_QUERY, t as ROADMAP_PROVIDERS, i as SIGNATURE_HEADER, r as TRACKING_EVENT_TYPES, u as UPDATE_INTEGRATION_MUTATION, v as curlExample, x as eventStatusTone, g as examplePayload, f as generateSigningSecret, y as humanize, m as integrationNameError, b as integrationStatusTone, p as signingSecretError, S as statusExplanation, h as webhookUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-movethewheels",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.1101.1",
|
|
4
4
|
"description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
38
38
|
"type:check": "bash scripts/build-limits.sh tsc --noEmit",
|
|
39
39
|
"validate:schema-drift": "graphql-schema-drift",
|
|
40
|
-
"sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run validate:schema-drift && bun run lint:sanity && bun run format && bun run type:check && bun run verify:live-logistics-contract && bun run verify:empty-reuse-retry && bun run verify:empty-reuse-recovery && bun run verify:trip-utilization-input && bun run verify:trip-draft-context && bun run verify:stock-binding-input && bun run verify:stock-observation-input && bun run verify:pending-pick-input && bun run verify:pick-review-ack && bun run verify:driver-license-expiry && bun run verify:tracking-alerts && bun run verify:freight-input && bun run verify:freight-recovery && bun run verify:freight-commercial && bun run verify:freight-invoice && bun run verify:procedure-input && bun run verify:consolidation && bun run verify:multimodal-single-attempt && bun run verify:workspace-predispatch && bun run verify:tracking-quality-outcomes && bun run verify:shipment-status-authority && bun run verify:free-time-permissions && bun run verify:core-codegen && bun run verify:order-vehicle-hold && bun run verify:pallet-preview && bun run verify:entity-export && bun run verify:entity-import && bun run verify:module-settings && bun run verify:consumed-fuel-preview && bun run build && bun run build-storybook'",
|
|
40
|
+
"sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run validate:schema-drift && bun run lint:sanity && bun run format && bun run type:check && bun run verify:live-logistics-contract && bun run verify:empty-reuse-retry && bun run verify:empty-reuse-recovery && bun run verify:trip-utilization-input && bun run verify:trip-draft-context && bun run verify:stock-binding-input && bun run verify:stock-observation-input && bun run verify:pending-pick-input && bun run verify:pick-review-ack && bun run verify:driver-license-expiry && bun run verify:tracking-alerts && bun run verify:freight-input && bun run verify:freight-recovery && bun run verify:freight-commercial && bun run verify:freight-invoice && bun run verify:procedure-input && bun run verify:consolidation && bun run verify:multimodal-single-attempt && bun run verify:workspace-predispatch && bun run verify:tracking-quality-outcomes && bun run verify:shipment-status-authority && bun run verify:free-time-permissions && bun run verify:core-codegen && bun run verify:order-vehicle-hold && bun run verify:pallet-preview && bun run verify:entity-export && bun run verify:entity-import && bun run verify:module-settings && bun run verify:carrier-integrations && bun run verify:consumed-fuel-preview && bun run build && bun run build-storybook'",
|
|
41
41
|
"storybook": "storybook dev -p 6006",
|
|
42
42
|
"build-storybook": "storybook build",
|
|
43
43
|
"test:storybook": "vitest run --project=storybook",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"verify:entity-export": "bun test scripts/check-entity-export.test.ts",
|
|
76
76
|
"verify:entity-import": "bun test scripts/check-entity-import.test.ts",
|
|
77
77
|
"verify:module-settings": "bun test scripts/check-module-settings.test.ts",
|
|
78
|
+
"verify:carrier-integrations": "bun test scripts/check-carrier-integrations.test.ts",
|
|
78
79
|
"verify:pallet-preview": "bun test scripts/check-pallet-preview.test.ts && bun scripts/verify-pallet-preview-hook.mjs",
|
|
79
80
|
"verify:consumed-fuel-preview": "bun test scripts/check-consumed-fuel-preview.test.ts && bun scripts/verify-consumed-fuel-preview-hook.mjs && bun scripts/verify-consumed-fuel-preview-component.mjs"
|
|
80
81
|
},
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
Store,
|
|
26
26
|
ArrowDownUp,
|
|
27
27
|
Settings,
|
|
28
|
+
Plug,
|
|
28
29
|
} from 'lucide-react';
|
|
29
30
|
import { MoveTheWheelsProvider } from './providers/MoveTheWheelsProvider';
|
|
30
31
|
import { ModuleSettingsProvider } from './providers/ModuleSettingsProvider';
|
|
@@ -309,6 +310,13 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
309
310
|
icon: <Bot size={16} />,
|
|
310
311
|
order: 13,
|
|
311
312
|
},
|
|
313
|
+
{
|
|
314
|
+
id: 'mtw-integrations',
|
|
315
|
+
label: tr('movethewheels.root.nav.integrations', 'Integrations'),
|
|
316
|
+
path: joinPath(basePath, 'integrations'),
|
|
317
|
+
icon: <Plug size={16} />,
|
|
318
|
+
order: 98,
|
|
319
|
+
},
|
|
312
320
|
{
|
|
313
321
|
id: 'mtw-settings',
|
|
314
322
|
label: tr('movethewheels.root.nav.settings', 'Settings'),
|
|
@@ -75,6 +75,7 @@ const CustomerDetailsPage = lazy(() => import('./pages/CustomerDetailsPage'));
|
|
|
75
75
|
const MarketplacePage = lazy(() => import('./pages/MarketplacePage'));
|
|
76
76
|
const ImportExportPage = lazy(() => import('./pages/ImportExportPage'));
|
|
77
77
|
const SettingsPage = lazy(() => import('./pages/SettingsPage'));
|
|
78
|
+
const IntegrationsPage = lazy(() => import('./pages/IntegrationsPage'));
|
|
78
79
|
const RoutesPage = lazy(() => import('./pages/RoutesPage'));
|
|
79
80
|
const RouteDetailsPage = lazy(() => import('./pages/RouteDetailsPage'));
|
|
80
81
|
const AnalyticsPage = lazy(() => import('./pages/AnalyticsPage'));
|
|
@@ -172,6 +173,9 @@ export function MoveTheWheelsRoutes() {
|
|
|
172
173
|
{/* Workspace operational defaults: currency, units, time zone (BOFF-10488) */}
|
|
173
174
|
<Route path="settings" element={<SettingsPage />} />
|
|
174
175
|
|
|
176
|
+
{/* Carrier, ELD and GPS tracking feeds as signed webhooks (BOFF-4404) */}
|
|
177
|
+
<Route path="integrations" element={<IntegrationsPage />} />
|
|
178
|
+
|
|
175
179
|
{/* Routes */}
|
|
176
180
|
<Route path="routes" element={<RoutesPage />} />
|
|
177
181
|
<Route path="routes/trip-utilization" element={<TripUtilizationPage />} />
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carrier and telematics integration hooks (BOFF-4404).
|
|
3
|
+
*
|
|
4
|
+
* Reads and writes the workspace's `MtwIntegration` rows and their delivery
|
|
5
|
+
* log through wspace-movethewheels-svc's integration module. The service
|
|
6
|
+
* validates every write (supported provider, signing-secret length) and never
|
|
7
|
+
* returns the signing secret.
|
|
8
|
+
*/
|
|
9
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
10
|
+
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
11
|
+
import { executeGraphQL } from '../utils/graphqlClient';
|
|
12
|
+
import {
|
|
13
|
+
CREATE_INTEGRATION_MUTATION,
|
|
14
|
+
DELETE_INTEGRATION_MUTATION,
|
|
15
|
+
INTEGRATION_COUNTS_QUERY,
|
|
16
|
+
INTEGRATION_EVENTS_QUERY,
|
|
17
|
+
LIST_INTEGRATIONS_QUERY,
|
|
18
|
+
UPDATE_INTEGRATION_MUTATION,
|
|
19
|
+
type CarrierIntegration,
|
|
20
|
+
type CarrierIntegrationEvent,
|
|
21
|
+
type IntegrationStatus,
|
|
22
|
+
type SupportedProvider,
|
|
23
|
+
} from '../utils/carrierIntegrations';
|
|
24
|
+
|
|
25
|
+
export interface CreateCarrierIntegrationInput {
|
|
26
|
+
provider: SupportedProvider;
|
|
27
|
+
name: string;
|
|
28
|
+
apiSecret: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface UpdateCarrierIntegrationInput {
|
|
32
|
+
name?: string;
|
|
33
|
+
status?: IntegrationStatus;
|
|
34
|
+
apiSecret?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function useGraphQLOptions() {
|
|
38
|
+
const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
|
|
39
|
+
useMoveTheWheels();
|
|
40
|
+
return useMemo(
|
|
41
|
+
() => ({
|
|
42
|
+
apiGatewayUrl,
|
|
43
|
+
authToken,
|
|
44
|
+
workspaceToken,
|
|
45
|
+
workspaceId,
|
|
46
|
+
tenantId,
|
|
47
|
+
actorId: currentUser?.id,
|
|
48
|
+
}),
|
|
49
|
+
[apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function toError(error: unknown, fallback: string): Error {
|
|
54
|
+
return error instanceof Error ? error : new Error(fallback);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function useCarrierIntegrations(options: {
|
|
58
|
+
status?: IntegrationStatus;
|
|
59
|
+
limit: number;
|
|
60
|
+
offset: number;
|
|
61
|
+
}) {
|
|
62
|
+
const graphQLOptions = useGraphQLOptions();
|
|
63
|
+
const { status, limit, offset } = options;
|
|
64
|
+
const [integrations, setIntegrations] = useState<CarrierIntegration[]>([]);
|
|
65
|
+
const [total, setTotal] = useState(0);
|
|
66
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
67
|
+
const [error, setError] = useState<Error | null>(null);
|
|
68
|
+
// Only the latest request may update state: a slower response for a
|
|
69
|
+
// previous filter or page must not overwrite the current view.
|
|
70
|
+
const latestRequest = useRef(0);
|
|
71
|
+
|
|
72
|
+
const load = useCallback(async () => {
|
|
73
|
+
const request = ++latestRequest.current;
|
|
74
|
+
setIsLoading(true);
|
|
75
|
+
setError(null);
|
|
76
|
+
try {
|
|
77
|
+
const data = await executeGraphQL<{
|
|
78
|
+
integrations: CarrierIntegration[];
|
|
79
|
+
integrationsCount: number;
|
|
80
|
+
}>(graphQLOptions, LIST_INTEGRATIONS_QUERY, {
|
|
81
|
+
filter: status ? { status } : undefined,
|
|
82
|
+
pagination: { limit, offset },
|
|
83
|
+
});
|
|
84
|
+
if (request !== latestRequest.current) return;
|
|
85
|
+
setIntegrations(data.integrations);
|
|
86
|
+
setTotal(data.integrationsCount);
|
|
87
|
+
} catch (loadError) {
|
|
88
|
+
if (request !== latestRequest.current) return;
|
|
89
|
+
setError(toError(loadError, 'Failed to load integrations'));
|
|
90
|
+
} finally {
|
|
91
|
+
if (request === latestRequest.current) setIsLoading(false);
|
|
92
|
+
}
|
|
93
|
+
}, [graphQLOptions, status, limit, offset]);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
void load();
|
|
97
|
+
return () => {
|
|
98
|
+
// Invalidate the in-flight request when the filter, page or unmount changes.
|
|
99
|
+
latestRequest.current += 1;
|
|
100
|
+
};
|
|
101
|
+
}, [load]);
|
|
102
|
+
|
|
103
|
+
// A refetch requested by a mutation that started under an earlier filter or
|
|
104
|
+
// page must load the view that is current when it completes, not the one
|
|
105
|
+
// captured when the mutation started.
|
|
106
|
+
const latestLoad = useRef(load);
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
latestLoad.current = load;
|
|
109
|
+
}, [load]);
|
|
110
|
+
const refetch = useCallback(() => latestLoad.current(), []);
|
|
111
|
+
|
|
112
|
+
return { integrations, total, isLoading, error, refetch };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Workspace-wide feed counts (all and active) in one request, independent of the list filter. */
|
|
116
|
+
export function useCarrierIntegrationCounts() {
|
|
117
|
+
const graphQLOptions = useGraphQLOptions();
|
|
118
|
+
const [counts, setCounts] = useState<{ all: number; active: number }>({ all: 0, active: 0 });
|
|
119
|
+
const latestRequest = useRef(0);
|
|
120
|
+
|
|
121
|
+
const load = useCallback(async () => {
|
|
122
|
+
const request = ++latestRequest.current;
|
|
123
|
+
try {
|
|
124
|
+
const data = await executeGraphQL<{ all: number; active: number }>(
|
|
125
|
+
graphQLOptions,
|
|
126
|
+
INTEGRATION_COUNTS_QUERY,
|
|
127
|
+
{}
|
|
128
|
+
);
|
|
129
|
+
if (request === latestRequest.current) setCounts({ all: data.all, active: data.active });
|
|
130
|
+
} catch {
|
|
131
|
+
// The list request surfaces load errors; the counts keep their last value.
|
|
132
|
+
}
|
|
133
|
+
}, [graphQLOptions]);
|
|
134
|
+
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
void load();
|
|
137
|
+
return () => {
|
|
138
|
+
latestRequest.current += 1;
|
|
139
|
+
};
|
|
140
|
+
}, [load]);
|
|
141
|
+
|
|
142
|
+
return { ...counts, refetch: load };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The most recent deliveries for one integration, newest first. */
|
|
146
|
+
export function useCarrierIntegrationEvents(
|
|
147
|
+
integrationId: string | null,
|
|
148
|
+
refreshKey: number,
|
|
149
|
+
limit = 20
|
|
150
|
+
) {
|
|
151
|
+
const graphQLOptions = useGraphQLOptions();
|
|
152
|
+
const [events, setEvents] = useState<CarrierIntegrationEvent[]>([]);
|
|
153
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
154
|
+
const [error, setError] = useState<Error | null>(null);
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (!integrationId) {
|
|
158
|
+
setEvents([]);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
let cancelled = false;
|
|
162
|
+
setIsLoading(true);
|
|
163
|
+
setError(null);
|
|
164
|
+
executeGraphQL<{ integrationEvents: CarrierIntegrationEvent[] }>(
|
|
165
|
+
graphQLOptions,
|
|
166
|
+
INTEGRATION_EVENTS_QUERY,
|
|
167
|
+
{ integrationId, pagination: { limit, offset: 0 } }
|
|
168
|
+
)
|
|
169
|
+
.then((data) => {
|
|
170
|
+
if (!cancelled) setEvents(data.integrationEvents);
|
|
171
|
+
})
|
|
172
|
+
.catch((loadError: unknown) => {
|
|
173
|
+
if (!cancelled) setError(toError(loadError, 'Failed to load deliveries'));
|
|
174
|
+
})
|
|
175
|
+
.finally(() => {
|
|
176
|
+
if (!cancelled) setIsLoading(false);
|
|
177
|
+
});
|
|
178
|
+
return () => {
|
|
179
|
+
cancelled = true;
|
|
180
|
+
};
|
|
181
|
+
}, [graphQLOptions, integrationId, refreshKey, limit]);
|
|
182
|
+
|
|
183
|
+
return { events, isLoading, error };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function useCarrierIntegrationActions() {
|
|
187
|
+
const graphQLOptions = useGraphQLOptions();
|
|
188
|
+
|
|
189
|
+
return useMemo(
|
|
190
|
+
() => ({
|
|
191
|
+
create: async (input: CreateCarrierIntegrationInput) => {
|
|
192
|
+
// A lost response must not create a second integration with a secret
|
|
193
|
+
// the operator never saw, so the write is attempted once.
|
|
194
|
+
const data = await executeGraphQL<{ createIntegration: CarrierIntegration }>(
|
|
195
|
+
graphQLOptions,
|
|
196
|
+
CREATE_INTEGRATION_MUTATION,
|
|
197
|
+
{ input: { ...input, name: input.name.trim(), status: 'ACTIVE' } },
|
|
198
|
+
'SINGLE_ATTEMPT'
|
|
199
|
+
);
|
|
200
|
+
return data.createIntegration;
|
|
201
|
+
},
|
|
202
|
+
update: async (id: string, input: UpdateCarrierIntegrationInput) => {
|
|
203
|
+
const data = await executeGraphQL<{ updateIntegration: CarrierIntegration }>(
|
|
204
|
+
graphQLOptions,
|
|
205
|
+
UPDATE_INTEGRATION_MUTATION,
|
|
206
|
+
{ id, input: input.name !== undefined ? { ...input, name: input.name.trim() } : input }
|
|
207
|
+
);
|
|
208
|
+
return data.updateIntegration;
|
|
209
|
+
},
|
|
210
|
+
remove: async (id: string) => {
|
|
211
|
+
const data = await executeGraphQL<{ deleteIntegration: boolean }>(
|
|
212
|
+
graphQLOptions,
|
|
213
|
+
DELETE_INTEGRATION_MUTATION,
|
|
214
|
+
{ id }
|
|
215
|
+
);
|
|
216
|
+
return data.deleteIntegration;
|
|
217
|
+
},
|
|
218
|
+
}),
|
|
219
|
+
[graphQLOptions]
|
|
220
|
+
);
|
|
221
|
+
}
|