@workos-inc/widgets 1.16.0-next.1785954585478 → 1.16.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/CHANGELOG.md +7 -1
- package/dist/cjs/lib/elements.cjs +2 -2
- package/dist/cjs/lib/elements.cjs.map +1 -1
- package/dist/cjs/lib/pipes-admin.cjs +7 -8
- package/dist/cjs/lib/pipes-admin.cjs.map +1 -1
- package/dist/cjs/lib/pipes.cjs +462 -133
- package/dist/cjs/lib/pipes.cjs.map +1 -1
- package/dist/cjs/lib/redacted-secret.cjs +33 -0
- package/dist/cjs/lib/redacted-secret.cjs.map +1 -0
- package/dist/cjs/lib/redacted-secret.d.cts +6 -0
- package/dist/esm/lib/elements.js +2 -2
- package/dist/esm/lib/elements.js.map +1 -1
- package/dist/esm/lib/pipes-admin.js +2 -3
- package/dist/esm/lib/pipes-admin.js.map +1 -1
- package/dist/esm/lib/pipes.js +465 -135
- package/dist/esm/lib/pipes.js.map +1 -1
- package/dist/esm/lib/redacted-secret.d.ts +6 -0
- package/dist/esm/lib/redacted-secret.js +8 -0
- package/dist/esm/lib/redacted-secret.js.map +1 -0
- package/package.json +1 -1
package/dist/cjs/lib/pipes.cjs
CHANGED
|
@@ -51,8 +51,14 @@ var import_utils = require("./utils.js");
|
|
|
51
51
|
var import_translation = require("./i18n/translation.js");
|
|
52
52
|
var import_use_translation = require("./i18n/use-translation.js");
|
|
53
53
|
var import_errors = require("../api/errors.js");
|
|
54
|
+
var import_redacted_secret = require("./redacted-secret.js");
|
|
54
55
|
const HANDOFF_INTERVAL_MS = 100;
|
|
55
56
|
const HANDOFF_MAX_ATTEMPTS = 100;
|
|
57
|
+
const CONNECT_AUTH_METHODS = [
|
|
58
|
+
"oauth",
|
|
59
|
+
"client_credentials",
|
|
60
|
+
"api_key"
|
|
61
|
+
];
|
|
56
62
|
const useOpenDataIntegrationAuthorizeUrl = (integration) => {
|
|
57
63
|
const authorizeDataIntegration = (0, import_endpoint.useAuthorizeDataIntegrationHook)();
|
|
58
64
|
const settings = (0, import_endpoint.useSettings)();
|
|
@@ -141,6 +147,140 @@ function getUnpinnedInstallationFields(integration) {
|
|
|
141
147
|
(field) => field.scope === "installation" && !integration.config?.[field.key]
|
|
142
148
|
).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
143
149
|
}
|
|
150
|
+
function getInitialConfigValues(fields, storedConfig) {
|
|
151
|
+
const values = {};
|
|
152
|
+
for (const field of fields) {
|
|
153
|
+
const stored = storedConfig[field.key];
|
|
154
|
+
if (typeof stored === "string" || typeof stored === "number" || typeof stored === "boolean") {
|
|
155
|
+
values[field.key] = String(stored);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return values;
|
|
159
|
+
}
|
|
160
|
+
function useConfigFieldsForm(fields, initialValues = {}) {
|
|
161
|
+
const [values, setValues] = (0, import_react.useState)(initialValues);
|
|
162
|
+
const [errors, setErrors] = (0, import_react.useState)({});
|
|
163
|
+
const translate = (0, import_use_translation.useTranslation)();
|
|
164
|
+
const requiredMessage = translate({
|
|
165
|
+
defaultMessage: "This field is required",
|
|
166
|
+
id: "B2HvfC",
|
|
167
|
+
description: "Validation error shown when a required configuration field is left empty"
|
|
168
|
+
});
|
|
169
|
+
const patternMessage = translate({
|
|
170
|
+
defaultMessage: "Enter a valid value.",
|
|
171
|
+
id: "2XWaGz",
|
|
172
|
+
description: "Validation error shown when a configuration field value does not match the expected format"
|
|
173
|
+
});
|
|
174
|
+
const setValue = (key, value) => {
|
|
175
|
+
setValues((prev) => ({ ...prev, [key]: value }));
|
|
176
|
+
setErrors((prev) => prev[key] ? { ...prev, [key]: null } : prev);
|
|
177
|
+
};
|
|
178
|
+
const reset = () => {
|
|
179
|
+
setValues(initialValues);
|
|
180
|
+
setErrors({});
|
|
181
|
+
};
|
|
182
|
+
const validate = () => {
|
|
183
|
+
const nextErrors = {};
|
|
184
|
+
let valid = true;
|
|
185
|
+
for (const field of fields) {
|
|
186
|
+
const value = (values[field.key] ?? "").trim();
|
|
187
|
+
if (field.required && !value) {
|
|
188
|
+
nextErrors[field.key] = requiredMessage;
|
|
189
|
+
valid = false;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (value && field.pattern) {
|
|
193
|
+
let matches = true;
|
|
194
|
+
try {
|
|
195
|
+
matches = new RegExp(field.pattern).test(value);
|
|
196
|
+
} catch {
|
|
197
|
+
matches = true;
|
|
198
|
+
}
|
|
199
|
+
if (!matches) {
|
|
200
|
+
nextErrors[field.key] = patternMessage;
|
|
201
|
+
valid = false;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
nextErrors[field.key] = null;
|
|
206
|
+
}
|
|
207
|
+
setErrors(nextErrors);
|
|
208
|
+
return valid;
|
|
209
|
+
};
|
|
210
|
+
const buildConfig = () => {
|
|
211
|
+
const config = {};
|
|
212
|
+
for (const field of fields) {
|
|
213
|
+
const value = (values[field.key] ?? "").trim();
|
|
214
|
+
if (value) {
|
|
215
|
+
config[field.key] = value;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return config;
|
|
219
|
+
};
|
|
220
|
+
const buildConfigPayload = (storedConfig) => {
|
|
221
|
+
const payload = { ...storedConfig };
|
|
222
|
+
for (const field of fields) {
|
|
223
|
+
const value = (values[field.key] ?? "").trim();
|
|
224
|
+
if (value) {
|
|
225
|
+
payload[field.key] = value;
|
|
226
|
+
} else {
|
|
227
|
+
delete payload[field.key];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return payload;
|
|
231
|
+
};
|
|
232
|
+
const hasMissingRequired = fields.some(
|
|
233
|
+
(field) => field.required && !(values[field.key] ?? "").trim()
|
|
234
|
+
);
|
|
235
|
+
const isDirty = fields.some(
|
|
236
|
+
(field) => (values[field.key] ?? "") !== (initialValues[field.key] ?? "")
|
|
237
|
+
);
|
|
238
|
+
return {
|
|
239
|
+
values,
|
|
240
|
+
errors,
|
|
241
|
+
setValue,
|
|
242
|
+
reset,
|
|
243
|
+
validate,
|
|
244
|
+
buildConfig,
|
|
245
|
+
buildConfigPayload,
|
|
246
|
+
hasMissingRequired,
|
|
247
|
+
isDirty
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function ConfigFieldInputs({
|
|
251
|
+
idPrefix,
|
|
252
|
+
fields,
|
|
253
|
+
values,
|
|
254
|
+
errors,
|
|
255
|
+
onChange
|
|
256
|
+
}) {
|
|
257
|
+
return fields.map((field) => {
|
|
258
|
+
const fieldId = `${idPrefix}-${field.key}`;
|
|
259
|
+
const errorId = `${fieldId}-error`;
|
|
260
|
+
const descriptionId = `${fieldId}-description`;
|
|
261
|
+
const error = errors[field.key];
|
|
262
|
+
const Control = field.secret ? import_elements.PasswordField : import_elements.TextField;
|
|
263
|
+
const describedBy = [field.description ? descriptionId : null, error ? errorId : null].filter(Boolean).join(" ") || void 0;
|
|
264
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { direction: "column", gap: "1", children: [
|
|
265
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Label, { htmlFor: fieldId, children: field.label }),
|
|
266
|
+
field.description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Text, { color: "gray", size: "2", id: descriptionId, mb: "1", children: field.description }) : null,
|
|
267
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
268
|
+
Control,
|
|
269
|
+
{
|
|
270
|
+
id: fieldId,
|
|
271
|
+
value: values[field.key] ?? "",
|
|
272
|
+
placeholder: field.example,
|
|
273
|
+
autoComplete: "off",
|
|
274
|
+
required: field.required || void 0,
|
|
275
|
+
"aria-invalid": !!error || void 0,
|
|
276
|
+
"aria-describedby": describedBy,
|
|
277
|
+
onChange: (event) => onChange(field.key, event.target.value)
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Text, { color: "red", size: "2", id: errorId, children: error }) : null
|
|
281
|
+
] }, field.key);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
144
284
|
function LogoSeparator() {
|
|
145
285
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
146
286
|
import_react_icons.DotsHorizontalIcon,
|
|
@@ -253,22 +393,11 @@ function ConnectDialog({
|
|
|
253
393
|
}) {
|
|
254
394
|
const initialStep = fields.length > 0 ? "fields" : "review";
|
|
255
395
|
const [step, setStep] = (0, import_react.useState)(initialStep);
|
|
256
|
-
const [values, setValues] = (0, import_react.useState)({});
|
|
257
|
-
const [errors, setErrors] = (0, import_react.useState)({});
|
|
258
396
|
const [submitError, setSubmitError] = (0, import_react.useState)(null);
|
|
259
397
|
const [isSubmitting, setIsSubmitting] = (0, import_react.useState)(false);
|
|
398
|
+
const configForm = useConfigFieldsForm(fields);
|
|
260
399
|
const eventHandler = useOpenDataIntegrationAuthorizeUrl(integration);
|
|
261
400
|
const translate = (0, import_use_translation.useTranslation)();
|
|
262
|
-
const requiredMessage = translate({
|
|
263
|
-
defaultMessage: "This field is required",
|
|
264
|
-
id: "B2HvfC",
|
|
265
|
-
description: "Validation error shown when a required configuration field is left empty"
|
|
266
|
-
});
|
|
267
|
-
const patternMessage = translate({
|
|
268
|
-
defaultMessage: "Enter a valid value.",
|
|
269
|
-
id: "2XWaGz",
|
|
270
|
-
description: "Validation error shown when a configuration field value does not match the expected format"
|
|
271
|
-
});
|
|
272
401
|
const genericConnectError = translate({
|
|
273
402
|
defaultMessage: "We couldn't start the connection. Please try again.",
|
|
274
403
|
id: "zGw1ma",
|
|
@@ -276,57 +405,15 @@ function ConnectDialog({
|
|
|
276
405
|
});
|
|
277
406
|
const reset = () => {
|
|
278
407
|
setStep(initialStep);
|
|
279
|
-
|
|
280
|
-
setErrors({});
|
|
408
|
+
configForm.reset();
|
|
281
409
|
setSubmitError(null);
|
|
282
410
|
setIsSubmitting(false);
|
|
283
411
|
};
|
|
284
|
-
const validateFields = () => {
|
|
285
|
-
const nextErrors = {};
|
|
286
|
-
let valid = true;
|
|
287
|
-
for (const field of fields) {
|
|
288
|
-
const value = (values[field.key] ?? "").trim();
|
|
289
|
-
if (field.required && !value) {
|
|
290
|
-
nextErrors[field.key] = requiredMessage;
|
|
291
|
-
valid = false;
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
if (value && field.pattern) {
|
|
295
|
-
let matches = true;
|
|
296
|
-
try {
|
|
297
|
-
matches = new RegExp(field.pattern).test(value);
|
|
298
|
-
} catch {
|
|
299
|
-
matches = true;
|
|
300
|
-
}
|
|
301
|
-
if (!matches) {
|
|
302
|
-
nextErrors[field.key] = patternMessage;
|
|
303
|
-
valid = false;
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
nextErrors[field.key] = null;
|
|
308
|
-
}
|
|
309
|
-
setErrors(nextErrors);
|
|
310
|
-
return valid;
|
|
311
|
-
};
|
|
312
|
-
const buildConfig = () => {
|
|
313
|
-
const config = {};
|
|
314
|
-
for (const field of fields) {
|
|
315
|
-
const value = (values[field.key] ?? "").trim();
|
|
316
|
-
if (value) {
|
|
317
|
-
config[field.key] = value;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
return config;
|
|
321
|
-
};
|
|
322
|
-
const hasMissingRequired = fields.some(
|
|
323
|
-
(field) => field.required && !(values[field.key] ?? "").trim()
|
|
324
|
-
);
|
|
325
412
|
const handleConnect = async () => {
|
|
326
413
|
setSubmitError(null);
|
|
327
414
|
setIsSubmitting(true);
|
|
328
415
|
try {
|
|
329
|
-
await eventHandler(buildConfig());
|
|
416
|
+
await eventHandler(configForm.buildConfig());
|
|
330
417
|
onOpenChange(false);
|
|
331
418
|
} catch (error) {
|
|
332
419
|
setSubmitError(
|
|
@@ -349,46 +436,16 @@ function ConnectDialog({
|
|
|
349
436
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_elements.Dialog.Content, { maxWidth: "430px", children: [
|
|
350
437
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectDialogHeader, { integration }),
|
|
351
438
|
step === "fields" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
352
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Flex, { direction: "column", gap: "4", children:
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { direction: "column", gap: "1", children: [
|
|
363
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Label, { htmlFor: fieldId, children: field.label }),
|
|
364
|
-
field.description ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Text, { color: "gray", size: "2", id: descriptionId, mb: "1", children: field.description }) : null,
|
|
365
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
366
|
-
Control,
|
|
367
|
-
{
|
|
368
|
-
id: fieldId,
|
|
369
|
-
value: values[field.key] ?? "",
|
|
370
|
-
placeholder: field.example,
|
|
371
|
-
autoComplete: "off",
|
|
372
|
-
required: field.required || void 0,
|
|
373
|
-
"aria-invalid": !!error || void 0,
|
|
374
|
-
"aria-describedby": describedBy,
|
|
375
|
-
onChange: (event) => {
|
|
376
|
-
setValues((prev) => ({
|
|
377
|
-
...prev,
|
|
378
|
-
[field.key]: event.target.value
|
|
379
|
-
}));
|
|
380
|
-
if (error) {
|
|
381
|
-
setErrors((prev) => ({
|
|
382
|
-
...prev,
|
|
383
|
-
[field.key]: null
|
|
384
|
-
}));
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
),
|
|
389
|
-
error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Text, { color: "red", size: "2", id: errorId, children: error }) : null
|
|
390
|
-
] }, field.key);
|
|
391
|
-
}) }) }),
|
|
439
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Flex, { direction: "column", gap: "4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
440
|
+
ConfigFieldInputs,
|
|
441
|
+
{
|
|
442
|
+
idPrefix: "pipes-config",
|
|
443
|
+
fields,
|
|
444
|
+
values: configForm.values,
|
|
445
|
+
errors: configForm.errors,
|
|
446
|
+
onChange: configForm.setValue
|
|
447
|
+
}
|
|
448
|
+
) }) }),
|
|
392
449
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { justify: "end", gap: "3", mt: "5", children: [
|
|
393
450
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Dialog.Close, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Button, { variant: "secondary", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
394
451
|
import_translation.Translation,
|
|
@@ -402,9 +459,9 @@ function ConnectDialog({
|
|
|
402
459
|
import_elements.Button,
|
|
403
460
|
{
|
|
404
461
|
type: "button",
|
|
405
|
-
disabled: hasMissingRequired,
|
|
462
|
+
disabled: configForm.hasMissingRequired,
|
|
406
463
|
onClick: () => {
|
|
407
|
-
if (
|
|
464
|
+
if (configForm.validate()) {
|
|
408
465
|
setStep("review");
|
|
409
466
|
}
|
|
410
467
|
},
|
|
@@ -668,11 +725,247 @@ function ApiKeyConnectButton({
|
|
|
668
725
|
) })
|
|
669
726
|
] });
|
|
670
727
|
}
|
|
671
|
-
function
|
|
728
|
+
function ClientCredentialsDialog({
|
|
729
|
+
integration,
|
|
730
|
+
fields,
|
|
731
|
+
open,
|
|
732
|
+
onOpenChange
|
|
733
|
+
}) {
|
|
734
|
+
const queryClient = (0, import_react_query.useQueryClient)();
|
|
735
|
+
const installation = integration.installation;
|
|
736
|
+
const isUpdate = installation?.auth_method === "client_credentials";
|
|
737
|
+
const storedConfig = (0, import_react.useMemo)(
|
|
738
|
+
() => isUpdate ? installation?.config ?? {} : {},
|
|
739
|
+
[isUpdate, installation?.config]
|
|
740
|
+
);
|
|
741
|
+
const initialConfigValues = (0, import_react.useMemo)(
|
|
742
|
+
() => getInitialConfigValues(fields, storedConfig),
|
|
743
|
+
[fields, storedConfig]
|
|
744
|
+
);
|
|
745
|
+
const redactedSecret = (0, import_redacted_secret.buildRedactedSecret)(
|
|
746
|
+
isUpdate ? installation?.client_secret_last_4 : null
|
|
747
|
+
);
|
|
748
|
+
const [clientId, setClientId] = (0, import_react.useState)(
|
|
749
|
+
(isUpdate ? installation?.client_id : null) ?? ""
|
|
750
|
+
);
|
|
751
|
+
const [clientSecret, setClientSecret] = (0, import_react.useState)(redactedSecret);
|
|
752
|
+
const configForm = useConfigFieldsForm(fields, initialConfigValues);
|
|
753
|
+
const translate = (0, import_use_translation.useTranslation)();
|
|
754
|
+
const clientIdPlaceholder = translate({
|
|
755
|
+
defaultMessage: "Enter your client ID",
|
|
756
|
+
id: "5YTAFo",
|
|
757
|
+
description: "Placeholder for the client credentials client ID input"
|
|
758
|
+
});
|
|
759
|
+
const clientSecretPlaceholder = translate({
|
|
760
|
+
defaultMessage: "Enter your client secret",
|
|
761
|
+
id: "53f0B7",
|
|
762
|
+
description: "Placeholder for the client credentials client secret input"
|
|
763
|
+
});
|
|
764
|
+
const secretMissing = !clientSecret.trim() || (0, import_redacted_secret.isRedactedSecret)(clientSecret);
|
|
765
|
+
const {
|
|
766
|
+
mutate: upsertClientCredentials,
|
|
767
|
+
error,
|
|
768
|
+
isPending
|
|
769
|
+
} = (0, import_endpoint.useUpsertDataIntegrationClientCredentials)({
|
|
770
|
+
mutation: {
|
|
771
|
+
onSuccess: () => {
|
|
772
|
+
onOpenChange(false);
|
|
773
|
+
queryClient.invalidateQueries({
|
|
774
|
+
queryKey: (0, import_endpoint.getMyDataIntegrationsQueryKey)()
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
const errorMessage = error instanceof import_errors.ApiError && error.message ? error.message : null;
|
|
780
|
+
const handleSubmit = () => {
|
|
781
|
+
if (!configForm.validate()) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const config = configForm.isDirty ? configForm.buildConfigPayload(storedConfig) : void 0;
|
|
785
|
+
upsertClientCredentials({
|
|
786
|
+
slug: integration.slug,
|
|
787
|
+
data: {
|
|
788
|
+
client_id: clientId.trim(),
|
|
789
|
+
client_secret: clientSecret.trim(),
|
|
790
|
+
...config ? { config } : {}
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
};
|
|
794
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_elements.Dialog.Content, { maxWidth: "430px", children: [
|
|
795
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Dialog.Title, { size: "3", weight: "bold", children: isUpdate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
796
|
+
import_translation.Translation,
|
|
797
|
+
{
|
|
798
|
+
defaultMessage: "Update client credentials for {integrationName}",
|
|
799
|
+
id: "/D85zF",
|
|
800
|
+
description: "Dialog title for updating stored client credentials",
|
|
801
|
+
values: { integrationName: integration.name }
|
|
802
|
+
}
|
|
803
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
804
|
+
import_translation.Translation,
|
|
805
|
+
{
|
|
806
|
+
defaultMessage: "Connect {integrationName} with client credentials",
|
|
807
|
+
id: "x+7tw3",
|
|
808
|
+
description: "Dialog title for connecting with client credentials",
|
|
809
|
+
values: { integrationName: integration.name }
|
|
810
|
+
}
|
|
811
|
+
) }),
|
|
812
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Callout.Root, { my: "3", color: "red", role: "alert", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_themes.Callout.Text, { children: errorMessage ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
813
|
+
import_translation.Translation,
|
|
814
|
+
{
|
|
815
|
+
defaultMessage: "An error occurred. Please check the value and try again.",
|
|
816
|
+
id: "bYLbLL",
|
|
817
|
+
description: "Generic error message for API key submission failure"
|
|
818
|
+
}
|
|
819
|
+
) }) }),
|
|
820
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { direction: "column", gap: "4", mt: "2", children: [
|
|
821
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { direction: "column", gap: "1", children: [
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Label, { htmlFor: "pipes-client-credentials-client-id", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
823
|
+
import_translation.Translation,
|
|
824
|
+
{
|
|
825
|
+
defaultMessage: "Client ID",
|
|
826
|
+
id: "/Cv5P1",
|
|
827
|
+
description: "Label for the client credentials client ID input"
|
|
828
|
+
}
|
|
829
|
+
) }),
|
|
830
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
831
|
+
import_elements.TextField,
|
|
832
|
+
{
|
|
833
|
+
id: "pipes-client-credentials-client-id",
|
|
834
|
+
value: clientId,
|
|
835
|
+
autoComplete: "off",
|
|
836
|
+
placeholder: clientIdPlaceholder,
|
|
837
|
+
onChange: (event) => setClientId(event.target.value)
|
|
838
|
+
}
|
|
839
|
+
)
|
|
840
|
+
] }),
|
|
841
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { direction: "column", gap: "1", children: [
|
|
842
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Label, { htmlFor: "pipes-client-credentials-client-secret", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
843
|
+
import_translation.Translation,
|
|
844
|
+
{
|
|
845
|
+
defaultMessage: "Client secret",
|
|
846
|
+
id: "uScBDj",
|
|
847
|
+
description: "Label for the client credentials client secret input"
|
|
848
|
+
}
|
|
849
|
+
) }),
|
|
850
|
+
isUpdate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
851
|
+
import_themes.Text,
|
|
852
|
+
{
|
|
853
|
+
color: "gray",
|
|
854
|
+
size: "2",
|
|
855
|
+
id: "pipes-client-credentials-client-secret-description",
|
|
856
|
+
mb: "1",
|
|
857
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
858
|
+
import_translation.Translation,
|
|
859
|
+
{
|
|
860
|
+
defaultMessage: "Enter the client secret again to save any changes.",
|
|
861
|
+
id: "ZrNPCF",
|
|
862
|
+
description: "Help text explaining the client secret must be re-entered to save an update"
|
|
863
|
+
}
|
|
864
|
+
)
|
|
865
|
+
}
|
|
866
|
+
),
|
|
867
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
868
|
+
import_elements.PasswordField,
|
|
869
|
+
{
|
|
870
|
+
id: "pipes-client-credentials-client-secret",
|
|
871
|
+
value: clientSecret,
|
|
872
|
+
autoComplete: "off",
|
|
873
|
+
type: (0, import_redacted_secret.isRedactedSecret)(clientSecret) ? "text" : void 0,
|
|
874
|
+
placeholder: clientSecretPlaceholder,
|
|
875
|
+
"aria-describedby": isUpdate ? "pipes-client-credentials-client-secret-description" : void 0,
|
|
876
|
+
onChange: (event) => setClientSecret(event.target.value),
|
|
877
|
+
onFocus: () => {
|
|
878
|
+
if ((0, import_redacted_secret.isRedactedSecret)(clientSecret)) {
|
|
879
|
+
setClientSecret("");
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
onBlur: () => {
|
|
883
|
+
if (clientSecret === "") {
|
|
884
|
+
setClientSecret(redactedSecret);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
)
|
|
889
|
+
] }),
|
|
890
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
891
|
+
ConfigFieldInputs,
|
|
892
|
+
{
|
|
893
|
+
idPrefix: "pipes-client-credentials-config",
|
|
894
|
+
fields,
|
|
895
|
+
values: configForm.values,
|
|
896
|
+
errors: configForm.errors,
|
|
897
|
+
onChange: configForm.setValue
|
|
898
|
+
}
|
|
899
|
+
)
|
|
900
|
+
] }),
|
|
901
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { justify: "end", gap: "3", mt: "5", children: [
|
|
902
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Dialog.Close, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Button, { variant: "secondary", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
903
|
+
import_translation.Translation,
|
|
904
|
+
{
|
|
905
|
+
defaultMessage: "Cancel",
|
|
906
|
+
id: "hHNj31",
|
|
907
|
+
description: "Cancel button text"
|
|
908
|
+
}
|
|
909
|
+
) }) }),
|
|
910
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
911
|
+
import_elements.Button,
|
|
912
|
+
{
|
|
913
|
+
type: "button",
|
|
914
|
+
disabled: isPending || !clientId.trim() || secretMissing || configForm.hasMissingRequired,
|
|
915
|
+
loading: isPending,
|
|
916
|
+
onClick: handleSubmit,
|
|
917
|
+
children: isUpdate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
918
|
+
import_translation.Translation,
|
|
919
|
+
{
|
|
920
|
+
defaultMessage: "Save",
|
|
921
|
+
id: "RT8KNi",
|
|
922
|
+
description: "Save button text"
|
|
923
|
+
}
|
|
924
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
925
|
+
import_translation.Translation,
|
|
926
|
+
{
|
|
927
|
+
defaultMessage: "Connect",
|
|
928
|
+
id: "quvdFp",
|
|
929
|
+
description: "Button to connect an integration"
|
|
930
|
+
}
|
|
931
|
+
)
|
|
932
|
+
}
|
|
933
|
+
)
|
|
934
|
+
] })
|
|
935
|
+
] }) });
|
|
936
|
+
}
|
|
937
|
+
function ClientCredentialsConnectButton({
|
|
672
938
|
integration
|
|
939
|
+
}) {
|
|
940
|
+
const [open, setOpen] = (0, import_react.useState)(false);
|
|
941
|
+
const fields = getUnpinnedInstallationFields(integration);
|
|
942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
943
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
944
|
+
ClientCredentialsDialog,
|
|
945
|
+
{
|
|
946
|
+
integration,
|
|
947
|
+
fields,
|
|
948
|
+
open,
|
|
949
|
+
onOpenChange: setOpen
|
|
950
|
+
}
|
|
951
|
+
),
|
|
952
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.Button, { variant: "secondary", onClick: () => setOpen(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
953
|
+
import_translation.Translation,
|
|
954
|
+
{
|
|
955
|
+
defaultMessage: "Connect",
|
|
956
|
+
id: "quvdFp",
|
|
957
|
+
description: "Button to connect an integration"
|
|
958
|
+
}
|
|
959
|
+
) })
|
|
960
|
+
] });
|
|
961
|
+
}
|
|
962
|
+
function MultiMethodConnectButton({
|
|
963
|
+
integration,
|
|
964
|
+
methods
|
|
673
965
|
}) {
|
|
674
966
|
const [apiKeyOpen, setApiKeyOpen] = (0, import_react.useState)(false);
|
|
675
967
|
const [oauthOpen, setOauthOpen] = (0, import_react.useState)(false);
|
|
968
|
+
const [clientCredentialsOpen, setClientCredentialsOpen] = (0, import_react.useState)(false);
|
|
676
969
|
const fields = getUnpinnedInstallationFields(integration);
|
|
677
970
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
678
971
|
apiKeyOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -683,7 +976,16 @@ function DualMethodConnectButton({
|
|
|
683
976
|
onOpenChange: setApiKeyOpen
|
|
684
977
|
}
|
|
685
978
|
),
|
|
686
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
979
|
+
clientCredentialsOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
980
|
+
ClientCredentialsDialog,
|
|
981
|
+
{
|
|
982
|
+
integration,
|
|
983
|
+
fields,
|
|
984
|
+
open: clientCredentialsOpen,
|
|
985
|
+
onOpenChange: setClientCredentialsOpen
|
|
986
|
+
}
|
|
987
|
+
),
|
|
988
|
+
methods.includes("oauth") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
687
989
|
ConnectDialog,
|
|
688
990
|
{
|
|
689
991
|
integration,
|
|
@@ -702,7 +1004,7 @@ function DualMethodConnectButton({
|
|
|
702
1004
|
}
|
|
703
1005
|
) }) }),
|
|
704
1006
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_elements.DropdownMenu.Content, { align: "end", children: [
|
|
705
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Item, { onClick: () => setOauthOpen(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { gap: "4", width: "100%", justify: "between", align: "center", children: [
|
|
1007
|
+
methods.includes("oauth") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Item, { onClick: () => setOauthOpen(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { gap: "4", width: "100%", justify: "between", align: "center", children: [
|
|
706
1008
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
707
1009
|
import_translation.Translation,
|
|
708
1010
|
{
|
|
@@ -714,7 +1016,15 @@ function DualMethodConnectButton({
|
|
|
714
1016
|
),
|
|
715
1017
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_icons.ExternalLinkIcon, { "aria-hidden": true })
|
|
716
1018
|
] }) }),
|
|
717
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Item, { onClick: () =>
|
|
1019
|
+
methods.includes("client_credentials") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Item, { onClick: () => setClientCredentialsOpen(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1020
|
+
import_translation.Translation,
|
|
1021
|
+
{
|
|
1022
|
+
defaultMessage: "Enter client credentials",
|
|
1023
|
+
id: "Y/87Vt",
|
|
1024
|
+
description: "Menu option to connect via client credentials"
|
|
1025
|
+
}
|
|
1026
|
+
) }),
|
|
1027
|
+
methods.includes("api_key") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Item, { onClick: () => setApiKeyOpen(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
718
1028
|
import_translation.Translation,
|
|
719
1029
|
{
|
|
720
1030
|
defaultMessage: "Enter API key",
|
|
@@ -729,6 +1039,7 @@ function DualMethodConnectButton({
|
|
|
729
1039
|
function ProviderStatus({ integration }) {
|
|
730
1040
|
const [disconnectOpen, setDisconnectOpen] = (0, import_react.useState)(false);
|
|
731
1041
|
const [apiKeyOpen, setApiKeyOpen] = (0, import_react.useState)(false);
|
|
1042
|
+
const [clientCredentialsOpen, setClientCredentialsOpen] = (0, import_react.useState)(false);
|
|
732
1043
|
const [reauthConfigOpen, setReauthConfigOpen] = (0, import_react.useState)(false);
|
|
733
1044
|
const authorizeEventHandler = useOpenDataIntegrationAuthorizeUrl(integration);
|
|
734
1045
|
const translate = (0, import_use_translation.useTranslation)();
|
|
@@ -741,6 +1052,7 @@ function ProviderStatus({ integration }) {
|
|
|
741
1052
|
if (integration.installation) {
|
|
742
1053
|
const connected = integration.installation.state === "connected";
|
|
743
1054
|
const isApiKeyInstallation = integration.installation.auth_method === "api_key";
|
|
1055
|
+
const isClientCredentialsInstallation = integration.installation.auth_method === "client_credentials";
|
|
744
1056
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
745
1057
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
746
1058
|
DisconnectAccountDialog,
|
|
@@ -759,7 +1071,16 @@ function ProviderStatus({ integration }) {
|
|
|
759
1071
|
onOpenChange: setApiKeyOpen
|
|
760
1072
|
}
|
|
761
1073
|
),
|
|
762
|
-
|
|
1074
|
+
clientCredentialsOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1075
|
+
ClientCredentialsDialog,
|
|
1076
|
+
{
|
|
1077
|
+
integration,
|
|
1078
|
+
fields: unpinnedFields,
|
|
1079
|
+
open: clientCredentialsOpen,
|
|
1080
|
+
onOpenChange: setClientCredentialsOpen
|
|
1081
|
+
}
|
|
1082
|
+
),
|
|
1083
|
+
unpinnedFields.length > 0 && !isClientCredentialsInstallation && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
763
1084
|
ConnectDialog,
|
|
764
1085
|
{
|
|
765
1086
|
integration,
|
|
@@ -784,7 +1105,40 @@ function ProviderStatus({ integration }) {
|
|
|
784
1105
|
description: "Status text for integration needing reauthorization"
|
|
785
1106
|
}
|
|
786
1107
|
) }),
|
|
787
|
-
|
|
1108
|
+
isClientCredentialsInstallation ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_elements.DropdownMenu.Root, { children: [
|
|
1109
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.IconButton, { title: pipeActionsTitle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_icons.DotsHorizontalIcon, {}) }) }),
|
|
1110
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_elements.DropdownMenu.Content, { align: "end", children: [
|
|
1111
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1112
|
+
import_elements.DropdownMenu.Item,
|
|
1113
|
+
{
|
|
1114
|
+
onClick: () => setClientCredentialsOpen(true),
|
|
1115
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1116
|
+
import_translation.Translation,
|
|
1117
|
+
{
|
|
1118
|
+
defaultMessage: "Update client credentials",
|
|
1119
|
+
id: "daHKWE",
|
|
1120
|
+
description: "Menu option to update stored client credentials"
|
|
1121
|
+
}
|
|
1122
|
+
)
|
|
1123
|
+
}
|
|
1124
|
+
),
|
|
1125
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1126
|
+
import_elements.DropdownMenu.Item,
|
|
1127
|
+
{
|
|
1128
|
+
variant: "destructive",
|
|
1129
|
+
onClick: () => setDisconnectOpen(true),
|
|
1130
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1131
|
+
import_translation.Translation,
|
|
1132
|
+
{
|
|
1133
|
+
defaultMessage: "Disconnect account",
|
|
1134
|
+
id: "esZQrd",
|
|
1135
|
+
description: "Menu option to disconnect an account"
|
|
1136
|
+
}
|
|
1137
|
+
)
|
|
1138
|
+
}
|
|
1139
|
+
)
|
|
1140
|
+
] })
|
|
1141
|
+
] }) : connected && isApiKeyInstallation ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_elements.DropdownMenu.Root, { children: [
|
|
788
1142
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Trigger, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.IconButton, { title: pipeActionsTitle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_icons.DotsHorizontalIcon, {}) }) }),
|
|
789
1143
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_elements.DropdownMenu.Content, { align: "end", children: [
|
|
790
1144
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_elements.DropdownMenu.Item, { onClick: () => setApiKeyOpen(true), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -896,34 +1250,9 @@ function ProviderStatus({ integration }) {
|
|
|
896
1250
|
] });
|
|
897
1251
|
}
|
|
898
1252
|
const authMethods = integration.authMethods ?? [];
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { align: "center", gap: "4", children: [
|
|
903
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_status.Status, { state: "neutral", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
904
|
-
import_translation.Translation,
|
|
905
|
-
{
|
|
906
|
-
defaultMessage: "Not connected",
|
|
907
|
-
id: "J7bTZa",
|
|
908
|
-
description: "Status text for an integration that is not connected"
|
|
909
|
-
}
|
|
910
|
-
) }),
|
|
911
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DualMethodConnectButton, { integration })
|
|
912
|
-
] });
|
|
913
|
-
}
|
|
914
|
-
if (supportsApiKey) {
|
|
915
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { align: "center", gap: "4", children: [
|
|
916
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_status.Status, { state: "neutral", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
917
|
-
import_translation.Translation,
|
|
918
|
-
{
|
|
919
|
-
defaultMessage: "Not connected",
|
|
920
|
-
id: "J7bTZa",
|
|
921
|
-
description: "Status text for an integration that is not connected"
|
|
922
|
-
}
|
|
923
|
-
) }),
|
|
924
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiKeyConnectButton, { integration })
|
|
925
|
-
] });
|
|
926
|
-
}
|
|
1253
|
+
const methods = CONNECT_AUTH_METHODS.filter(
|
|
1254
|
+
(method) => authMethods.includes(method)
|
|
1255
|
+
);
|
|
927
1256
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_themes.Flex, { align: "center", gap: "4", children: [
|
|
928
1257
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_status.Status, { state: "neutral", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
929
1258
|
import_translation.Translation,
|
|
@@ -933,7 +1262,7 @@ function ProviderStatus({ integration }) {
|
|
|
933
1262
|
description: "Status text for an integration that is not connected"
|
|
934
1263
|
}
|
|
935
1264
|
) }),
|
|
936
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectIntegrationButton, { integration })
|
|
1265
|
+
methods.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MultiMethodConnectButton, { integration, methods }) : methods.length === 1 && methods[0] === "api_key" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiKeyConnectButton, { integration }) : methods.length === 1 && methods[0] === "client_credentials" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClientCredentialsConnectButton, { integration }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectIntegrationButton, { integration })
|
|
937
1266
|
] });
|
|
938
1267
|
}
|
|
939
1268
|
function DisconnectAccountDialog({
|