@tangle-network/sandbox-ui 0.113.7 → 0.114.0
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/WorkflowGraph-B-NsdYz_.js +1961 -0
- package/dist/assets.d.ts +214 -206
- package/dist/assets.js +1057 -908
- package/dist/auth-CWe17Hly.js +37 -0
- package/dist/auth.d.ts +2 -30
- package/dist/auth.js +2 -15
- package/dist/chat-BOvFAe6p.js +903 -0
- package/dist/chat.d.ts +2 -230
- package/dist/chat.js +2 -34
- package/dist/connectors.d.ts +52 -49
- package/dist/connectors.js +595 -643
- package/dist/dashboard-CZ3VgNPR.js +4969 -0
- package/dist/dashboard.d.ts +4 -1297
- package/dist/dashboard.js +4 -152
- package/dist/editor.d.ts +2 -1
- package/dist/editor.js +2 -31
- package/dist/files.d.ts +2 -6
- package/dist/files.js +2 -33
- package/dist/globals.css +63 -3
- package/dist/hooks.d.ts +146 -148
- package/dist/hooks.js +222 -55
- package/dist/index-CKRaj1pM.d.ts +776 -0
- package/dist/index-CXX5IpH2.d.ts +2 -0
- package/dist/index-DPePq7c8.d.ts +1340 -0
- package/dist/index-DiyLLZuJ.d.ts +230 -0
- package/dist/index-DvmZLMT1.d.ts +30 -0
- package/dist/index-I8ZLVtHd.d.ts +330 -0
- package/dist/index-PRM636Ex.d.ts +86 -0
- package/dist/index.d.ts +21 -33
- package/dist/index.js +21 -701
- package/dist/info-panel-C9cqDzOL.js +594 -0
- package/dist/integrations.d.ts +133 -141
- package/dist/integrations.js +491 -673
- package/dist/markdown.d.ts +2 -1
- package/dist/markdown.js +2 -13
- package/dist/model-I-NIxeM-.js +1250 -0
- package/dist/model-brand-B9A4H9pJ.d.ts +98 -0
- package/dist/model-brand-hMyDTUqB.js +624 -0
- package/dist/motion-DkyBRJb9.js +37 -0
- package/dist/openui.d.ts +2 -1
- package/dist/openui.js +2 -7
- package/dist/pages.d.ts +307 -300
- package/dist/pages.js +4404 -4181
- package/dist/primitives-Dh1vG6J3.js +113 -0
- package/dist/primitives.d.ts +2 -74
- package/dist/primitives.js +2 -204
- package/dist/provider-logo-DjqmConF.js +311 -0
- package/dist/run.d.ts +2 -1
- package/dist/run.js +2 -23
- package/dist/sdk-hooks.d.ts +3 -4
- package/dist/sdk-hooks.js +3 -31
- package/dist/sidecar-DgH_vQ6m.d.ts +9 -0
- package/dist/stores.d.ts +14 -15
- package/dist/stores.js +13 -81
- package/dist/styles.css +63 -3
- package/dist/template-card-L-4gmjx2.d.ts +90 -0
- package/dist/terminal-CN9ZgHRt.js +197 -0
- package/dist/terminal.d.ts +60 -60
- package/dist/terminal.js +2 -10
- package/dist/tokens.css +51 -0
- package/dist/types.d.ts +4 -11
- package/dist/types.js +0 -1
- package/dist/use-pty-session-C8cE0adT.js +669 -0
- package/dist/use-sidecar-auth-CCTJcrq7.d.ts +145 -0
- package/dist/use-sidecar-auth-DkdcufOz.js +742 -0
- package/dist/utils-DzWbktWG.js +2 -0
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +2 -37
- package/dist/workbench-BCaCKl-J.js +8347 -0
- package/dist/workbench.d.ts +2 -310
- package/dist/workbench.js +2 -44
- package/dist/workflows.d.ts +341 -339
- package/dist/workflows.js +74 -109
- package/dist/workspace-B6AtI6ld.js +2510 -0
- package/dist/workspace.d.ts +2 -761
- package/dist/workspace.js +2 -51
- package/package.json +46 -40
- package/dist/WorkflowGraph-2QOORYIR.js +0 -1722
- package/dist/chunk-2BUPSB7O.js +0 -0
- package/dist/chunk-3KBNPQQF.js +0 -2697
- package/dist/chunk-3LIXVEEE.js +0 -279
- package/dist/chunk-72UF2H5H.js +0 -707
- package/dist/chunk-AI25G5MX.js +0 -944
- package/dist/chunk-ANSC5AFC.js +0 -314
- package/dist/chunk-AZ3AWMTM.js +0 -8
- package/dist/chunk-BGVEHENC.js +0 -10
- package/dist/chunk-CZ7CSFO4.js +0 -39
- package/dist/chunk-DYXXYGS4.js +0 -4700
- package/dist/chunk-EI44GEQ5.js +0 -6
- package/dist/chunk-IACRDXER.js +0 -219
- package/dist/chunk-IOB2PW5Z.js +0 -55
- package/dist/chunk-M4K7ZGR2.js +0 -24
- package/dist/chunk-MYZAW4RX.js +0 -665
- package/dist/chunk-OMUIPJQZ.js +0 -555
- package/dist/chunk-RQZRXKMR.js +0 -398
- package/dist/chunk-SOKKTB7W.js +0 -230
- package/dist/chunk-TADGS4Q7.js +0 -1010
- package/dist/chunk-UCQNOUN3.js +0 -7964
- package/dist/chunk-ULPLRBMF.js +0 -34
- package/dist/chunk-WID73FPH.js +0 -89
- package/dist/chunk-X3YXP7FG.js +0 -38
- package/dist/logo-DiAjq759.d.ts +0 -15
- package/dist/model-brand-BNnaUKKZ.d.ts +0 -99
- package/dist/ports-list-CDIvTNnX.d.ts +0 -17
- package/dist/sdk-hooks-Djkjq4cp.d.ts +0 -145
- package/dist/template-card-Bn-Bw5xC.d.ts +0 -87
package/dist/integrations.js
CHANGED
|
@@ -1,692 +1,510 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
normalizeProviderId,
|
|
4
|
-
providerLogoCandidates
|
|
5
|
-
} from "./chunk-3LIXVEEE.js";
|
|
6
|
-
import "./chunk-CZ7CSFO4.js";
|
|
7
|
-
|
|
8
|
-
// src/integrations/integrations-panel.tsx
|
|
9
|
-
import * as React from "react";
|
|
10
|
-
import {
|
|
11
|
-
Button,
|
|
12
|
-
Card,
|
|
13
|
-
CardContent,
|
|
14
|
-
Dialog,
|
|
15
|
-
DialogContent,
|
|
16
|
-
DialogDescription,
|
|
17
|
-
DialogFooter,
|
|
18
|
-
DialogHeader,
|
|
19
|
-
DialogTitle,
|
|
20
|
-
DropdownMenu,
|
|
21
|
-
DropdownMenuContent,
|
|
22
|
-
DropdownMenuItem,
|
|
23
|
-
DropdownMenuSeparator,
|
|
24
|
-
DropdownMenuTrigger,
|
|
25
|
-
EmptyState
|
|
26
|
-
} from "@tangle-network/ui/primitives";
|
|
27
|
-
import { cn } from "@tangle-network/ui/utils";
|
|
28
|
-
import { Check, ExternalLink, MoreVertical, Search, Unplug } from "lucide-react";
|
|
29
|
-
import { focusFieldWithin, focusRing } from "@tangle-network/ui/utils";
|
|
1
|
+
import { n as normalizeProviderId, r as providerLogoCandidates, t as ProviderIcon } from "./provider-logo-DjqmConF.js";
|
|
2
|
+
import { Button, Card, CardContent, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptyState } from "@tangle-network/ui/primitives";
|
|
30
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
4
|
+
import * as React$1 from "react";
|
|
5
|
+
import { cn, focusFieldWithin, focusRing } from "@tangle-network/ui/utils";
|
|
6
|
+
import { Check, ExternalLink, MoreVertical, Search, Unplug } from "lucide-react";
|
|
7
|
+
//#region src/integrations/integrations-panel.tsx
|
|
8
|
+
const DEFAULT_FEATURED_IDS = [
|
|
9
|
+
"gmail",
|
|
10
|
+
"google-sheets",
|
|
11
|
+
"google-drive",
|
|
12
|
+
"google-docs",
|
|
13
|
+
"google-calendar",
|
|
14
|
+
"outlook",
|
|
15
|
+
"outlook-mail",
|
|
16
|
+
"microsoft-calendar",
|
|
17
|
+
"microsoft-excel",
|
|
18
|
+
"microsoft-teams",
|
|
19
|
+
"slack",
|
|
20
|
+
"discord",
|
|
21
|
+
"hubspot",
|
|
22
|
+
"salesforce",
|
|
23
|
+
"notion",
|
|
24
|
+
"airtable",
|
|
25
|
+
"github",
|
|
26
|
+
"gitlab",
|
|
27
|
+
"linear",
|
|
28
|
+
"jira",
|
|
29
|
+
"asana",
|
|
30
|
+
"stripe",
|
|
31
|
+
"stripe-pack",
|
|
32
|
+
"twilio",
|
|
33
|
+
"twilio-sms",
|
|
34
|
+
"linkedin",
|
|
35
|
+
"zoom",
|
|
36
|
+
"shopify",
|
|
37
|
+
"mailchimp",
|
|
38
|
+
"zendesk",
|
|
39
|
+
"intercom",
|
|
40
|
+
"dropbox",
|
|
41
|
+
"webhook"
|
|
65
42
|
];
|
|
66
43
|
function defaultConnectorOf(provider) {
|
|
67
|
-
|
|
44
|
+
return provider.connectors?.[0]?.connectorId ?? provider.providerId;
|
|
68
45
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
focusRing,
|
|
114
|
-
sort === value ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
115
|
-
),
|
|
116
|
-
children: label
|
|
117
|
-
},
|
|
118
|
-
value
|
|
119
|
-
))
|
|
120
|
-
}
|
|
121
|
-
)
|
|
122
|
-
] });
|
|
46
|
+
const TILE_GRID_CLASS = "grid grid-cols-3 gap-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6";
|
|
47
|
+
const DEFAULT_SKELETON_COUNT = 12;
|
|
48
|
+
const TILE_BOX_CLASS = "flex min-w-0 aspect-square flex-col items-center justify-center gap-2 rounded-xl border p-3 text-center";
|
|
49
|
+
/**
|
|
50
|
+
* Search + sort row. Rendered by the skeleton AND the loaded panel from this
|
|
51
|
+
* one component, which is the whole point: the row is ~74px tall with a 16px
|
|
52
|
+
* gap under it, and a skeleton that omitted it under-reserved the page by that
|
|
53
|
+
* much on every single load — the panel visibly dropped when data arrived.
|
|
54
|
+
* Height parity here is structural, not a matched pair of magic numbers.
|
|
55
|
+
*
|
|
56
|
+
* The controls stay live while loading. They drive local state only (`query`,
|
|
57
|
+
* `sort`), so typing during the fetch is applied the moment the catalog lands
|
|
58
|
+
* instead of being thrown away — and keeping the same input element mounted
|
|
59
|
+
* across the transition is what preserves its autofocus.
|
|
60
|
+
*/
|
|
61
|
+
function IntegrationsToolbar({ query, onQueryChange, sort, onSortChange }) {
|
|
62
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
63
|
+
className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between",
|
|
64
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
65
|
+
className: `flex flex-1 items-center gap-2 rounded-lg border bg-card px-3 py-2 ${focusFieldWithin}`,
|
|
66
|
+
children: [/* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), /* @__PURE__ */ jsx("input", {
|
|
67
|
+
type: "text",
|
|
68
|
+
value: query,
|
|
69
|
+
onChange: (e) => onQueryChange(e.target.value),
|
|
70
|
+
"aria-label": "Search integrations",
|
|
71
|
+
placeholder: "Search integrations...",
|
|
72
|
+
autoFocus: true,
|
|
73
|
+
"data-testid": "integration-search",
|
|
74
|
+
className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
|
|
75
|
+
})]
|
|
76
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
77
|
+
role: "group",
|
|
78
|
+
"aria-label": "Sort integrations",
|
|
79
|
+
className: "flex shrink-0 items-center gap-1 rounded-lg border border-border bg-card p-1",
|
|
80
|
+
children: [["featured", "Featured"], ["alpha", "A–Z"]].map(([value, label]) => /* @__PURE__ */ jsx("button", {
|
|
81
|
+
type: "button",
|
|
82
|
+
"aria-pressed": sort === value,
|
|
83
|
+
onClick: () => onSortChange(value),
|
|
84
|
+
"data-testid": `sort-${value}`,
|
|
85
|
+
className: cn("rounded-md px-2.5 py-1 text-xs font-medium transition-colors", focusRing, sort === value ? "bg-accent text-foreground" : "text-muted-foreground hover:text-foreground"),
|
|
86
|
+
children: label
|
|
87
|
+
}, value))
|
|
88
|
+
})]
|
|
89
|
+
});
|
|
123
90
|
}
|
|
124
|
-
|
|
91
|
+
const LOGO_SIZE = 48;
|
|
125
92
|
function buildConnectionIndex(connections) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
93
|
+
const index = /* @__PURE__ */ new Map();
|
|
94
|
+
for (const conn of connections) {
|
|
95
|
+
if (conn.status === "revoked") continue;
|
|
96
|
+
index.set(`${conn.providerId}:${conn.connectorId ?? conn.providerId}`, conn);
|
|
97
|
+
}
|
|
98
|
+
return index;
|
|
132
99
|
}
|
|
133
100
|
function makeFeaturedRank(featuredIds) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
101
|
+
const rank = /* @__PURE__ */ new Map();
|
|
102
|
+
featuredIds.forEach((id, i) => {
|
|
103
|
+
const norm = normalizeProviderId(id);
|
|
104
|
+
if (!rank.has(id)) rank.set(id, i);
|
|
105
|
+
if (!rank.has(norm)) rank.set(norm, i);
|
|
106
|
+
});
|
|
107
|
+
return rank;
|
|
141
108
|
}
|
|
142
109
|
function rankOf(provider, rank) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return norm ?? Number.MAX_SAFE_INTEGER;
|
|
110
|
+
const raw = provider.providerId.toLowerCase();
|
|
111
|
+
const direct = rank.get(raw);
|
|
112
|
+
if (direct !== void 0) return direct;
|
|
113
|
+
return rank.get(normalizeProviderId(raw)) ?? Number.MAX_SAFE_INTEGER;
|
|
148
114
|
}
|
|
149
115
|
function displayNameOf(provider) {
|
|
150
|
-
|
|
116
|
+
return provider.displayName ?? provider.title ?? provider.providerId.replace(/[-_]/g, " ");
|
|
151
117
|
}
|
|
152
118
|
function matchesQuery(provider, q) {
|
|
153
|
-
|
|
154
|
-
return hay.includes(q);
|
|
119
|
+
return `${displayNameOf(provider)} ${provider.providerId} ${provider.description ?? ""}`.toLowerCase().includes(q);
|
|
155
120
|
}
|
|
156
|
-
function IntegrationsPanel({
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
" Disconnect"
|
|
460
|
-
]
|
|
461
|
-
}
|
|
462
|
-
)
|
|
463
|
-
]
|
|
464
|
-
}
|
|
465
|
-
)
|
|
466
|
-
] })
|
|
467
|
-
]
|
|
468
|
-
},
|
|
469
|
-
`${provider.providerId}:${connectorId}`
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
return /* @__PURE__ */ jsxs(
|
|
473
|
-
"button",
|
|
474
|
-
{
|
|
475
|
-
type: "button",
|
|
476
|
-
"data-testid": `integration-${provider.providerId}`,
|
|
477
|
-
"data-connected": "false",
|
|
478
|
-
"data-connecting": connectingId === provider.providerId ? "true" : void 0,
|
|
479
|
-
disabled: connectingId === provider.providerId,
|
|
480
|
-
"aria-busy": connectingId === provider.providerId,
|
|
481
|
-
onClick: () => {
|
|
482
|
-
void startConnect({
|
|
483
|
-
providerId: provider.providerId,
|
|
484
|
-
connectorId
|
|
485
|
-
});
|
|
486
|
-
},
|
|
487
|
-
title: provider.description ? provider.description : `Connect ${name}`,
|
|
488
|
-
className: cn(
|
|
489
|
-
TILE_BOX_CLASS,
|
|
490
|
-
"group border-border bg-card transition-all",
|
|
491
|
-
"hover:border-[var(--border-strong)] hover:bg-accent/40 hover:shadow-sm",
|
|
492
|
-
focusRing
|
|
493
|
-
),
|
|
494
|
-
children: [
|
|
495
|
-
/* @__PURE__ */ jsx(
|
|
496
|
-
ProviderIcon,
|
|
497
|
-
{
|
|
498
|
-
id: provider.providerId,
|
|
499
|
-
iconUrl: provider.iconUrl,
|
|
500
|
-
displayName: displayNameOf(provider),
|
|
501
|
-
size: LOGO_SIZE,
|
|
502
|
-
className: "rounded-2xl"
|
|
503
|
-
}
|
|
504
|
-
),
|
|
505
|
-
/* @__PURE__ */ jsx("span", { className: "line-clamp-2 h-8 w-full text-xs font-medium leading-4 text-foreground", children: name })
|
|
506
|
-
]
|
|
507
|
-
},
|
|
508
|
-
`${provider.providerId}:${connectorId}`
|
|
509
|
-
);
|
|
510
|
-
}) }),
|
|
511
|
-
/* @__PURE__ */ jsx(
|
|
512
|
-
Dialog,
|
|
513
|
-
{
|
|
514
|
-
open: !!disconnectTarget,
|
|
515
|
-
onOpenChange: (open) => {
|
|
516
|
-
if (!open) closeDisconnect();
|
|
517
|
-
},
|
|
518
|
-
children: /* @__PURE__ */ jsxs(
|
|
519
|
-
DialogContent,
|
|
520
|
-
{
|
|
521
|
-
className: "max-w-sm",
|
|
522
|
-
onCloseAutoFocus: (e) => e.preventDefault(),
|
|
523
|
-
children: [
|
|
524
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
525
|
-
/* @__PURE__ */ jsxs(DialogTitle, { children: [
|
|
526
|
-
"Disconnect ",
|
|
527
|
-
disconnectTarget?.name ?? "integration",
|
|
528
|
-
"?"
|
|
529
|
-
] }),
|
|
530
|
-
/* @__PURE__ */ jsxs(DialogDescription, { children: [
|
|
531
|
-
"This removes Sandbox's access to your",
|
|
532
|
-
" ",
|
|
533
|
-
disconnectTarget?.name ?? "this",
|
|
534
|
-
" account",
|
|
535
|
-
disconnectAccountLabel ? ` (${disconnectAccountLabel})` : "",
|
|
536
|
-
". You can reconnect anytime."
|
|
537
|
-
] })
|
|
538
|
-
] }),
|
|
539
|
-
disconnectError ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", role: "alert", children: disconnectError }) : null,
|
|
540
|
-
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
541
|
-
/* @__PURE__ */ jsx(
|
|
542
|
-
Button,
|
|
543
|
-
{
|
|
544
|
-
variant: "outline",
|
|
545
|
-
onClick: closeDisconnect,
|
|
546
|
-
"data-testid": "cancel-disconnect",
|
|
547
|
-
children: "Cancel"
|
|
548
|
-
}
|
|
549
|
-
),
|
|
550
|
-
/* @__PURE__ */ jsx(
|
|
551
|
-
Button,
|
|
552
|
-
{
|
|
553
|
-
variant: "destructive",
|
|
554
|
-
loading: isDisconnecting,
|
|
555
|
-
onClick: confirmDisconnect,
|
|
556
|
-
"data-testid": "confirm-disconnect",
|
|
557
|
-
children: "Disconnect"
|
|
558
|
-
}
|
|
559
|
-
)
|
|
560
|
-
] })
|
|
561
|
-
]
|
|
562
|
-
}
|
|
563
|
-
)
|
|
564
|
-
}
|
|
565
|
-
)
|
|
566
|
-
] });
|
|
121
|
+
function IntegrationsPanel({ catalog, connections, healthByConnectionId, isLoading, error, onConnect, onDisconnect, getManageHref, onManage, emptyCatalogLabel = "No integrations available yet.", featuredIds = DEFAULT_FEATURED_IDS, defaultSort = "featured", skeletonCount = DEFAULT_SKELETON_COUNT, className }) {
|
|
122
|
+
const [query, setQuery] = React$1.useState("");
|
|
123
|
+
const [sort, setSort] = React$1.useState(defaultSort);
|
|
124
|
+
const [disconnectTarget, setDisconnectTarget] = React$1.useState(null);
|
|
125
|
+
const [isDisconnecting, setIsDisconnecting] = React$1.useState(false);
|
|
126
|
+
const [disconnectError, setDisconnectError] = React$1.useState(null);
|
|
127
|
+
const activeDisconnect = React$1.useRef(0);
|
|
128
|
+
const connectionIndex = React$1.useMemo(() => buildConnectionIndex(connections), [connections]);
|
|
129
|
+
const featuredRank = React$1.useMemo(() => makeFeaturedRank(featuredIds), [featuredIds]);
|
|
130
|
+
const visible = React$1.useMemo(() => {
|
|
131
|
+
const q = query.trim().toLowerCase();
|
|
132
|
+
const sorted = [...q ? catalog.filter((p) => matchesQuery(p, q)) : catalog];
|
|
133
|
+
sorted.sort((a, b) => {
|
|
134
|
+
if (sort === "featured") {
|
|
135
|
+
const ra = rankOf(a, featuredRank);
|
|
136
|
+
const rb = rankOf(b, featuredRank);
|
|
137
|
+
if (ra !== rb) return ra - rb;
|
|
138
|
+
}
|
|
139
|
+
return displayNameOf(a).localeCompare(displayNameOf(b));
|
|
140
|
+
});
|
|
141
|
+
return sorted;
|
|
142
|
+
}, [
|
|
143
|
+
catalog,
|
|
144
|
+
query,
|
|
145
|
+
sort,
|
|
146
|
+
featuredRank
|
|
147
|
+
]);
|
|
148
|
+
const closeDisconnect = React$1.useCallback(() => {
|
|
149
|
+
activeDisconnect.current += 1;
|
|
150
|
+
setIsDisconnecting(false);
|
|
151
|
+
setDisconnectTarget(null);
|
|
152
|
+
setDisconnectError(null);
|
|
153
|
+
}, []);
|
|
154
|
+
const [connectingId, setConnectingId] = React$1.useState(null);
|
|
155
|
+
const [connectError, setConnectError] = React$1.useState(null);
|
|
156
|
+
const activeConnect = React$1.useRef(0);
|
|
157
|
+
const startConnect = React$1.useCallback(async (input) => {
|
|
158
|
+
const reqId = activeConnect.current += 1;
|
|
159
|
+
setConnectError(null);
|
|
160
|
+
let pending;
|
|
161
|
+
try {
|
|
162
|
+
pending = onConnect(input);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
setConnectError(e instanceof Error ? e.message : "Failed to start the connection.");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (typeof pending?.then !== "function") return;
|
|
168
|
+
setConnectingId(input.providerId);
|
|
169
|
+
try {
|
|
170
|
+
await pending;
|
|
171
|
+
} catch (e) {
|
|
172
|
+
if (activeConnect.current !== reqId) return;
|
|
173
|
+
setConnectError(e instanceof Error ? e.message : "Failed to start the connection.");
|
|
174
|
+
} finally {
|
|
175
|
+
if (activeConnect.current === reqId) setConnectingId(null);
|
|
176
|
+
}
|
|
177
|
+
}, [onConnect]);
|
|
178
|
+
const confirmDisconnect = React$1.useCallback(async () => {
|
|
179
|
+
if (!disconnectTarget) return;
|
|
180
|
+
const reqId = activeDisconnect.current += 1;
|
|
181
|
+
setIsDisconnecting(true);
|
|
182
|
+
setDisconnectError(null);
|
|
183
|
+
try {
|
|
184
|
+
await Promise.resolve(onDisconnect(disconnectTarget.connectionId));
|
|
185
|
+
if (activeDisconnect.current !== reqId) return;
|
|
186
|
+
setDisconnectTarget(null);
|
|
187
|
+
} catch (e) {
|
|
188
|
+
if (activeDisconnect.current !== reqId) return;
|
|
189
|
+
setDisconnectError(e instanceof Error ? e.message : "Failed to disconnect.");
|
|
190
|
+
} finally {
|
|
191
|
+
if (activeDisconnect.current === reqId) setIsDisconnecting(false);
|
|
192
|
+
}
|
|
193
|
+
}, [disconnectTarget, onDisconnect]);
|
|
194
|
+
if (error) return /* @__PURE__ */ jsx(Card, {
|
|
195
|
+
className: cn("border-destructive/50", className),
|
|
196
|
+
children: /* @__PURE__ */ jsx(CardContent, {
|
|
197
|
+
className: "py-6",
|
|
198
|
+
children: /* @__PURE__ */ jsxs("p", {
|
|
199
|
+
className: "text-sm text-destructive",
|
|
200
|
+
children: ["Failed to load integrations: ", error.message]
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
});
|
|
204
|
+
if (isLoading && catalog.length === 0) return /* @__PURE__ */ jsxs("div", {
|
|
205
|
+
className: cn("space-y-4", className),
|
|
206
|
+
"data-testid": "integrations-skeleton",
|
|
207
|
+
children: [/* @__PURE__ */ jsx(IntegrationsToolbar, {
|
|
208
|
+
query,
|
|
209
|
+
onQueryChange: setQuery,
|
|
210
|
+
sort,
|
|
211
|
+
onSortChange: setSort
|
|
212
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
213
|
+
className: TILE_GRID_CLASS,
|
|
214
|
+
children: Array.from({ length: skeletonCount }).map((_, i) => /* @__PURE__ */ jsxs("div", {
|
|
215
|
+
"aria-hidden": true,
|
|
216
|
+
className: cn(TILE_BOX_CLASS, "animate-pulse border-border bg-muted/40"),
|
|
217
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
218
|
+
className: "shrink-0 rounded-2xl bg-muted",
|
|
219
|
+
style: {
|
|
220
|
+
width: LOGO_SIZE,
|
|
221
|
+
height: LOGO_SIZE
|
|
222
|
+
}
|
|
223
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
224
|
+
className: "flex h-8 w-full items-center justify-center",
|
|
225
|
+
children: /* @__PURE__ */ jsx("div", { className: "h-2.5 w-3/4 rounded bg-muted" })
|
|
226
|
+
})]
|
|
227
|
+
}, i))
|
|
228
|
+
})]
|
|
229
|
+
});
|
|
230
|
+
if (catalog.length === 0) return /* @__PURE__ */ jsx(EmptyState, {
|
|
231
|
+
title: "No integrations",
|
|
232
|
+
description: emptyCatalogLabel,
|
|
233
|
+
className
|
|
234
|
+
});
|
|
235
|
+
const disconnectAccountLabel = disconnectTarget?.accountDisplay;
|
|
236
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
237
|
+
className: cn("space-y-4", className),
|
|
238
|
+
"data-testid": "integrations-panel",
|
|
239
|
+
children: [
|
|
240
|
+
/* @__PURE__ */ jsx(IntegrationsToolbar, {
|
|
241
|
+
query,
|
|
242
|
+
onQueryChange: setQuery,
|
|
243
|
+
sort,
|
|
244
|
+
onSortChange: setSort
|
|
245
|
+
}),
|
|
246
|
+
connectError ? /* @__PURE__ */ jsx("div", {
|
|
247
|
+
role: "alert",
|
|
248
|
+
"data-testid": "integration-connect-error",
|
|
249
|
+
className: "mb-3 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
250
|
+
children: connectError
|
|
251
|
+
}) : null,
|
|
252
|
+
visible.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
|
|
253
|
+
title: "No matches",
|
|
254
|
+
description: `No integrations match "${query.trim()}".`
|
|
255
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
256
|
+
className: TILE_GRID_CLASS,
|
|
257
|
+
children: visible.map((provider) => {
|
|
258
|
+
const connectorId = defaultConnectorOf(provider);
|
|
259
|
+
const live = connectionIndex.get(`${provider.providerId}:${connectorId}`);
|
|
260
|
+
const name = displayNameOf(provider);
|
|
261
|
+
if (Boolean(live) && live) {
|
|
262
|
+
const manageHref = getManageHref?.(live);
|
|
263
|
+
const canManage = Boolean(manageHref) || Boolean(onManage);
|
|
264
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
265
|
+
"data-testid": `integration-${provider.providerId}`,
|
|
266
|
+
"data-connected": "true",
|
|
267
|
+
title: live.accountDisplay ? `${name} — ${live.accountDisplay}` : name,
|
|
268
|
+
className: cn(TILE_BOX_CLASS, "group relative", "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)]"),
|
|
269
|
+
children: [
|
|
270
|
+
manageHref ? /* @__PURE__ */ jsx("a", {
|
|
271
|
+
href: manageHref,
|
|
272
|
+
target: "_blank",
|
|
273
|
+
rel: "noopener noreferrer",
|
|
274
|
+
"aria-label": `Manage ${name} (opens in new window)`,
|
|
275
|
+
"data-testid": `manage-${provider.providerId}`,
|
|
276
|
+
className: `absolute inset-0 z-10 rounded-xl ${focusRing}`
|
|
277
|
+
}) : null,
|
|
278
|
+
/* @__PURE__ */ jsx("span", {
|
|
279
|
+
className: "pointer-events-none absolute left-2 top-2 flex h-4 w-4 items-center justify-center rounded-full bg-[var(--surface-success-text)] text-white",
|
|
280
|
+
children: /* @__PURE__ */ jsx(Check, {
|
|
281
|
+
className: "h-3 w-3",
|
|
282
|
+
strokeWidth: 3
|
|
283
|
+
})
|
|
284
|
+
}),
|
|
285
|
+
/* @__PURE__ */ jsxs("div", {
|
|
286
|
+
className: "pointer-events-none flex w-full min-w-0 flex-col items-center gap-2",
|
|
287
|
+
children: [/* @__PURE__ */ jsx(ProviderIcon, {
|
|
288
|
+
id: provider.providerId,
|
|
289
|
+
iconUrl: provider.iconUrl,
|
|
290
|
+
displayName: displayNameOf(provider),
|
|
291
|
+
size: LOGO_SIZE,
|
|
292
|
+
className: "rounded-2xl"
|
|
293
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
294
|
+
className: "flex h-8 w-full flex-col justify-center leading-4",
|
|
295
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
296
|
+
className: cn("w-full text-xs font-medium text-foreground", live.accountDisplay ? "truncate" : "line-clamp-2"),
|
|
297
|
+
children: name
|
|
298
|
+
}), live.accountDisplay ? /* @__PURE__ */ jsx("span", {
|
|
299
|
+
className: "w-full truncate text-[11px] leading-4 text-muted-foreground",
|
|
300
|
+
"data-testid": `account-${provider.providerId}`,
|
|
301
|
+
children: live.accountDisplay
|
|
302
|
+
}) : null]
|
|
303
|
+
})]
|
|
304
|
+
}),
|
|
305
|
+
/* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
306
|
+
asChild: true,
|
|
307
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
308
|
+
type: "button",
|
|
309
|
+
"data-testid": `menu-${provider.providerId}`,
|
|
310
|
+
"aria-label": `More actions for ${name}`,
|
|
311
|
+
title: "More actions",
|
|
312
|
+
className: `absolute right-1.5 top-1.5 z-20 flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-background hover:text-foreground focus-visible:bg-background focus-visible:text-foreground ${focusRing}`,
|
|
313
|
+
children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-3.5 w-3.5" })
|
|
314
|
+
})
|
|
315
|
+
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
316
|
+
side: "right",
|
|
317
|
+
align: "start",
|
|
318
|
+
className: "min-w-[160px] rounded-md",
|
|
319
|
+
children: [
|
|
320
|
+
manageHref ? /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
321
|
+
asChild: true,
|
|
322
|
+
children: /* @__PURE__ */ jsxs("a", {
|
|
323
|
+
href: manageHref,
|
|
324
|
+
target: "_blank",
|
|
325
|
+
rel: "noopener noreferrer",
|
|
326
|
+
"data-testid": `manage-item-${provider.providerId}`,
|
|
327
|
+
children: [/* @__PURE__ */ jsx(ExternalLink, { className: "mr-2 h-4 w-4" }), " Manage"]
|
|
328
|
+
})
|
|
329
|
+
}) : onManage ? /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
330
|
+
"data-testid": `manage-item-${provider.providerId}`,
|
|
331
|
+
onClick: () => onManage({
|
|
332
|
+
connectionId: live.id,
|
|
333
|
+
providerId: provider.providerId
|
|
334
|
+
}),
|
|
335
|
+
children: [/* @__PURE__ */ jsx(ExternalLink, { className: "mr-2 h-4 w-4" }), " Manage"]
|
|
336
|
+
}) : null,
|
|
337
|
+
canManage ? /* @__PURE__ */ jsx(DropdownMenuSeparator, {}) : null,
|
|
338
|
+
/* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
339
|
+
"data-testid": `disconnect-${provider.providerId}`,
|
|
340
|
+
className: "text-destructive focus:text-destructive",
|
|
341
|
+
onClick: () => setDisconnectTarget({
|
|
342
|
+
connectionId: live.id,
|
|
343
|
+
name,
|
|
344
|
+
accountDisplay: live.accountDisplay
|
|
345
|
+
}),
|
|
346
|
+
children: [/* @__PURE__ */ jsx(Unplug, { className: "mr-2 h-4 w-4" }), " Disconnect"]
|
|
347
|
+
})
|
|
348
|
+
]
|
|
349
|
+
})] })
|
|
350
|
+
]
|
|
351
|
+
}, `${provider.providerId}:${connectorId}`);
|
|
352
|
+
}
|
|
353
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
354
|
+
type: "button",
|
|
355
|
+
"data-testid": `integration-${provider.providerId}`,
|
|
356
|
+
"data-connected": "false",
|
|
357
|
+
"data-connecting": connectingId === provider.providerId ? "true" : void 0,
|
|
358
|
+
disabled: connectingId === provider.providerId,
|
|
359
|
+
"aria-busy": connectingId === provider.providerId,
|
|
360
|
+
onClick: () => {
|
|
361
|
+
startConnect({
|
|
362
|
+
providerId: provider.providerId,
|
|
363
|
+
connectorId
|
|
364
|
+
});
|
|
365
|
+
},
|
|
366
|
+
title: provider.description ? provider.description : `Connect ${name}`,
|
|
367
|
+
className: cn(TILE_BOX_CLASS, "group border-border bg-card transition-all", "hover:border-[var(--border-strong)] hover:bg-accent/40 hover:shadow-sm", focusRing),
|
|
368
|
+
children: [/* @__PURE__ */ jsx(ProviderIcon, {
|
|
369
|
+
id: provider.providerId,
|
|
370
|
+
iconUrl: provider.iconUrl,
|
|
371
|
+
displayName: displayNameOf(provider),
|
|
372
|
+
size: LOGO_SIZE,
|
|
373
|
+
className: "rounded-2xl"
|
|
374
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
375
|
+
className: "line-clamp-2 h-8 w-full text-xs font-medium leading-4 text-foreground",
|
|
376
|
+
children: name
|
|
377
|
+
})]
|
|
378
|
+
}, `${provider.providerId}:${connectorId}`);
|
|
379
|
+
})
|
|
380
|
+
}),
|
|
381
|
+
/* @__PURE__ */ jsx(Dialog, {
|
|
382
|
+
open: !!disconnectTarget,
|
|
383
|
+
onOpenChange: (open) => {
|
|
384
|
+
if (!open) closeDisconnect();
|
|
385
|
+
},
|
|
386
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
387
|
+
className: "max-w-sm",
|
|
388
|
+
onCloseAutoFocus: (e) => e.preventDefault(),
|
|
389
|
+
children: [
|
|
390
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsxs(DialogTitle, { children: [
|
|
391
|
+
"Disconnect ",
|
|
392
|
+
disconnectTarget?.name ?? "integration",
|
|
393
|
+
"?"
|
|
394
|
+
] }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
|
|
395
|
+
"This removes Sandbox's access to your",
|
|
396
|
+
" ",
|
|
397
|
+
disconnectTarget?.name ?? "this",
|
|
398
|
+
" account",
|
|
399
|
+
disconnectAccountLabel ? ` (${disconnectAccountLabel})` : "",
|
|
400
|
+
". You can reconnect anytime."
|
|
401
|
+
] })] }),
|
|
402
|
+
disconnectError ? /* @__PURE__ */ jsx("p", {
|
|
403
|
+
className: "text-sm text-destructive",
|
|
404
|
+
role: "alert",
|
|
405
|
+
children: disconnectError
|
|
406
|
+
}) : null,
|
|
407
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
|
|
408
|
+
variant: "outline",
|
|
409
|
+
onClick: closeDisconnect,
|
|
410
|
+
"data-testid": "cancel-disconnect",
|
|
411
|
+
children: "Cancel"
|
|
412
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
413
|
+
variant: "destructive",
|
|
414
|
+
loading: isDisconnecting,
|
|
415
|
+
onClick: confirmDisconnect,
|
|
416
|
+
"data-testid": "confirm-disconnect",
|
|
417
|
+
children: "Disconnect"
|
|
418
|
+
})] })
|
|
419
|
+
]
|
|
420
|
+
})
|
|
421
|
+
})
|
|
422
|
+
]
|
|
423
|
+
});
|
|
567
424
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
import * as React2 from "react";
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/integrations/use-integrations.ts
|
|
571
427
|
function unwrap(json) {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
return json;
|
|
428
|
+
if (json && typeof json === "object" && "data" in json && json.data !== void 0) return json.data;
|
|
429
|
+
return json;
|
|
576
430
|
}
|
|
577
|
-
function useIntegrations({
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const json = unwrap(await res.json());
|
|
655
|
-
if (!json?.authorizationUrl) {
|
|
656
|
-
throw new Error("Platform did not return an authorizationUrl");
|
|
657
|
-
}
|
|
658
|
-
window.location.href = json.authorizationUrl;
|
|
659
|
-
},
|
|
660
|
-
[base, fetcher]
|
|
661
|
-
);
|
|
662
|
-
const disconnect = React2.useCallback(
|
|
663
|
-
async (connectionId) => {
|
|
664
|
-
const res = await fetcher(
|
|
665
|
-
`${base}/connections/${encodeURIComponent(connectionId)}`,
|
|
666
|
-
{ method: "DELETE", credentials: "include" }
|
|
667
|
-
);
|
|
668
|
-
if (!res.ok) {
|
|
669
|
-
const text = await res.text().catch(() => "");
|
|
670
|
-
throw new Error(`Failed to revoke connection (${res.status}): ${text}`);
|
|
671
|
-
}
|
|
672
|
-
await refresh();
|
|
673
|
-
},
|
|
674
|
-
[base, fetcher, refresh]
|
|
675
|
-
);
|
|
676
|
-
return {
|
|
677
|
-
catalog,
|
|
678
|
-
connections,
|
|
679
|
-
healthByConnectionId,
|
|
680
|
-
isLoading,
|
|
681
|
-
error,
|
|
682
|
-
refresh,
|
|
683
|
-
connect,
|
|
684
|
-
disconnect
|
|
685
|
-
};
|
|
431
|
+
function useIntegrations({ apiBaseUrl, fetchImpl, autoLoad = true }) {
|
|
432
|
+
const fetcher = fetchImpl ?? (typeof fetch === "function" ? fetch : null);
|
|
433
|
+
if (!fetcher) throw new Error("useIntegrations: fetch is not available in this environment");
|
|
434
|
+
const base = apiBaseUrl.replace(/\/+$/, "");
|
|
435
|
+
const [catalog, setCatalog] = React$1.useState([]);
|
|
436
|
+
const [connections, setConnections] = React$1.useState([]);
|
|
437
|
+
const [healthByConnectionId, setHealthByConnectionId] = React$1.useState({});
|
|
438
|
+
const [isLoading, setIsLoading] = React$1.useState(autoLoad);
|
|
439
|
+
const [error, setError] = React$1.useState(null);
|
|
440
|
+
const refresh = React$1.useCallback(async () => {
|
|
441
|
+
setIsLoading(true);
|
|
442
|
+
setError(null);
|
|
443
|
+
try {
|
|
444
|
+
const [catalogRes, connectionsRes] = await Promise.all([fetcher(`${base}/catalog`, { credentials: "include" }), fetcher(`${base}/connections`, { credentials: "include" })]);
|
|
445
|
+
if (!catalogRes.ok) throw new Error(`Failed to load integration catalog (${catalogRes.status})`);
|
|
446
|
+
if (!connectionsRes.ok) throw new Error(`Failed to load integration connections (${connectionsRes.status})`);
|
|
447
|
+
const catalogJson = unwrap(await catalogRes.json());
|
|
448
|
+
const providers = catalogJson?.catalog?.providers ?? catalogJson?.providers ?? [];
|
|
449
|
+
setCatalog(providers);
|
|
450
|
+
const connectionsJson = unwrap(await connectionsRes.json());
|
|
451
|
+
setConnections(connectionsJson?.connections ?? []);
|
|
452
|
+
try {
|
|
453
|
+
const healthRes = await fetcher(`${base}/healthchecks`, { credentials: "include" });
|
|
454
|
+
if (healthRes.ok) {
|
|
455
|
+
const healthJson = unwrap(await healthRes.json());
|
|
456
|
+
const map = {};
|
|
457
|
+
for (const h of healthJson?.healthchecks ?? []) map[h.connectionId] = h;
|
|
458
|
+
setHealthByConnectionId(map);
|
|
459
|
+
}
|
|
460
|
+
} catch {}
|
|
461
|
+
} catch (err) {
|
|
462
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
463
|
+
} finally {
|
|
464
|
+
setIsLoading(false);
|
|
465
|
+
}
|
|
466
|
+
}, [base, fetcher]);
|
|
467
|
+
React$1.useEffect(() => {
|
|
468
|
+
if (autoLoad) refresh();
|
|
469
|
+
}, [autoLoad, refresh]);
|
|
470
|
+
return {
|
|
471
|
+
catalog,
|
|
472
|
+
connections,
|
|
473
|
+
healthByConnectionId,
|
|
474
|
+
isLoading,
|
|
475
|
+
error,
|
|
476
|
+
refresh,
|
|
477
|
+
connect: React$1.useCallback(async (input) => {
|
|
478
|
+
const res = await fetcher(`${base}/auth/start`, {
|
|
479
|
+
method: "POST",
|
|
480
|
+
credentials: "include",
|
|
481
|
+
headers: { "content-type": "application/json" },
|
|
482
|
+
body: JSON.stringify(input)
|
|
483
|
+
});
|
|
484
|
+
if (!res.ok) {
|
|
485
|
+
const text = await res.text().catch(() => "");
|
|
486
|
+
throw new Error(`Failed to start OAuth (${res.status}): ${text}`);
|
|
487
|
+
}
|
|
488
|
+
const json = unwrap(await res.json());
|
|
489
|
+
if (!json?.authorizationUrl) throw new Error("Platform did not return an authorizationUrl");
|
|
490
|
+
window.location.href = json.authorizationUrl;
|
|
491
|
+
}, [base, fetcher]),
|
|
492
|
+
disconnect: React$1.useCallback(async (connectionId) => {
|
|
493
|
+
const res = await fetcher(`${base}/connections/${encodeURIComponent(connectionId)}`, {
|
|
494
|
+
method: "DELETE",
|
|
495
|
+
credentials: "include"
|
|
496
|
+
});
|
|
497
|
+
if (!res.ok) {
|
|
498
|
+
const text = await res.text().catch(() => "");
|
|
499
|
+
throw new Error(`Failed to revoke connection (${res.status}): ${text}`);
|
|
500
|
+
}
|
|
501
|
+
await refresh();
|
|
502
|
+
}, [
|
|
503
|
+
base,
|
|
504
|
+
fetcher,
|
|
505
|
+
refresh
|
|
506
|
+
])
|
|
507
|
+
};
|
|
686
508
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
ProviderIcon,
|
|
690
|
-
providerLogoCandidates,
|
|
691
|
-
useIntegrations
|
|
692
|
-
};
|
|
509
|
+
//#endregion
|
|
510
|
+
export { IntegrationsPanel, ProviderIcon, providerLogoCandidates, useIntegrations };
|