@elevasis/ui 2.74.0 → 2.75.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/api/index.js +3 -3
- package/dist/app/index.js +8 -8
- package/dist/auth/index.js +4 -4
- package/dist/charts/index.js +4 -4
- package/dist/{chunk-BA3AZJUR.js → chunk-2ZRY3ARL.js} +1 -1
- package/dist/{chunk-RFNJ2PTO.js → chunk-3H5DIJN7.js} +1 -1
- package/dist/{chunk-5A6KCFS5.js → chunk-47YJVIZE.js} +8 -8
- package/dist/{chunk-E6YGA5WH.js → chunk-5RH6NYZH.js} +1 -1
- package/dist/{chunk-3QHX4ZKV.js → chunk-5SLRGUC4.js} +1 -1
- package/dist/{chunk-7ARBBELS.js → chunk-5VF5LPYS.js} +1 -1
- package/dist/{chunk-4MXNHJTA.js → chunk-6BJ5HMMC.js} +242 -546
- package/dist/{chunk-ZWI62BQ6.js → chunk-7OOFMEX6.js} +10 -7
- package/dist/{chunk-R5UGIFYU.js → chunk-A2SB32YV.js} +25 -1
- package/dist/{chunk-GO4XJ5D4.js → chunk-CRPH5AAB.js} +1 -1
- package/dist/{chunk-43LC6XAX.js → chunk-D3XXPCCK.js} +1 -1
- package/dist/{chunk-BRTGCFBH.js → chunk-DMSTDZCJ.js} +1 -1
- package/dist/{chunk-GPQQGQGT.js → chunk-DTEGVSJ6.js} +8 -8
- package/dist/{chunk-GDOR57D7.js → chunk-DVAOCHIF.js} +3 -3
- package/dist/{chunk-3LOKYZJC.js → chunk-G7O4CBMC.js} +1 -1
- package/dist/{chunk-CQPULKKL.js → chunk-GDTK33QT.js} +4 -4
- package/dist/{chunk-HYQOGRWG.js → chunk-ICHDIVQO.js} +1 -1
- package/dist/{chunk-DCQKTDIG.js → chunk-IYATYH5I.js} +1 -1
- package/dist/{chunk-CRA7WQZH.js → chunk-L3NBMLRN.js} +1 -1
- package/dist/{chunk-LN4UW6S4.js → chunk-LFLM723H.js} +1 -1
- package/dist/{chunk-FZSG6M6W.js → chunk-M6VAZAQW.js} +3 -3
- package/dist/{chunk-7RJ7P6S6.js → chunk-MLHZUN2X.js} +5 -5
- package/dist/{chunk-FA7LMNQO.js → chunk-NMHQAYZU.js} +2 -2
- package/dist/{chunk-ZYNVCWDZ.js → chunk-NYIIRZSA.js} +3 -3
- package/dist/{chunk-2MRRDF32.js → chunk-OGHTJKBV.js} +9 -9
- package/dist/{chunk-ARAFQBNI.js → chunk-QFS2GZMV.js} +3 -3
- package/dist/{chunk-6JN2TLV2.js → chunk-SB3IHM5Y.js} +6 -6
- package/dist/{chunk-WZU3MCKI.js → chunk-SEYOVNS5.js} +1 -1
- package/dist/{chunk-4M2BSHVT.js → chunk-SN7X5N2A.js} +1 -1
- package/dist/{chunk-ZFA56VSR.js → chunk-TX3UHDL5.js} +2 -2
- package/dist/{chunk-W2QRBQQA.js → chunk-VWGCURZF.js} +3 -3
- package/dist/{chunk-O32UQ26A.js → chunk-ZG6YJUT4.js} +1 -1
- package/dist/components/index.d.ts +48 -11
- package/dist/components/index.js +315 -39
- package/dist/components/navigation/index.js +5 -5
- package/dist/features/auth/index.js +6 -6
- package/dist/features/clients/index.js +6 -6
- package/dist/features/content/index.js +13 -10
- package/dist/features/crm/index.js +8 -8
- package/dist/features/dashboard/index.js +6 -6
- package/dist/features/delivery/index.js +17 -17
- package/dist/features/knowledge/index.js +4 -4
- package/dist/features/lead-gen/index.js +10 -10
- package/dist/features/monitoring/index.js +8 -8
- package/dist/features/monitoring/requests/index.js +6 -6
- package/dist/features/operations/index.js +11 -11
- package/dist/features/settings/index.d.ts +8 -3
- package/dist/features/settings/index.js +8 -8
- package/dist/hooks/access/index.js +4 -4
- package/dist/hooks/delivery/index.js +3 -3
- package/dist/hooks/published.d.ts +6 -2
- package/dist/hooks/published.js +16 -16
- package/dist/knowledge/index.js +8 -8
- package/dist/layout/index.js +7 -7
- package/dist/provider/index.js +4 -4
- package/dist/provider/published.js +4 -4
- package/dist/test-utils/index.js +3 -3
- package/dist/types/index.d.ts +14 -0
- package/dist/utils/index.js +1 -1
- package/package.json +7 -3
|
@@ -1,58 +1,28 @@
|
|
|
1
1
|
import { useAvailablePresets } from './chunk-ZFISGKYW.js';
|
|
2
|
-
import { MEMBERSHIP_STATUS_COLORS, PROVISIONING_STATE_DISPLAY, transformMembershipToTableRow, useUserMemberships, useUpdateWebhookEndpoint, useDeleteWebhookEndpoint, useCreateWebhookEndpoint, useListWebhookEndpoints, useOrgRoles, useAssignRole, useRevokeRole, useOrganizationMembers, usePermissionCatalog } from './chunk-
|
|
3
|
-
import {
|
|
4
|
-
import { AppShellLoader } from './chunk-
|
|
2
|
+
import { MEMBERSHIP_STATUS_COLORS, PROVISIONING_STATE_DISPLAY, transformMembershipToTableRow, useUserMemberships, useUpdateWebhookEndpoint, useDeleteWebhookEndpoint, useCreateWebhookEndpoint, useListWebhookEndpoints, useOrgRoles, useAssignRole, useRevokeRole, useOrganizationMembers, usePermissionCatalog } from './chunk-QFS2GZMV.js';
|
|
3
|
+
import { useUpdateCredential, CredentialSchemas } from './chunk-G7O4CBMC.js';
|
|
4
|
+
import { AppShellLoader } from './chunk-5RH6NYZH.js';
|
|
5
5
|
import { FilterBar } from './chunk-PDHTXPSF.js';
|
|
6
|
-
import {
|
|
7
|
-
import { showApiErrorNotification, showErrorNotification, showSuccessNotification } from './chunk-
|
|
6
|
+
import { CustomModal, APIErrorAlert, EmptyState, PageTitleCaption, useResources, ListSkeleton, StatCard, useAccess, AccessKeys, AccessGuard } from './chunk-SB3IHM5Y.js';
|
|
7
|
+
import { showApiErrorNotification, showErrorNotification, showSuccessNotification } from './chunk-ICHDIVQO.js';
|
|
8
8
|
import { CardHeader } from './chunk-62GVNH5U.js';
|
|
9
9
|
import { useInitialization } from './chunk-P45GQ3ZW.js';
|
|
10
10
|
import { RoleBadge } from './chunk-UD657ICS.js';
|
|
11
11
|
import { useOrganization } from './chunk-DD3CCMCZ.js';
|
|
12
|
-
import { isAPIClientError, formatDateTime, OAUTH_FLOW_TIMEOUT } from './chunk-
|
|
12
|
+
import { isAPIClientError, formatDateTime, OAUTH_FLOW_TIMEOUT } from './chunk-A2SB32YV.js';
|
|
13
13
|
import { useElevasisServices } from './chunk-BI5VDO2K.js';
|
|
14
14
|
import { brandSupabaseOrgId } from './chunk-MQZE7SUI.js';
|
|
15
15
|
import { useAuthContext } from './chunk-BRJ3QZ4E.js';
|
|
16
|
-
import {
|
|
17
|
-
import { IconShieldOff,
|
|
18
|
-
import {
|
|
16
|
+
import { Text, Stack, Title, Alert, TextInput, Group, Button, Paper, SimpleGrid, Card, ThemeIcon, Badge, Tooltip, Select, Table, Center, Loader, Box, Code, CopyButton, ActionIcon, UnstyledButton, Switch, Divider, Textarea, MultiSelect, PasswordInput } from '@mantine/core';
|
|
17
|
+
import { IconShieldOff, IconAlertCircle, IconBrandDropbox, IconBrandGoogleDrive, IconPlug, IconUsers, IconSearch, IconBuilding, IconWebhook, IconCheck, IconCopy, IconUser, IconMail, IconPalette, IconEye, IconSparkles, IconTrendingUp, IconExclamationMark, IconPencil, IconClock, IconPlayerPause, IconPlayerPlay, IconTrash, IconAlertTriangle, IconPlus, IconActivity, IconShieldLock, IconStar, IconUserCog, IconUsersGroup, IconSettings, IconRefresh, IconEdit, IconUserX, IconUserCheck } from '@tabler/icons-react';
|
|
18
|
+
import { modals } from '@mantine/modals';
|
|
19
19
|
import { notifications } from '@mantine/notifications';
|
|
20
20
|
import { z } from 'zod';
|
|
21
21
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
22
22
|
import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
|
23
|
-
import { useForm } from '@mantine/form';
|
|
24
|
-
import { create } from 'zustand';
|
|
25
23
|
import { useQueryClient } from '@tanstack/react-query';
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
var openModal = (payload) => {
|
|
29
|
-
const id = payload.modalId || randomId();
|
|
30
|
-
createEvent("openModal")({ ...payload, modalId: id });
|
|
31
|
-
return id;
|
|
32
|
-
};
|
|
33
|
-
var openConfirmModal = (payload) => {
|
|
34
|
-
const id = payload.modalId || randomId();
|
|
35
|
-
createEvent("openConfirmModal")({ ...payload, modalId: id });
|
|
36
|
-
return id;
|
|
37
|
-
};
|
|
38
|
-
var openContextModal = (payload) => {
|
|
39
|
-
const id = payload.modalId || randomId();
|
|
40
|
-
createEvent("openContextModal")({ ...payload, modalId: id });
|
|
41
|
-
return id;
|
|
42
|
-
};
|
|
43
|
-
var closeModal = createEvent("closeModal");
|
|
44
|
-
var closeAllModals = createEvent("closeAllModals");
|
|
45
|
-
var updateModal = (payload) => createEvent("updateModal")(payload);
|
|
46
|
-
var updateContextModal = (payload) => createEvent("updateContextModal")(payload);
|
|
47
|
-
var modals = {
|
|
48
|
-
open: openModal,
|
|
49
|
-
close: closeModal,
|
|
50
|
-
closeAll: closeAllModals,
|
|
51
|
-
openConfirmModal,
|
|
52
|
-
openContextModal,
|
|
53
|
-
updateModal,
|
|
54
|
-
updateContextModal
|
|
55
|
-
};
|
|
24
|
+
import { create } from 'zustand';
|
|
25
|
+
import { useForm } from '@mantine/form';
|
|
56
26
|
|
|
57
27
|
// ../core/src/integrations/oauth/provider-registry.ts
|
|
58
28
|
var OAUTH_PROVIDERS = {
|
|
@@ -315,282 +285,6 @@ function showAuthError(error) {
|
|
|
315
285
|
}
|
|
316
286
|
showApiErrorNotification(error);
|
|
317
287
|
}
|
|
318
|
-
function CredentialList({ credentials, isLoading, onEdit }) {
|
|
319
|
-
const [deleteCredential, setDeleteCredential] = useState(null);
|
|
320
|
-
const deleteMutation = useDeleteCredential();
|
|
321
|
-
const handleDelete = () => {
|
|
322
|
-
if (deleteCredential) {
|
|
323
|
-
deleteMutation.mutate(deleteCredential.id);
|
|
324
|
-
setDeleteCredential(null);
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
const handleCloseDelete = () => {
|
|
328
|
-
if (!deleteMutation.isPending) {
|
|
329
|
-
setDeleteCredential(null);
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
if (isLoading) {
|
|
333
|
-
return /* @__PURE__ */ jsx(ListSkeleton, { rows: 3, rowHeight: 60 });
|
|
334
|
-
}
|
|
335
|
-
if (credentials.length === 0) {
|
|
336
|
-
return /* @__PURE__ */ jsx(
|
|
337
|
-
EmptyState,
|
|
338
|
-
{
|
|
339
|
-
icon: IconKey,
|
|
340
|
-
title: "No credentials yet",
|
|
341
|
-
description: "Create your first credential to enable external service integrations"
|
|
342
|
-
}
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
346
|
-
/* @__PURE__ */ jsxs(Table, { children: [
|
|
347
|
-
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
348
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
|
|
349
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Created" }),
|
|
350
|
-
/* @__PURE__ */ jsx(Table.Th, { w: 100 })
|
|
351
|
-
] }) }),
|
|
352
|
-
/* @__PURE__ */ jsx(Table.Tbody, { children: credentials.map((credential) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
353
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
354
|
-
/* @__PURE__ */ jsx(IconKey, { size: 16, style: { opacity: 0.6 } }),
|
|
355
|
-
/* @__PURE__ */ jsx(Text, { fw: 500, children: credential.name })
|
|
356
|
-
] }) }),
|
|
357
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
358
|
-
/* @__PURE__ */ jsx(IconCalendar, { size: 14, style: { opacity: 0.5 } }),
|
|
359
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(credential.createdAt) })
|
|
360
|
-
] }) }),
|
|
361
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", children: [
|
|
362
|
-
onEdit && /* @__PURE__ */ jsx(Tooltip, { label: "Edit credential", children: /* @__PURE__ */ jsx(
|
|
363
|
-
ActionIcon,
|
|
364
|
-
{
|
|
365
|
-
variant: "subtle",
|
|
366
|
-
color: "gray",
|
|
367
|
-
onClick: () => onEdit(credential),
|
|
368
|
-
disabled: deleteMutation.isPending,
|
|
369
|
-
children: /* @__PURE__ */ jsx(IconPencil, { size: 16 })
|
|
370
|
-
}
|
|
371
|
-
) }),
|
|
372
|
-
/* @__PURE__ */ jsx(Tooltip, { label: "Delete credential", position: "left", children: /* @__PURE__ */ jsx(
|
|
373
|
-
ActionIcon,
|
|
374
|
-
{
|
|
375
|
-
variant: "subtle",
|
|
376
|
-
color: "red",
|
|
377
|
-
onClick: () => setDeleteCredential(credential),
|
|
378
|
-
disabled: deleteMutation.isPending,
|
|
379
|
-
children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
|
|
380
|
-
}
|
|
381
|
-
) })
|
|
382
|
-
] }) })
|
|
383
|
-
] }, credential.id)) })
|
|
384
|
-
] }),
|
|
385
|
-
/* @__PURE__ */ jsx(CustomModal, { opened: !!deleteCredential, onClose: handleCloseDelete, size: "md", loading: deleteMutation.isPending, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
386
|
-
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
387
|
-
/* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
|
|
388
|
-
/* @__PURE__ */ jsx(Title, { order: 3, children: "Delete Credential" })
|
|
389
|
-
] }),
|
|
390
|
-
/* @__PURE__ */ jsxs(Text, { size: "sm", children: [
|
|
391
|
-
"Are you sure you want to delete",
|
|
392
|
-
" ",
|
|
393
|
-
/* @__PURE__ */ jsxs(Text, { span: true, fw: 600, children: [
|
|
394
|
-
'"',
|
|
395
|
-
deleteCredential?.name,
|
|
396
|
-
'"'
|
|
397
|
-
] }),
|
|
398
|
-
"?"
|
|
399
|
-
] }),
|
|
400
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone. Any agents or tools using this credential will stop working immediately." }),
|
|
401
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
|
|
402
|
-
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleCloseDelete, disabled: deleteMutation.isPending, children: "Cancel" }),
|
|
403
|
-
/* @__PURE__ */ jsx(
|
|
404
|
-
Button,
|
|
405
|
-
{
|
|
406
|
-
color: "red",
|
|
407
|
-
onClick: handleDelete,
|
|
408
|
-
loading: deleteMutation.isPending,
|
|
409
|
-
leftSection: /* @__PURE__ */ jsx(IconTrash, { size: 16 }),
|
|
410
|
-
children: "Delete Credential"
|
|
411
|
-
}
|
|
412
|
-
)
|
|
413
|
-
] })
|
|
414
|
-
] }) })
|
|
415
|
-
] });
|
|
416
|
-
}
|
|
417
|
-
function CreateCredentialModal({ opened, onClose, onSuccess }) {
|
|
418
|
-
const createCredential = useCreateCredential();
|
|
419
|
-
const [selectedType, setSelectedType] = useState("api-key");
|
|
420
|
-
const [schema, setSchema] = useState(getCredentialSchema("api-key"));
|
|
421
|
-
useEffect(() => {
|
|
422
|
-
setSchema(getCredentialSchema(selectedType));
|
|
423
|
-
}, [selectedType]);
|
|
424
|
-
const getInitialValues = (currentSchema) => {
|
|
425
|
-
const values = { name: "" };
|
|
426
|
-
if (currentSchema.fields) {
|
|
427
|
-
currentSchema.fields.forEach((field) => {
|
|
428
|
-
values[field.key] = "";
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
return values;
|
|
432
|
-
};
|
|
433
|
-
const getValidation = (currentSchema) => {
|
|
434
|
-
const validation = {
|
|
435
|
-
name: (value) => {
|
|
436
|
-
const result = CredentialSchemas.CreateRequest.shape.name.safeParse(value);
|
|
437
|
-
return result.success ? null : result.error.issues[0]?.message || "Invalid credential name";
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
if (currentSchema.fields) {
|
|
441
|
-
currentSchema.fields.forEach((field) => {
|
|
442
|
-
if (field.required) {
|
|
443
|
-
validation[field.key] = (value) => !value.trim() ? `${field.label} is required` : null;
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
return validation;
|
|
448
|
-
};
|
|
449
|
-
const form = useForm({
|
|
450
|
-
initialValues: getInitialValues(schema),
|
|
451
|
-
validate: getValidation(schema)
|
|
452
|
-
});
|
|
453
|
-
useEffect(() => {
|
|
454
|
-
const newValues = getInitialValues(schema);
|
|
455
|
-
form.setValues(newValues);
|
|
456
|
-
form.setFieldValue("name", form.values.name || "");
|
|
457
|
-
form.resetDirty();
|
|
458
|
-
}, [selectedType]);
|
|
459
|
-
const handleSubmit = async (values) => {
|
|
460
|
-
if (schema.type === "oauth") {
|
|
461
|
-
console.error("OAuth credentials cannot be created via this modal");
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
const data = {
|
|
465
|
-
name: values.name.trim(),
|
|
466
|
-
type: selectedType,
|
|
467
|
-
value: buildCredentialValue(selectedType, values)
|
|
468
|
-
};
|
|
469
|
-
const validationResult = CredentialSchemas.CreateRequest.safeParse(data);
|
|
470
|
-
if (!validationResult.success) {
|
|
471
|
-
console.error("Validation failed:", validationResult.error);
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
try {
|
|
475
|
-
const result = await createCredential.mutateAsync(data);
|
|
476
|
-
form.reset();
|
|
477
|
-
setSelectedType("api-key");
|
|
478
|
-
onSuccess(result);
|
|
479
|
-
} catch (error) {
|
|
480
|
-
console.error("Failed to create credential:", error);
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
const handleClose = () => {
|
|
484
|
-
if (!createCredential.isPending) {
|
|
485
|
-
form.reset();
|
|
486
|
-
setSelectedType("api-key");
|
|
487
|
-
onClose();
|
|
488
|
-
}
|
|
489
|
-
};
|
|
490
|
-
return /* @__PURE__ */ jsx(CustomModal, { opened, onClose: handleClose, size: "lg", loading: createCredential.isPending, children: /* @__PURE__ */ jsx("form", { onSubmit: form.onSubmit(handleSubmit), children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
491
|
-
/* @__PURE__ */ jsx(Title, { order: 3, children: "Create New Credential" }),
|
|
492
|
-
/* @__PURE__ */ jsx(
|
|
493
|
-
Select,
|
|
494
|
-
{
|
|
495
|
-
label: "Credential Type",
|
|
496
|
-
description: "Select the integration type",
|
|
497
|
-
placeholder: "Select credential type",
|
|
498
|
-
data: Object.entries(CREDENTIAL_SCHEMAS).filter(([_, s]) => s.type !== "oauth").map(([key, s]) => ({ value: key, label: s.label })),
|
|
499
|
-
value: selectedType,
|
|
500
|
-
onChange: (value) => setSelectedType(value || "api-key"),
|
|
501
|
-
required: true,
|
|
502
|
-
disabled: createCredential.isPending
|
|
503
|
-
}
|
|
504
|
-
),
|
|
505
|
-
/* @__PURE__ */ jsx(
|
|
506
|
-
TextInput,
|
|
507
|
-
{
|
|
508
|
-
label: "Credential Name",
|
|
509
|
-
description: schema.nameSuggestions.length > 0 ? `Suggested: ${schema.nameSuggestions.join(", ")}` : "Lowercase letters, numbers, and hyphens only (e.g., service-dev, service-prod)",
|
|
510
|
-
placeholder: schema.nameSuggestions[0] || "e.g., service-prod",
|
|
511
|
-
leftSection: /* @__PURE__ */ jsx(IconKey, { size: 16 }),
|
|
512
|
-
maxLength: 100,
|
|
513
|
-
required: true,
|
|
514
|
-
...form.getInputProps("name"),
|
|
515
|
-
disabled: createCredential.isPending
|
|
516
|
-
}
|
|
517
|
-
),
|
|
518
|
-
schema.type === "oauth" ? /* @__PURE__ */ jsxs(Alert, { icon: /* @__PURE__ */ jsx(IconInfoCircle, { size: 16 }), color: "blue", children: [
|
|
519
|
-
schema.label,
|
|
520
|
-
" uses OAuth authentication. Please go to the OAuth Integrations page to connect your",
|
|
521
|
-
" ",
|
|
522
|
-
schema.label,
|
|
523
|
-
" account."
|
|
524
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
525
|
-
schema.fields?.map((field) => {
|
|
526
|
-
const Input = field.type === "text" ? TextInput : PasswordInput;
|
|
527
|
-
return /* @__PURE__ */ jsx(
|
|
528
|
-
Input,
|
|
529
|
-
{
|
|
530
|
-
label: field.label,
|
|
531
|
-
description: field.description,
|
|
532
|
-
placeholder: field.placeholder,
|
|
533
|
-
required: field.required,
|
|
534
|
-
value: form.values[field.key] || "",
|
|
535
|
-
onChange: (e) => form.setFieldValue(field.key, e.target.value),
|
|
536
|
-
error: form.errors[field.key],
|
|
537
|
-
disabled: createCredential.isPending
|
|
538
|
-
},
|
|
539
|
-
field.key
|
|
540
|
-
);
|
|
541
|
-
}),
|
|
542
|
-
schema.docsUrl && /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
|
|
543
|
-
"Get credentials from",
|
|
544
|
-
" ",
|
|
545
|
-
/* @__PURE__ */ jsx(Anchor, { href: schema.docsUrl, target: "_blank", rel: "noopener noreferrer", children: schema.docsUrl })
|
|
546
|
-
] })
|
|
547
|
-
] }),
|
|
548
|
-
createCredential.error && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconExclamationMark, { size: 16 }), color: "red", children: createCredential.error instanceof Error ? createCredential.error.message : "An error occurred while creating the credential" }),
|
|
549
|
-
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
|
|
550
|
-
/* @__PURE__ */ jsx(Button, { variant: "subtle", onClick: handleClose, disabled: createCredential.isPending, children: "Cancel" }),
|
|
551
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", loading: createCredential.isPending, leftSection: /* @__PURE__ */ jsx(IconKey, { size: 16 }), children: "Create Credential" })
|
|
552
|
-
] })
|
|
553
|
-
] }) }) });
|
|
554
|
-
}
|
|
555
|
-
function CredentialSettings({ oauthSection }) {
|
|
556
|
-
const { organizationReady } = useInitialization();
|
|
557
|
-
const [showCreateModal, setShowCreateModal] = useState(false);
|
|
558
|
-
const { data: credentials = [], isLoading } = useCredentials();
|
|
559
|
-
if (!organizationReady) return /* @__PURE__ */ jsx(AppShellLoader, {});
|
|
560
|
-
const handleCredentialCreated = () => {
|
|
561
|
-
setShowCreateModal(false);
|
|
562
|
-
};
|
|
563
|
-
return /* @__PURE__ */ jsxs(Stack, { children: [
|
|
564
|
-
/* @__PURE__ */ jsx(
|
|
565
|
-
PageTitleCaption,
|
|
566
|
-
{
|
|
567
|
-
title: "Organization Credentials",
|
|
568
|
-
caption: "Manage encrypted credentials for third-party service integrations"
|
|
569
|
-
}
|
|
570
|
-
),
|
|
571
|
-
oauthSection,
|
|
572
|
-
/* @__PURE__ */ jsxs(Paper, { withBorder: true, children: [
|
|
573
|
-
/* @__PURE__ */ jsx(
|
|
574
|
-
CardHeader,
|
|
575
|
-
{
|
|
576
|
-
icon: /* @__PURE__ */ jsx(IconKey, { size: 18 }),
|
|
577
|
-
title: "Credentials",
|
|
578
|
-
subtitle: "Configured API keys and credentials",
|
|
579
|
-
rightSection: /* @__PURE__ */ jsx(Button, { leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: () => setShowCreateModal(true), variant: "light", children: "Create Credential" })
|
|
580
|
-
}
|
|
581
|
-
),
|
|
582
|
-
/* @__PURE__ */ jsx(CredentialList, { credentials, isLoading })
|
|
583
|
-
] }),
|
|
584
|
-
/* @__PURE__ */ jsx(
|
|
585
|
-
CreateCredentialModal,
|
|
586
|
-
{
|
|
587
|
-
opened: showCreateModal,
|
|
588
|
-
onClose: () => setShowCreateModal(false),
|
|
589
|
-
onSuccess: handleCredentialCreated
|
|
590
|
-
}
|
|
591
|
-
)
|
|
592
|
-
] });
|
|
593
|
-
}
|
|
594
288
|
function OAuthConnectModal({ opened, onClose, provider, onConnect, error, isLoading }) {
|
|
595
289
|
const [credentialName, setCredentialName] = useState("");
|
|
596
290
|
useEffect(() => {
|
|
@@ -628,25 +322,236 @@ function OAuthConnectModal({ opened, onClose, provider, onConnect, error, isLoad
|
|
|
628
322
|
] }),
|
|
629
323
|
error && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "red", title: "Connection Failed", children: error }),
|
|
630
324
|
/* @__PURE__ */ jsx(
|
|
631
|
-
TextInput,
|
|
325
|
+
TextInput,
|
|
326
|
+
{
|
|
327
|
+
label: "Credential Name",
|
|
328
|
+
placeholder: `${provider.id}-prod`,
|
|
329
|
+
value: credentialName,
|
|
330
|
+
onChange: (e) => setCredentialName(e.target.value),
|
|
331
|
+
required: true,
|
|
332
|
+
disabled: isLoading,
|
|
333
|
+
description: "Use naming convention to indicate environment (e.g., notion-dev, slack-prod)"
|
|
334
|
+
}
|
|
335
|
+
),
|
|
336
|
+
/* @__PURE__ */ jsxs(Group, { justify: "space-between", mt: "md", children: [
|
|
337
|
+
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: isLoading, children: "Cancel" }),
|
|
338
|
+
/* @__PURE__ */ jsxs(Button, { onClick: handleConnect, loading: isLoading, disabled: !credentialName.trim(), children: [
|
|
339
|
+
"Connect ",
|
|
340
|
+
provider.name
|
|
341
|
+
] })
|
|
342
|
+
] })
|
|
343
|
+
] }) });
|
|
344
|
+
}
|
|
345
|
+
var OAUTH_COMPLETION_CHANNEL = "elevasis-oauth";
|
|
346
|
+
var OAUTH_COMPLETION_STORAGE_KEY = "elevasis:oauth-complete";
|
|
347
|
+
function useOAuthFlow({ apiUrl: apiUrlOverride } = {}) {
|
|
348
|
+
const [isAuthorizing, setIsAuthorizing] = useState(false);
|
|
349
|
+
const [error, setError] = useState(null);
|
|
350
|
+
const { isReady, apiUrl: contextApiUrl } = useElevasisServices();
|
|
351
|
+
const { getAccessToken, organizationId } = useAuthContext();
|
|
352
|
+
const apiUrl = apiUrlOverride ?? contextApiUrl;
|
|
353
|
+
const authorize = useCallback(
|
|
354
|
+
async (provider, credentialName) => {
|
|
355
|
+
if (!isReady || !organizationId) {
|
|
356
|
+
setError("Organization context not ready");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
setIsAuthorizing(true);
|
|
360
|
+
setError(null);
|
|
361
|
+
const width = 600;
|
|
362
|
+
const height = 700;
|
|
363
|
+
const left = Math.round(window.screen.width / 2 - width / 2);
|
|
364
|
+
const top = Math.round(window.screen.height / 2 - height / 2);
|
|
365
|
+
const popup = window.open(
|
|
366
|
+
"about:blank",
|
|
367
|
+
`oauth-${provider}`,
|
|
368
|
+
`width=${width},height=${height},left=${left},top=${top}`
|
|
369
|
+
);
|
|
370
|
+
if (!popup) {
|
|
371
|
+
setIsAuthorizing(false);
|
|
372
|
+
setError("Please enable popups and try again");
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
let token;
|
|
376
|
+
try {
|
|
377
|
+
token = await getAccessToken();
|
|
378
|
+
} catch {
|
|
379
|
+
popup.close();
|
|
380
|
+
setIsAuthorizing(false);
|
|
381
|
+
setError("Authorization failed. Please sign in again.");
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
const authorizeUrl = new URL(`${apiUrl}/api/oauth/authorize/${provider}`);
|
|
386
|
+
authorizeUrl.searchParams.set("credentialName", credentialName);
|
|
387
|
+
authorizeUrl.searchParams.set("token", token);
|
|
388
|
+
authorizeUrl.searchParams.set("orgId", organizationId);
|
|
389
|
+
authorizeUrl.searchParams.set("appOrigin", window.location.origin);
|
|
390
|
+
popup.location.href = authorizeUrl.toString();
|
|
391
|
+
await waitForOAuthCallback(popup, Date.now());
|
|
392
|
+
} catch (err) {
|
|
393
|
+
const rawError = err instanceof Error ? err.message : "unknown";
|
|
394
|
+
if (rawError.includes("popup_blocked")) {
|
|
395
|
+
setError("Please enable popups and try again");
|
|
396
|
+
} else if (rawError.includes("timeout")) {
|
|
397
|
+
setError("Authorization timed out. Please try again.");
|
|
398
|
+
} else if (rawError.includes("popup_closed")) {
|
|
399
|
+
setError("Authorization was cancelled.");
|
|
400
|
+
} else if (rawError.includes("token_exchange_failed")) {
|
|
401
|
+
setError("Failed to obtain access token. Please try again.");
|
|
402
|
+
} else if (rawError.includes("invalid_state")) {
|
|
403
|
+
setError("Security validation failed. Please try again.");
|
|
404
|
+
} else if (rawError.includes("state_expired")) {
|
|
405
|
+
setError("Authorization expired. Please try again.");
|
|
406
|
+
} else {
|
|
407
|
+
setError(`Failed to connect: ${rawError}`);
|
|
408
|
+
}
|
|
409
|
+
throw err;
|
|
410
|
+
} finally {
|
|
411
|
+
setIsAuthorizing(false);
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
[isReady, organizationId, apiUrl, getAccessToken]
|
|
415
|
+
);
|
|
416
|
+
const waitForOAuthCallback = (popup, startedAt) => {
|
|
417
|
+
return new Promise((resolve, reject) => {
|
|
418
|
+
let broadcastChannel = null;
|
|
419
|
+
const complete = (data) => {
|
|
420
|
+
const payload = parseOAuthCompletionPayload(data, startedAt);
|
|
421
|
+
if (!payload) return;
|
|
422
|
+
cleanup();
|
|
423
|
+
if (payload.status === "success") {
|
|
424
|
+
resolve();
|
|
425
|
+
} else {
|
|
426
|
+
reject(new Error(payload.error || "oauth_failed"));
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
const messageHandler = (event) => {
|
|
430
|
+
if (!isTrustedCallbackOrigin(event.origin)) return;
|
|
431
|
+
complete(event.data);
|
|
432
|
+
};
|
|
433
|
+
const storageHandler = (event) => {
|
|
434
|
+
if (event.key !== OAUTH_COMPLETION_STORAGE_KEY || !event.newValue) return;
|
|
435
|
+
try {
|
|
436
|
+
complete(JSON.parse(event.newValue));
|
|
437
|
+
} catch {
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
const rejectIfPopupClosed = () => {
|
|
441
|
+
if (popup.closed) {
|
|
442
|
+
cleanup();
|
|
443
|
+
reject(new Error("popup_closed"));
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
window.addEventListener("message", messageHandler);
|
|
447
|
+
window.addEventListener("storage", storageHandler);
|
|
448
|
+
window.addEventListener("focus", rejectIfPopupClosed);
|
|
449
|
+
if ("BroadcastChannel" in window) {
|
|
450
|
+
broadcastChannel = new BroadcastChannel(OAUTH_COMPLETION_CHANNEL);
|
|
451
|
+
broadcastChannel.onmessage = (event) => complete(event.data);
|
|
452
|
+
}
|
|
453
|
+
const timeout = setTimeout(() => {
|
|
454
|
+
cleanup();
|
|
455
|
+
reject(new Error("timeout"));
|
|
456
|
+
}, OAUTH_FLOW_TIMEOUT);
|
|
457
|
+
const cleanup = () => {
|
|
458
|
+
clearTimeout(timeout);
|
|
459
|
+
broadcastChannel?.close();
|
|
460
|
+
window.removeEventListener("focus", rejectIfPopupClosed);
|
|
461
|
+
window.removeEventListener("storage", storageHandler);
|
|
462
|
+
window.removeEventListener("message", messageHandler);
|
|
463
|
+
};
|
|
464
|
+
});
|
|
465
|
+
};
|
|
466
|
+
return { authorize, isAuthorizing, error };
|
|
467
|
+
}
|
|
468
|
+
function parseOAuthCompletionPayload(data, startedAt) {
|
|
469
|
+
if (!data || typeof data !== "object") return null;
|
|
470
|
+
const payload = data;
|
|
471
|
+
if (payload.type !== "oauth-complete") return null;
|
|
472
|
+
if (payload.completedAt && payload.completedAt < startedAt) return null;
|
|
473
|
+
return payload;
|
|
474
|
+
}
|
|
475
|
+
function isTrustedCallbackOrigin(origin) {
|
|
476
|
+
if (origin === window.location.origin || origin === "https://app.elevasis.io") {
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
const url = new URL(origin);
|
|
481
|
+
return url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
482
|
+
} catch {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
var PROVIDER_ICONS = {
|
|
487
|
+
"google-sheets": IconBrandGoogleDrive,
|
|
488
|
+
dropbox: IconBrandDropbox
|
|
489
|
+
};
|
|
490
|
+
function OAuthIntegrationsCard({ apiUrl } = {}) {
|
|
491
|
+
const [selectedProviderId, setSelectedProviderId] = useState(null);
|
|
492
|
+
const [modalOpened, setModalOpened] = useState(false);
|
|
493
|
+
const { authorize, isAuthorizing, error } = useOAuthFlow({ apiUrl });
|
|
494
|
+
const queryClient = useQueryClient();
|
|
495
|
+
const handleConnect = async (credentialName) => {
|
|
496
|
+
if (!selectedProviderId) return;
|
|
497
|
+
try {
|
|
498
|
+
await authorize(selectedProviderId, credentialName);
|
|
499
|
+
setModalOpened(false);
|
|
500
|
+
setSelectedProviderId(null);
|
|
501
|
+
queryClient.invalidateQueries({ queryKey: ["credentials"] });
|
|
502
|
+
} catch (err) {
|
|
503
|
+
console.error("OAuth flow failed:", err);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
const handleOpenModal = (providerId) => {
|
|
507
|
+
setSelectedProviderId(providerId);
|
|
508
|
+
setModalOpened(true);
|
|
509
|
+
};
|
|
510
|
+
useEffect(() => {
|
|
511
|
+
const params = new URLSearchParams(window.location.search);
|
|
512
|
+
const requested = params.get("connect");
|
|
513
|
+
if (requested && OAUTH_PROVIDERS[requested]) {
|
|
514
|
+
handleOpenModal(requested);
|
|
515
|
+
params.delete("connect");
|
|
516
|
+
const remaining = params.toString();
|
|
517
|
+
const newUrl = window.location.pathname + (remaining ? `?${remaining}` : "") + window.location.hash;
|
|
518
|
+
window.history.replaceState({}, "", newUrl);
|
|
519
|
+
}
|
|
520
|
+
}, []);
|
|
521
|
+
const providers = Object.values(OAUTH_PROVIDERS);
|
|
522
|
+
const selectedProvider = selectedProviderId ? OAUTH_PROVIDERS[selectedProviderId] : null;
|
|
523
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
524
|
+
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
525
|
+
/* @__PURE__ */ jsx(Group, { justify: "space-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
526
|
+
/* @__PURE__ */ jsx(Title, { order: 3, children: "OAuth Integrations" }),
|
|
527
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", mt: 4, children: "Connect third-party services using OAuth 2.0" })
|
|
528
|
+
] }) }),
|
|
529
|
+
/* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2, md: 3 }, children: providers.map((provider) => {
|
|
530
|
+
const Icon = PROVIDER_ICONS[provider.id] || IconPlug;
|
|
531
|
+
return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
532
|
+
/* @__PURE__ */ jsx(Group, { justify: "space-between", align: "flex-start", children: /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
|
|
533
|
+
/* @__PURE__ */ jsx(ThemeIcon, { size: "lg", variant: "light", children: /* @__PURE__ */ jsx(Icon, { size: 20 }) }),
|
|
534
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { fw: 600, style: { fontFamily: "var(--mantine-font-family-headings)" }, children: provider.name }) })
|
|
535
|
+
] }) }),
|
|
536
|
+
/* @__PURE__ */ jsx(Button, { variant: "filled", size: "sm", onClick: () => handleOpenModal(provider.id), fullWidth: true, children: "Connect" })
|
|
537
|
+
] }) }, provider.id);
|
|
538
|
+
}) })
|
|
539
|
+
] }) }),
|
|
540
|
+
/* @__PURE__ */ jsx(
|
|
541
|
+
OAuthConnectModal,
|
|
632
542
|
{
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
543
|
+
opened: modalOpened,
|
|
544
|
+
onClose: () => {
|
|
545
|
+
setModalOpened(false);
|
|
546
|
+
setSelectedProviderId(null);
|
|
547
|
+
},
|
|
548
|
+
provider: selectedProvider,
|
|
549
|
+
onConnect: handleConnect,
|
|
550
|
+
error,
|
|
551
|
+
isLoading: isAuthorizing
|
|
640
552
|
}
|
|
641
|
-
)
|
|
642
|
-
|
|
643
|
-
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: isLoading, children: "Cancel" }),
|
|
644
|
-
/* @__PURE__ */ jsxs(Button, { onClick: handleConnect, loading: isLoading, disabled: !credentialName.trim(), children: [
|
|
645
|
-
"Connect ",
|
|
646
|
-
provider.name
|
|
647
|
-
] })
|
|
648
|
-
] })
|
|
649
|
-
] }) });
|
|
553
|
+
)
|
|
554
|
+
] });
|
|
650
555
|
}
|
|
651
556
|
function MembershipStatusBadge({ status, size = "sm", variant = "light" }) {
|
|
652
557
|
const getStatusLabel = (status2) => {
|
|
@@ -1642,7 +1547,7 @@ function OrgMembersList({ orgId }) {
|
|
|
1642
1547
|
const [selectedMember, setSelectedMember] = useState(null);
|
|
1643
1548
|
const [modalOpened, setModalOpened] = useState(false);
|
|
1644
1549
|
const [defaultTab, setDefaultTab] = useState("roles");
|
|
1645
|
-
const
|
|
1550
|
+
const openModal = (member, tab) => {
|
|
1646
1551
|
setSelectedMember(member);
|
|
1647
1552
|
setDefaultTab(tab);
|
|
1648
1553
|
setModalOpened(true);
|
|
@@ -1691,7 +1596,7 @@ function OrgMembersList({ orgId }) {
|
|
|
1691
1596
|
/* @__PURE__ */ jsx(MembershipStatusBadge, { status: member.status }),
|
|
1692
1597
|
/* @__PURE__ */ jsx(ProvisioningStateBadge, { state: member.provisioningState })
|
|
1693
1598
|
] }) }),
|
|
1694
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Tooltip, { label: "Edit member access", withArrow: true, children: /* @__PURE__ */ jsx(ActionIcon, { variant: "light", size: "sm", color: "gray", onClick: () =>
|
|
1599
|
+
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Tooltip, { label: "Edit member access", withArrow: true, children: /* @__PURE__ */ jsx(ActionIcon, { variant: "light", size: "sm", color: "gray", onClick: () => openModal(member, "roles"), children: /* @__PURE__ */ jsx(IconPencil, { size: 14 }) }) }) })
|
|
1695
1600
|
] }, member.id);
|
|
1696
1601
|
}) })
|
|
1697
1602
|
] }) }),
|
|
@@ -1843,145 +1748,6 @@ function MyRolesPage({ onManageMembers, onManageOrganizationRoles }) {
|
|
|
1843
1748
|
] }) }) })
|
|
1844
1749
|
] });
|
|
1845
1750
|
}
|
|
1846
|
-
var OAUTH_COMPLETION_CHANNEL = "elevasis-oauth";
|
|
1847
|
-
var OAUTH_COMPLETION_STORAGE_KEY = "elevasis:oauth-complete";
|
|
1848
|
-
function useOAuthFlow({ apiUrl }) {
|
|
1849
|
-
const [isAuthorizing, setIsAuthorizing] = useState(false);
|
|
1850
|
-
const [error, setError] = useState(null);
|
|
1851
|
-
const { isReady } = useElevasisServices();
|
|
1852
|
-
const { getAccessToken, organizationId } = useAuthContext();
|
|
1853
|
-
const authorize = useCallback(
|
|
1854
|
-
async (provider, credentialName) => {
|
|
1855
|
-
if (!isReady || !organizationId) {
|
|
1856
|
-
setError("Organization context not ready");
|
|
1857
|
-
return;
|
|
1858
|
-
}
|
|
1859
|
-
setIsAuthorizing(true);
|
|
1860
|
-
setError(null);
|
|
1861
|
-
const width = 600;
|
|
1862
|
-
const height = 700;
|
|
1863
|
-
const left = Math.round(window.screen.width / 2 - width / 2);
|
|
1864
|
-
const top = Math.round(window.screen.height / 2 - height / 2);
|
|
1865
|
-
const popup = window.open(
|
|
1866
|
-
"about:blank",
|
|
1867
|
-
`oauth-${provider}`,
|
|
1868
|
-
`width=${width},height=${height},left=${left},top=${top}`
|
|
1869
|
-
);
|
|
1870
|
-
if (!popup) {
|
|
1871
|
-
setIsAuthorizing(false);
|
|
1872
|
-
setError("Please enable popups and try again");
|
|
1873
|
-
return;
|
|
1874
|
-
}
|
|
1875
|
-
let token;
|
|
1876
|
-
try {
|
|
1877
|
-
token = await getAccessToken();
|
|
1878
|
-
} catch {
|
|
1879
|
-
popup.close();
|
|
1880
|
-
setIsAuthorizing(false);
|
|
1881
|
-
setError("Authorization failed. Please sign in again.");
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
try {
|
|
1885
|
-
const authorizeUrl = new URL(`${apiUrl}/api/oauth/authorize/${provider}`);
|
|
1886
|
-
authorizeUrl.searchParams.set("credentialName", credentialName);
|
|
1887
|
-
authorizeUrl.searchParams.set("token", token);
|
|
1888
|
-
authorizeUrl.searchParams.set("orgId", organizationId);
|
|
1889
|
-
popup.location.href = authorizeUrl.toString();
|
|
1890
|
-
await waitForOAuthCallback(popup, Date.now());
|
|
1891
|
-
} catch (err) {
|
|
1892
|
-
const rawError = err instanceof Error ? err.message : "unknown";
|
|
1893
|
-
if (rawError.includes("popup_blocked")) {
|
|
1894
|
-
setError("Please enable popups and try again");
|
|
1895
|
-
} else if (rawError.includes("timeout")) {
|
|
1896
|
-
setError("Authorization timed out. Please try again.");
|
|
1897
|
-
} else if (rawError.includes("popup_closed")) {
|
|
1898
|
-
setError("Authorization was cancelled.");
|
|
1899
|
-
} else if (rawError.includes("token_exchange_failed")) {
|
|
1900
|
-
setError("Failed to obtain access token. Please try again.");
|
|
1901
|
-
} else if (rawError.includes("invalid_state")) {
|
|
1902
|
-
setError("Security validation failed. Please try again.");
|
|
1903
|
-
} else if (rawError.includes("state_expired")) {
|
|
1904
|
-
setError("Authorization expired. Please try again.");
|
|
1905
|
-
} else {
|
|
1906
|
-
setError(`Failed to connect: ${rawError}`);
|
|
1907
|
-
}
|
|
1908
|
-
throw err;
|
|
1909
|
-
} finally {
|
|
1910
|
-
setIsAuthorizing(false);
|
|
1911
|
-
}
|
|
1912
|
-
},
|
|
1913
|
-
[isReady, organizationId, apiUrl, getAccessToken]
|
|
1914
|
-
);
|
|
1915
|
-
const waitForOAuthCallback = (popup, startedAt) => {
|
|
1916
|
-
return new Promise((resolve, reject) => {
|
|
1917
|
-
let broadcastChannel = null;
|
|
1918
|
-
const complete = (data) => {
|
|
1919
|
-
const payload = parseOAuthCompletionPayload(data, startedAt);
|
|
1920
|
-
if (!payload) return;
|
|
1921
|
-
cleanup();
|
|
1922
|
-
if (payload.status === "success") {
|
|
1923
|
-
resolve();
|
|
1924
|
-
} else {
|
|
1925
|
-
reject(new Error(payload.error || "oauth_failed"));
|
|
1926
|
-
}
|
|
1927
|
-
};
|
|
1928
|
-
const messageHandler = (event) => {
|
|
1929
|
-
if (!isTrustedCallbackOrigin(event.origin)) return;
|
|
1930
|
-
complete(event.data);
|
|
1931
|
-
};
|
|
1932
|
-
const storageHandler = (event) => {
|
|
1933
|
-
if (event.key !== OAUTH_COMPLETION_STORAGE_KEY || !event.newValue) return;
|
|
1934
|
-
try {
|
|
1935
|
-
complete(JSON.parse(event.newValue));
|
|
1936
|
-
} catch {
|
|
1937
|
-
}
|
|
1938
|
-
};
|
|
1939
|
-
const rejectIfPopupClosed = () => {
|
|
1940
|
-
if (popup.closed) {
|
|
1941
|
-
cleanup();
|
|
1942
|
-
reject(new Error("popup_closed"));
|
|
1943
|
-
}
|
|
1944
|
-
};
|
|
1945
|
-
window.addEventListener("message", messageHandler);
|
|
1946
|
-
window.addEventListener("storage", storageHandler);
|
|
1947
|
-
window.addEventListener("focus", rejectIfPopupClosed);
|
|
1948
|
-
if ("BroadcastChannel" in window) {
|
|
1949
|
-
broadcastChannel = new BroadcastChannel(OAUTH_COMPLETION_CHANNEL);
|
|
1950
|
-
broadcastChannel.onmessage = (event) => complete(event.data);
|
|
1951
|
-
}
|
|
1952
|
-
const timeout = setTimeout(() => {
|
|
1953
|
-
cleanup();
|
|
1954
|
-
reject(new Error("timeout"));
|
|
1955
|
-
}, OAUTH_FLOW_TIMEOUT);
|
|
1956
|
-
const cleanup = () => {
|
|
1957
|
-
clearTimeout(timeout);
|
|
1958
|
-
broadcastChannel?.close();
|
|
1959
|
-
window.removeEventListener("focus", rejectIfPopupClosed);
|
|
1960
|
-
window.removeEventListener("storage", storageHandler);
|
|
1961
|
-
window.removeEventListener("message", messageHandler);
|
|
1962
|
-
};
|
|
1963
|
-
});
|
|
1964
|
-
};
|
|
1965
|
-
return { authorize, isAuthorizing, error };
|
|
1966
|
-
}
|
|
1967
|
-
function parseOAuthCompletionPayload(data, startedAt) {
|
|
1968
|
-
if (!data || typeof data !== "object") return null;
|
|
1969
|
-
const payload = data;
|
|
1970
|
-
if (payload.type !== "oauth-complete") return null;
|
|
1971
|
-
if (payload.completedAt && payload.completedAt < startedAt) return null;
|
|
1972
|
-
return payload;
|
|
1973
|
-
}
|
|
1974
|
-
function isTrustedCallbackOrigin(origin) {
|
|
1975
|
-
if (origin === window.location.origin || origin === "https://app.elevasis.io") {
|
|
1976
|
-
return true;
|
|
1977
|
-
}
|
|
1978
|
-
try {
|
|
1979
|
-
const url = new URL(origin);
|
|
1980
|
-
return url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
1981
|
-
} catch {
|
|
1982
|
-
return false;
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
1751
|
function EditCredentialModal({ apiUrl, credential, onClose }) {
|
|
1986
1752
|
const [credentialName, setCredentialName] = useState("");
|
|
1987
1753
|
const [nameError, setNameError] = useState(null);
|
|
@@ -2153,75 +1919,5 @@ function EditCredentialModal({ apiUrl, credential, onClose }) {
|
|
|
2153
1919
|
] })
|
|
2154
1920
|
] }) });
|
|
2155
1921
|
}
|
|
2156
|
-
var PROVIDER_ICONS = {
|
|
2157
|
-
"google-sheets": IconBrandGoogleDrive,
|
|
2158
|
-
dropbox: IconBrandDropbox
|
|
2159
|
-
};
|
|
2160
|
-
function OAuthIntegrationsCard({ apiUrl }) {
|
|
2161
|
-
const [selectedProviderId, setSelectedProviderId] = useState(null);
|
|
2162
|
-
const [modalOpened, setModalOpened] = useState(false);
|
|
2163
|
-
const { authorize, isAuthorizing, error } = useOAuthFlow({ apiUrl });
|
|
2164
|
-
const queryClient = useQueryClient();
|
|
2165
|
-
const handleConnect = async (credentialName) => {
|
|
2166
|
-
if (!selectedProviderId) return;
|
|
2167
|
-
try {
|
|
2168
|
-
await authorize(selectedProviderId, credentialName);
|
|
2169
|
-
setModalOpened(false);
|
|
2170
|
-
setSelectedProviderId(null);
|
|
2171
|
-
queryClient.invalidateQueries({ queryKey: ["credentials"] });
|
|
2172
|
-
} catch (err) {
|
|
2173
|
-
console.error("OAuth flow failed:", err);
|
|
2174
|
-
}
|
|
2175
|
-
};
|
|
2176
|
-
const handleOpenModal = (providerId) => {
|
|
2177
|
-
setSelectedProviderId(providerId);
|
|
2178
|
-
setModalOpened(true);
|
|
2179
|
-
};
|
|
2180
|
-
useEffect(() => {
|
|
2181
|
-
const params = new URLSearchParams(window.location.search);
|
|
2182
|
-
const requested = params.get("connect");
|
|
2183
|
-
if (requested && OAUTH_PROVIDERS[requested]) {
|
|
2184
|
-
handleOpenModal(requested);
|
|
2185
|
-
params.delete("connect");
|
|
2186
|
-
const remaining = params.toString();
|
|
2187
|
-
const newUrl = window.location.pathname + (remaining ? `?${remaining}` : "") + window.location.hash;
|
|
2188
|
-
window.history.replaceState({}, "", newUrl);
|
|
2189
|
-
}
|
|
2190
|
-
}, []);
|
|
2191
|
-
const providers = Object.values(OAUTH_PROVIDERS);
|
|
2192
|
-
const selectedProvider = selectedProviderId ? OAUTH_PROVIDERS[selectedProviderId] : null;
|
|
2193
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2194
|
-
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
2195
|
-
/* @__PURE__ */ jsx(Group, { justify: "space-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
2196
|
-
/* @__PURE__ */ jsx(Title, { order: 3, children: "OAuth Integrations" }),
|
|
2197
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", mt: 4, children: "Connect third-party services using OAuth 2.0" })
|
|
2198
|
-
] }) }),
|
|
2199
|
-
/* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2, md: 3 }, children: providers.map((provider) => {
|
|
2200
|
-
const Icon = PROVIDER_ICONS[provider.id] || IconPlug;
|
|
2201
|
-
return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
2202
|
-
/* @__PURE__ */ jsx(Group, { justify: "space-between", align: "flex-start", children: /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
|
|
2203
|
-
/* @__PURE__ */ jsx(ThemeIcon, { size: "lg", variant: "light", color: "blue", children: /* @__PURE__ */ jsx(Icon, { size: 20 }) }),
|
|
2204
|
-
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { fw: 600, style: { fontFamily: "var(--mantine-font-family-headings)" }, children: provider.name }) })
|
|
2205
|
-
] }) }),
|
|
2206
|
-
/* @__PURE__ */ jsx(Button, { variant: "filled", size: "sm", onClick: () => handleOpenModal(provider.id), fullWidth: true, children: "Connect" })
|
|
2207
|
-
] }) }, provider.id);
|
|
2208
|
-
}) })
|
|
2209
|
-
] }) }),
|
|
2210
|
-
/* @__PURE__ */ jsx(
|
|
2211
|
-
OAuthConnectModal,
|
|
2212
|
-
{
|
|
2213
|
-
opened: modalOpened,
|
|
2214
|
-
onClose: () => {
|
|
2215
|
-
setModalOpened(false);
|
|
2216
|
-
setSelectedProviderId(null);
|
|
2217
|
-
},
|
|
2218
|
-
provider: selectedProvider,
|
|
2219
|
-
onConnect: handleConnect,
|
|
2220
|
-
error,
|
|
2221
|
-
isLoading: isAuthorizing
|
|
2222
|
-
}
|
|
2223
|
-
)
|
|
2224
|
-
] });
|
|
2225
|
-
}
|
|
2226
1922
|
|
|
2227
|
-
export { AccountSettings, AppearanceSettings,
|
|
1923
|
+
export { AccountSettings, AppearanceSettings, CREDENTIAL_SCHEMAS, CreateWebhookEndpointModal, EditCredentialModal, EditWebhookEndpointModal, MemberAccessModal, MembershipStatusBadge, MyRolesPage, OAuthConnectModal, OAuthIntegrationsCard, OrgMembersList, OrganizationMembershipsList, OrganizationSettings, ProvisioningStateBadge, WebhookEndpointList, WebhookEndpointSettings, WebhookUrlDisplayModal, buildCredentialValue, getCredentialSchema, showAuthError };
|