@elevasis/ui 2.73.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.d.ts +58 -0
- package/dist/app/index.js +9 -10
- package/dist/auth/index.js +5 -6
- package/dist/charts/index.js +5 -6
- package/dist/chunk-2GIJBHZT.js +60 -0
- package/dist/{chunk-Z4ICFQXG.js → chunk-2ZRY3ARL.js} +1 -1
- package/dist/{chunk-5B2ZFO44.js → chunk-3H5DIJN7.js} +1 -1
- package/dist/{chunk-LZT2H2MA.js → chunk-47YJVIZE.js} +36 -43
- package/dist/{chunk-3TS5U5BL.js → chunk-5RH6NYZH.js} +1 -1
- package/dist/chunk-5SLRGUC4.js +224 -0
- package/dist/{chunk-JYBJMVNY.js → chunk-5VF5LPYS.js} +1 -1
- package/dist/{chunk-SWKAH2YM.js → chunk-6BJ5HMMC.js} +282 -553
- package/dist/{chunk-NI37YNRP.js → chunk-7OOFMEX6.js} +11 -5
- package/dist/{chunk-IBCEAVB2.js → chunk-A2SB32YV.js} +65 -5
- package/dist/{chunk-CJ2232PQ.js → chunk-CRPH5AAB.js} +1 -1
- package/dist/chunk-D3XXPCCK.js +69 -0
- package/dist/chunk-DMSTDZCJ.js +305 -0
- package/dist/chunk-DTEGVSJ6.js +645 -0
- package/dist/chunk-DVAOCHIF.js +165 -0
- package/dist/chunk-G7O4CBMC.js +231 -0
- package/dist/{chunk-IGBPWD6S.js → chunk-GDTK33QT.js} +5 -3
- package/dist/chunk-ICHDIVQO.js +54 -0
- package/dist/{chunk-U2H3TIHD.js → chunk-IYATYH5I.js} +2 -52
- package/dist/chunk-L3NBMLRN.js +49 -0
- package/dist/{chunk-LEPYXLJ2.js → chunk-LFLM723H.js} +1 -1
- package/dist/chunk-M6VAZAQW.js +45 -0
- package/dist/{chunk-AJOVWB47.js → chunk-MLHZUN2X.js} +6 -3
- package/dist/chunk-N6K2Q5BM.js +393 -0
- package/dist/chunk-NMHQAYZU.js +439 -0
- package/dist/chunk-NYIIRZSA.js +286 -0
- package/dist/{chunk-ETN3TTRT.js → chunk-OGHTJKBV.js} +115 -8
- package/dist/{chunk-M7WWRZ5Z.js → chunk-QFGNRKIE.js} +1 -78
- package/dist/chunk-QFS2GZMV.js +515 -0
- package/dist/{chunk-OCVSPAKM.js → chunk-SB3IHM5Y.js} +1150 -4346
- package/dist/{chunk-4CW2C5BD.js → chunk-SEYOVNS5.js} +1 -1
- package/dist/chunk-SN7X5N2A.js +116 -0
- package/dist/{chunk-PQ4B62HH.js → chunk-TX3UHDL5.js} +2 -2
- package/dist/chunk-VM2OWOXL.js +24 -0
- package/dist/{chunk-ZDKPHFTH.js → chunk-VWGCURZF.js} +5 -3
- package/dist/chunk-WDBNW44Z.js +49 -0
- package/dist/chunk-YQQ5GRUR.js +21 -0
- package/dist/{chunk-NHNTX6ZL.js → chunk-ZG6YJUT4.js} +3 -2
- package/dist/components/index.d.ts +228 -6
- package/dist/components/index.js +321 -35
- package/dist/components/navigation/index.js +6 -7
- package/dist/features/auth/index.d.ts +78 -2
- package/dist/features/auth/index.js +18 -9
- package/dist/features/clients/index.js +7 -7
- package/dist/features/content/index.d.ts +226 -5
- package/dist/features/content/index.js +270 -19
- package/dist/features/crm/index.d.ts +58 -0
- package/dist/features/crm/index.js +10 -8
- package/dist/features/dashboard/index.js +9 -8
- package/dist/features/delivery/index.d.ts +58 -0
- package/dist/features/delivery/index.js +23 -7
- package/dist/features/knowledge/index.js +5 -6
- package/dist/features/lead-gen/index.d.ts +9 -15
- package/dist/features/lead-gen/index.js +12 -8
- package/dist/features/monitoring/index.js +11 -9
- package/dist/features/monitoring/requests/index.js +8 -7
- package/dist/features/operations/index.js +15 -11
- package/dist/features/public-agent-chat/index.js +3 -1
- package/dist/features/settings/index.d.ts +66 -3
- package/dist/features/settings/index.js +9 -8
- package/dist/hooks/access/index.js +5 -6
- package/dist/hooks/delivery/index.d.ts +58 -0
- package/dist/hooks/delivery/index.js +3 -2
- package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +1 -1
- package/dist/hooks/published.d.ts +181 -5
- package/dist/hooks/published.js +22 -6
- package/dist/initialization/index.d.ts +58 -0
- package/dist/knowledge/index.js +167 -11
- package/dist/{knowledge-search-index-RZLIVWPJ.js → knowledge-search-index-6GQK55IQ.js} +1 -1
- package/dist/layout/index.js +8 -9
- package/dist/profile/index.d.ts +58 -0
- package/dist/provider/index.d.ts +77 -1
- package/dist/provider/index.js +5 -6
- package/dist/provider/published.d.ts +105 -1
- package/dist/provider/published.js +5 -6
- package/dist/test-utils/index.d.ts +16 -5
- package/dist/test-utils/index.js +17 -18
- package/dist/types/index.d.ts +73 -1
- package/dist/utils/index.d.ts +83 -3
- package/dist/utils/index.js +1 -1
- package/package.json +10 -5
- package/dist/chunk-6M6OLGQY.js +0 -36
- package/dist/chunk-MKH2KOAO.js +0 -39
- package/dist/hooks/index.css +0 -659
- package/dist/hooks/index.d.ts +0 -14073
- package/dist/hooks/index.js +0 -27
- package/dist/supabase/index.d.ts +0 -7118
- package/dist/supabase/index.js +0 -3
- package/dist/typeform/index.d.ts +0 -462
- package/dist/typeform/index.js +0 -2051
- package/dist/typeform/schemas.d.ts +0 -67
- package/dist/typeform/schemas.js +0 -2
|
@@ -1,56 +1,28 @@
|
|
|
1
1
|
import { useAvailablePresets } from './chunk-ZFISGKYW.js';
|
|
2
|
-
import {
|
|
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';
|
|
3
5
|
import { FilterBar } from './chunk-PDHTXPSF.js';
|
|
4
|
-
import {
|
|
5
|
-
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';
|
|
6
8
|
import { CardHeader } from './chunk-62GVNH5U.js';
|
|
7
9
|
import { useInitialization } from './chunk-P45GQ3ZW.js';
|
|
8
10
|
import { RoleBadge } from './chunk-UD657ICS.js';
|
|
9
11
|
import { useOrganization } from './chunk-DD3CCMCZ.js';
|
|
10
|
-
import { isAPIClientError, formatDateTime, OAUTH_FLOW_TIMEOUT } from './chunk-
|
|
12
|
+
import { isAPIClientError, formatDateTime, OAUTH_FLOW_TIMEOUT } from './chunk-A2SB32YV.js';
|
|
11
13
|
import { useElevasisServices } from './chunk-BI5VDO2K.js';
|
|
12
14
|
import { brandSupabaseOrgId } from './chunk-MQZE7SUI.js';
|
|
13
15
|
import { useAuthContext } from './chunk-BRJ3QZ4E.js';
|
|
14
|
-
import {
|
|
15
|
-
import { IconShieldOff,
|
|
16
|
-
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';
|
|
17
19
|
import { notifications } from '@mantine/notifications';
|
|
18
20
|
import { z } from 'zod';
|
|
19
21
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
20
22
|
import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
|
21
|
-
import { useForm } from '@mantine/form';
|
|
22
|
-
import { create } from 'zustand';
|
|
23
23
|
import { useQueryClient } from '@tanstack/react-query';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
var openModal = (payload) => {
|
|
27
|
-
const id = payload.modalId || randomId();
|
|
28
|
-
createEvent("openModal")({ ...payload, modalId: id });
|
|
29
|
-
return id;
|
|
30
|
-
};
|
|
31
|
-
var openConfirmModal = (payload) => {
|
|
32
|
-
const id = payload.modalId || randomId();
|
|
33
|
-
createEvent("openConfirmModal")({ ...payload, modalId: id });
|
|
34
|
-
return id;
|
|
35
|
-
};
|
|
36
|
-
var openContextModal = (payload) => {
|
|
37
|
-
const id = payload.modalId || randomId();
|
|
38
|
-
createEvent("openContextModal")({ ...payload, modalId: id });
|
|
39
|
-
return id;
|
|
40
|
-
};
|
|
41
|
-
var closeModal = createEvent("closeModal");
|
|
42
|
-
var closeAllModals = createEvent("closeAllModals");
|
|
43
|
-
var updateModal = (payload) => createEvent("updateModal")(payload);
|
|
44
|
-
var updateContextModal = (payload) => createEvent("updateContextModal")(payload);
|
|
45
|
-
var modals = {
|
|
46
|
-
open: openModal,
|
|
47
|
-
close: closeModal,
|
|
48
|
-
closeAll: closeAllModals,
|
|
49
|
-
openConfirmModal,
|
|
50
|
-
openContextModal,
|
|
51
|
-
updateModal,
|
|
52
|
-
updateContextModal
|
|
53
|
-
};
|
|
24
|
+
import { create } from 'zustand';
|
|
25
|
+
import { useForm } from '@mantine/form';
|
|
54
26
|
|
|
55
27
|
// ../core/src/integrations/oauth/provider-registry.ts
|
|
56
28
|
var OAUTH_PROVIDERS = {
|
|
@@ -236,6 +208,31 @@ var CREDENTIAL_SCHEMAS = {
|
|
|
236
208
|
],
|
|
237
209
|
docsUrl: "https://developer.clickup.com/docs/authentication",
|
|
238
210
|
nameSuggestions: ["clickup-demo", "clickup-prod", "clickup-dev"]
|
|
211
|
+
},
|
|
212
|
+
instagram: {
|
|
213
|
+
type: "single_field",
|
|
214
|
+
label: "Instagram",
|
|
215
|
+
description: "Long-lived Instagram access token for publishing through Meta\u2019s Content Publishing API.",
|
|
216
|
+
fields: [
|
|
217
|
+
{
|
|
218
|
+
key: "accessToken",
|
|
219
|
+
label: "Access Token",
|
|
220
|
+
type: "password",
|
|
221
|
+
required: true,
|
|
222
|
+
placeholder: "IGAA...",
|
|
223
|
+
description: "Long-lived token from the Meta app dashboard. Valid 60 days from issue or last refresh."
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
key: "igUserId",
|
|
227
|
+
label: "Instagram User ID",
|
|
228
|
+
type: "text",
|
|
229
|
+
required: true,
|
|
230
|
+
placeholder: "17841400000000000",
|
|
231
|
+
description: "The Instagram professional account id shown beside the token on the same Meta screen."
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
docsUrl: "https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/content-publishing",
|
|
235
|
+
nameSuggestions: ["elevasis-instagram", "instagram-prod", "instagram-dev"]
|
|
239
236
|
}
|
|
240
237
|
};
|
|
241
238
|
function getCredentialSchema(type) {
|
|
@@ -288,279 +285,6 @@ function showAuthError(error) {
|
|
|
288
285
|
}
|
|
289
286
|
showApiErrorNotification(error);
|
|
290
287
|
}
|
|
291
|
-
function CredentialList({ credentials, isLoading, onEdit }) {
|
|
292
|
-
const [deleteCredential, setDeleteCredential] = useState(null);
|
|
293
|
-
const deleteMutation = useDeleteCredential();
|
|
294
|
-
const handleDelete = () => {
|
|
295
|
-
if (deleteCredential) {
|
|
296
|
-
deleteMutation.mutate(deleteCredential.id);
|
|
297
|
-
setDeleteCredential(null);
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
const handleCloseDelete = () => {
|
|
301
|
-
if (!deleteMutation.isPending) {
|
|
302
|
-
setDeleteCredential(null);
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
if (isLoading) {
|
|
306
|
-
return /* @__PURE__ */ jsx(ListSkeleton, { rows: 3, rowHeight: 60 });
|
|
307
|
-
}
|
|
308
|
-
if (credentials.length === 0) {
|
|
309
|
-
return /* @__PURE__ */ jsx(
|
|
310
|
-
EmptyState,
|
|
311
|
-
{
|
|
312
|
-
icon: IconKey,
|
|
313
|
-
title: "No credentials yet",
|
|
314
|
-
description: "Create your first credential to enable external service integrations"
|
|
315
|
-
}
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
319
|
-
/* @__PURE__ */ jsxs(Table, { children: [
|
|
320
|
-
/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
321
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Name" }),
|
|
322
|
-
/* @__PURE__ */ jsx(Table.Th, { children: "Created" }),
|
|
323
|
-
/* @__PURE__ */ jsx(Table.Th, { w: 100 })
|
|
324
|
-
] }) }),
|
|
325
|
-
/* @__PURE__ */ jsx(Table.Tbody, { children: credentials.map((credential) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
|
|
326
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
327
|
-
/* @__PURE__ */ jsx(IconKey, { size: 16, style: { opacity: 0.6 } }),
|
|
328
|
-
/* @__PURE__ */ jsx(Text, { fw: 500, children: credential.name })
|
|
329
|
-
] }) }),
|
|
330
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
|
|
331
|
-
/* @__PURE__ */ jsx(IconCalendar, { size: 14, style: { opacity: 0.5 } }),
|
|
332
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatDateTime(credential.createdAt) })
|
|
333
|
-
] }) }),
|
|
334
|
-
/* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsxs(Group, { gap: "xs", justify: "flex-end", children: [
|
|
335
|
-
onEdit && /* @__PURE__ */ jsx(Tooltip, { label: "Edit credential", children: /* @__PURE__ */ jsx(
|
|
336
|
-
ActionIcon,
|
|
337
|
-
{
|
|
338
|
-
variant: "subtle",
|
|
339
|
-
color: "gray",
|
|
340
|
-
onClick: () => onEdit(credential),
|
|
341
|
-
disabled: deleteMutation.isPending,
|
|
342
|
-
children: /* @__PURE__ */ jsx(IconPencil, { size: 16 })
|
|
343
|
-
}
|
|
344
|
-
) }),
|
|
345
|
-
/* @__PURE__ */ jsx(Tooltip, { label: "Delete credential", position: "left", children: /* @__PURE__ */ jsx(
|
|
346
|
-
ActionIcon,
|
|
347
|
-
{
|
|
348
|
-
variant: "subtle",
|
|
349
|
-
color: "red",
|
|
350
|
-
onClick: () => setDeleteCredential(credential),
|
|
351
|
-
disabled: deleteMutation.isPending,
|
|
352
|
-
children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
|
|
353
|
-
}
|
|
354
|
-
) })
|
|
355
|
-
] }) })
|
|
356
|
-
] }, credential.id)) })
|
|
357
|
-
] }),
|
|
358
|
-
/* @__PURE__ */ jsx(CustomModal, { opened: !!deleteCredential, onClose: handleCloseDelete, size: "md", loading: deleteMutation.isPending, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
359
|
-
/* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
|
|
360
|
-
/* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, color: "var(--color-error)" }),
|
|
361
|
-
/* @__PURE__ */ jsx(Title, { order: 3, children: "Delete Credential" })
|
|
362
|
-
] }),
|
|
363
|
-
/* @__PURE__ */ jsxs(Text, { size: "sm", children: [
|
|
364
|
-
"Are you sure you want to delete",
|
|
365
|
-
" ",
|
|
366
|
-
/* @__PURE__ */ jsxs(Text, { span: true, fw: 600, children: [
|
|
367
|
-
'"',
|
|
368
|
-
deleteCredential?.name,
|
|
369
|
-
'"'
|
|
370
|
-
] }),
|
|
371
|
-
"?"
|
|
372
|
-
] }),
|
|
373
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "This action cannot be undone. Any agents or tools using this credential will stop working immediately." }),
|
|
374
|
-
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
|
|
375
|
-
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleCloseDelete, disabled: deleteMutation.isPending, children: "Cancel" }),
|
|
376
|
-
/* @__PURE__ */ jsx(
|
|
377
|
-
Button,
|
|
378
|
-
{
|
|
379
|
-
color: "red",
|
|
380
|
-
onClick: handleDelete,
|
|
381
|
-
loading: deleteMutation.isPending,
|
|
382
|
-
leftSection: /* @__PURE__ */ jsx(IconTrash, { size: 16 }),
|
|
383
|
-
children: "Delete Credential"
|
|
384
|
-
}
|
|
385
|
-
)
|
|
386
|
-
] })
|
|
387
|
-
] }) })
|
|
388
|
-
] });
|
|
389
|
-
}
|
|
390
|
-
function CreateCredentialModal({ opened, onClose, onSuccess }) {
|
|
391
|
-
const createCredential = useCreateCredential();
|
|
392
|
-
const [selectedType, setSelectedType] = useState("api-key");
|
|
393
|
-
const [schema, setSchema] = useState(getCredentialSchema("api-key"));
|
|
394
|
-
useEffect(() => {
|
|
395
|
-
setSchema(getCredentialSchema(selectedType));
|
|
396
|
-
}, [selectedType]);
|
|
397
|
-
const getInitialValues = (currentSchema) => {
|
|
398
|
-
const values = { name: "" };
|
|
399
|
-
if (currentSchema.fields) {
|
|
400
|
-
currentSchema.fields.forEach((field) => {
|
|
401
|
-
values[field.key] = "";
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
return values;
|
|
405
|
-
};
|
|
406
|
-
const getValidation = (currentSchema) => {
|
|
407
|
-
const validation = {
|
|
408
|
-
name: (value) => {
|
|
409
|
-
const result = CredentialSchemas.CreateRequest.shape.name.safeParse(value);
|
|
410
|
-
return result.success ? null : result.error.issues[0]?.message || "Invalid credential name";
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
|
-
if (currentSchema.fields) {
|
|
414
|
-
currentSchema.fields.forEach((field) => {
|
|
415
|
-
if (field.required) {
|
|
416
|
-
validation[field.key] = (value) => !value.trim() ? `${field.label} is required` : null;
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
return validation;
|
|
421
|
-
};
|
|
422
|
-
const form = useForm({
|
|
423
|
-
initialValues: getInitialValues(schema),
|
|
424
|
-
validate: getValidation(schema)
|
|
425
|
-
});
|
|
426
|
-
useEffect(() => {
|
|
427
|
-
const newValues = getInitialValues(schema);
|
|
428
|
-
form.setValues(newValues);
|
|
429
|
-
form.setFieldValue("name", form.values.name || "");
|
|
430
|
-
form.resetDirty();
|
|
431
|
-
}, [selectedType]);
|
|
432
|
-
const handleSubmit = async (values) => {
|
|
433
|
-
if (schema.type === "oauth") {
|
|
434
|
-
console.error("OAuth credentials cannot be created via this modal");
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
const data = {
|
|
438
|
-
name: values.name.trim(),
|
|
439
|
-
type: selectedType,
|
|
440
|
-
value: buildCredentialValue(selectedType, values)
|
|
441
|
-
};
|
|
442
|
-
const validationResult = CredentialSchemas.CreateRequest.safeParse(data);
|
|
443
|
-
if (!validationResult.success) {
|
|
444
|
-
console.error("Validation failed:", validationResult.error);
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
try {
|
|
448
|
-
const result = await createCredential.mutateAsync(data);
|
|
449
|
-
form.reset();
|
|
450
|
-
setSelectedType("api-key");
|
|
451
|
-
onSuccess(result);
|
|
452
|
-
} catch (error) {
|
|
453
|
-
console.error("Failed to create credential:", error);
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
const handleClose = () => {
|
|
457
|
-
if (!createCredential.isPending) {
|
|
458
|
-
form.reset();
|
|
459
|
-
setSelectedType("api-key");
|
|
460
|
-
onClose();
|
|
461
|
-
}
|
|
462
|
-
};
|
|
463
|
-
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: [
|
|
464
|
-
/* @__PURE__ */ jsx(Title, { order: 3, children: "Create New Credential" }),
|
|
465
|
-
/* @__PURE__ */ jsx(
|
|
466
|
-
Select,
|
|
467
|
-
{
|
|
468
|
-
label: "Credential Type",
|
|
469
|
-
description: "Select the integration type",
|
|
470
|
-
placeholder: "Select credential type",
|
|
471
|
-
data: Object.entries(CREDENTIAL_SCHEMAS).filter(([_, s]) => s.type !== "oauth").map(([key, s]) => ({ value: key, label: s.label })),
|
|
472
|
-
value: selectedType,
|
|
473
|
-
onChange: (value) => setSelectedType(value || "api-key"),
|
|
474
|
-
required: true,
|
|
475
|
-
disabled: createCredential.isPending
|
|
476
|
-
}
|
|
477
|
-
),
|
|
478
|
-
/* @__PURE__ */ jsx(
|
|
479
|
-
TextInput,
|
|
480
|
-
{
|
|
481
|
-
label: "Credential Name",
|
|
482
|
-
description: schema.nameSuggestions.length > 0 ? `Suggested: ${schema.nameSuggestions.join(", ")}` : "Lowercase letters, numbers, and hyphens only (e.g., service-dev, service-prod)",
|
|
483
|
-
placeholder: schema.nameSuggestions[0] || "e.g., service-prod",
|
|
484
|
-
leftSection: /* @__PURE__ */ jsx(IconKey, { size: 16 }),
|
|
485
|
-
maxLength: 100,
|
|
486
|
-
required: true,
|
|
487
|
-
...form.getInputProps("name"),
|
|
488
|
-
disabled: createCredential.isPending
|
|
489
|
-
}
|
|
490
|
-
),
|
|
491
|
-
schema.type === "oauth" ? /* @__PURE__ */ jsxs(Alert, { icon: /* @__PURE__ */ jsx(IconInfoCircle, { size: 16 }), color: "blue", children: [
|
|
492
|
-
schema.label,
|
|
493
|
-
" uses OAuth authentication. Please go to the OAuth Integrations page to connect your",
|
|
494
|
-
" ",
|
|
495
|
-
schema.label,
|
|
496
|
-
" account."
|
|
497
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
498
|
-
schema.fields?.map((field) => /* @__PURE__ */ jsx(
|
|
499
|
-
PasswordInput,
|
|
500
|
-
{
|
|
501
|
-
label: field.label,
|
|
502
|
-
description: field.description,
|
|
503
|
-
placeholder: field.placeholder,
|
|
504
|
-
required: field.required,
|
|
505
|
-
value: form.values[field.key] || "",
|
|
506
|
-
onChange: (e) => form.setFieldValue(field.key, e.target.value),
|
|
507
|
-
error: form.errors[field.key],
|
|
508
|
-
disabled: createCredential.isPending
|
|
509
|
-
},
|
|
510
|
-
field.key
|
|
511
|
-
)),
|
|
512
|
-
schema.docsUrl && /* @__PURE__ */ jsxs(Text, { size: "sm", c: "dimmed", children: [
|
|
513
|
-
"Get credentials from",
|
|
514
|
-
" ",
|
|
515
|
-
/* @__PURE__ */ jsx(Anchor, { href: schema.docsUrl, target: "_blank", rel: "noopener noreferrer", children: schema.docsUrl })
|
|
516
|
-
] })
|
|
517
|
-
] }),
|
|
518
|
-
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" }),
|
|
519
|
-
/* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "sm", children: [
|
|
520
|
-
/* @__PURE__ */ jsx(Button, { variant: "subtle", onClick: handleClose, disabled: createCredential.isPending, children: "Cancel" }),
|
|
521
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", loading: createCredential.isPending, leftSection: /* @__PURE__ */ jsx(IconKey, { size: 16 }), children: "Create Credential" })
|
|
522
|
-
] })
|
|
523
|
-
] }) }) });
|
|
524
|
-
}
|
|
525
|
-
function CredentialSettings({ oauthSection }) {
|
|
526
|
-
const { organizationReady } = useInitialization();
|
|
527
|
-
const [showCreateModal, setShowCreateModal] = useState(false);
|
|
528
|
-
const { data: credentials = [], isLoading } = useCredentials();
|
|
529
|
-
if (!organizationReady) return /* @__PURE__ */ jsx(AppShellLoader, {});
|
|
530
|
-
const handleCredentialCreated = () => {
|
|
531
|
-
setShowCreateModal(false);
|
|
532
|
-
};
|
|
533
|
-
return /* @__PURE__ */ jsxs(Stack, { children: [
|
|
534
|
-
/* @__PURE__ */ jsx(
|
|
535
|
-
PageTitleCaption,
|
|
536
|
-
{
|
|
537
|
-
title: "Organization Credentials",
|
|
538
|
-
caption: "Manage encrypted credentials for third-party service integrations"
|
|
539
|
-
}
|
|
540
|
-
),
|
|
541
|
-
oauthSection,
|
|
542
|
-
/* @__PURE__ */ jsxs(Paper, { withBorder: true, children: [
|
|
543
|
-
/* @__PURE__ */ jsx(
|
|
544
|
-
CardHeader,
|
|
545
|
-
{
|
|
546
|
-
icon: /* @__PURE__ */ jsx(IconKey, { size: 18 }),
|
|
547
|
-
title: "Credentials",
|
|
548
|
-
subtitle: "Configured API keys and credentials",
|
|
549
|
-
rightSection: /* @__PURE__ */ jsx(Button, { leftSection: /* @__PURE__ */ jsx(IconPlus, { size: 16 }), onClick: () => setShowCreateModal(true), variant: "light", children: "Create Credential" })
|
|
550
|
-
}
|
|
551
|
-
),
|
|
552
|
-
/* @__PURE__ */ jsx(CredentialList, { credentials, isLoading })
|
|
553
|
-
] }),
|
|
554
|
-
/* @__PURE__ */ jsx(
|
|
555
|
-
CreateCredentialModal,
|
|
556
|
-
{
|
|
557
|
-
opened: showCreateModal,
|
|
558
|
-
onClose: () => setShowCreateModal(false),
|
|
559
|
-
onSuccess: handleCredentialCreated
|
|
560
|
-
}
|
|
561
|
-
)
|
|
562
|
-
] });
|
|
563
|
-
}
|
|
564
288
|
function OAuthConnectModal({ opened, onClose, provider, onConnect, error, isLoading }) {
|
|
565
289
|
const [credentialName, setCredentialName] = useState("");
|
|
566
290
|
useEffect(() => {
|
|
@@ -598,25 +322,236 @@ function OAuthConnectModal({ opened, onClose, provider, onConnect, error, isLoad
|
|
|
598
322
|
] }),
|
|
599
323
|
error && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "red", title: "Connection Failed", children: error }),
|
|
600
324
|
/* @__PURE__ */ jsx(
|
|
601
|
-
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,
|
|
602
542
|
{
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
543
|
+
opened: modalOpened,
|
|
544
|
+
onClose: () => {
|
|
545
|
+
setModalOpened(false);
|
|
546
|
+
setSelectedProviderId(null);
|
|
547
|
+
},
|
|
548
|
+
provider: selectedProvider,
|
|
549
|
+
onConnect: handleConnect,
|
|
550
|
+
error,
|
|
551
|
+
isLoading: isAuthorizing
|
|
610
552
|
}
|
|
611
|
-
)
|
|
612
|
-
|
|
613
|
-
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: isLoading, children: "Cancel" }),
|
|
614
|
-
/* @__PURE__ */ jsxs(Button, { onClick: handleConnect, loading: isLoading, disabled: !credentialName.trim(), children: [
|
|
615
|
-
"Connect ",
|
|
616
|
-
provider.name
|
|
617
|
-
] })
|
|
618
|
-
] })
|
|
619
|
-
] }) });
|
|
553
|
+
)
|
|
554
|
+
] });
|
|
620
555
|
}
|
|
621
556
|
function MembershipStatusBadge({ status, size = "sm", variant = "light" }) {
|
|
622
557
|
const getStatusLabel = (status2) => {
|
|
@@ -1612,7 +1547,7 @@ function OrgMembersList({ orgId }) {
|
|
|
1612
1547
|
const [selectedMember, setSelectedMember] = useState(null);
|
|
1613
1548
|
const [modalOpened, setModalOpened] = useState(false);
|
|
1614
1549
|
const [defaultTab, setDefaultTab] = useState("roles");
|
|
1615
|
-
const
|
|
1550
|
+
const openModal = (member, tab) => {
|
|
1616
1551
|
setSelectedMember(member);
|
|
1617
1552
|
setDefaultTab(tab);
|
|
1618
1553
|
setModalOpened(true);
|
|
@@ -1661,7 +1596,7 @@ function OrgMembersList({ orgId }) {
|
|
|
1661
1596
|
/* @__PURE__ */ jsx(MembershipStatusBadge, { status: member.status }),
|
|
1662
1597
|
/* @__PURE__ */ jsx(ProvisioningStateBadge, { state: member.provisioningState })
|
|
1663
1598
|
] }) }),
|
|
1664
|
-
/* @__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 }) }) }) })
|
|
1665
1600
|
] }, member.id);
|
|
1666
1601
|
}) })
|
|
1667
1602
|
] }) }),
|
|
@@ -1813,145 +1748,6 @@ function MyRolesPage({ onManageMembers, onManageOrganizationRoles }) {
|
|
|
1813
1748
|
] }) }) })
|
|
1814
1749
|
] });
|
|
1815
1750
|
}
|
|
1816
|
-
var OAUTH_COMPLETION_CHANNEL = "elevasis-oauth";
|
|
1817
|
-
var OAUTH_COMPLETION_STORAGE_KEY = "elevasis:oauth-complete";
|
|
1818
|
-
function useOAuthFlow({ apiUrl }) {
|
|
1819
|
-
const [isAuthorizing, setIsAuthorizing] = useState(false);
|
|
1820
|
-
const [error, setError] = useState(null);
|
|
1821
|
-
const { isReady } = useElevasisServices();
|
|
1822
|
-
const { getAccessToken, organizationId } = useAuthContext();
|
|
1823
|
-
const authorize = useCallback(
|
|
1824
|
-
async (provider, credentialName) => {
|
|
1825
|
-
if (!isReady || !organizationId) {
|
|
1826
|
-
setError("Organization context not ready");
|
|
1827
|
-
return;
|
|
1828
|
-
}
|
|
1829
|
-
setIsAuthorizing(true);
|
|
1830
|
-
setError(null);
|
|
1831
|
-
const width = 600;
|
|
1832
|
-
const height = 700;
|
|
1833
|
-
const left = Math.round(window.screen.width / 2 - width / 2);
|
|
1834
|
-
const top = Math.round(window.screen.height / 2 - height / 2);
|
|
1835
|
-
const popup = window.open(
|
|
1836
|
-
"about:blank",
|
|
1837
|
-
`oauth-${provider}`,
|
|
1838
|
-
`width=${width},height=${height},left=${left},top=${top}`
|
|
1839
|
-
);
|
|
1840
|
-
if (!popup) {
|
|
1841
|
-
setIsAuthorizing(false);
|
|
1842
|
-
setError("Please enable popups and try again");
|
|
1843
|
-
return;
|
|
1844
|
-
}
|
|
1845
|
-
let token;
|
|
1846
|
-
try {
|
|
1847
|
-
token = await getAccessToken();
|
|
1848
|
-
} catch {
|
|
1849
|
-
popup.close();
|
|
1850
|
-
setIsAuthorizing(false);
|
|
1851
|
-
setError("Authorization failed. Please sign in again.");
|
|
1852
|
-
return;
|
|
1853
|
-
}
|
|
1854
|
-
try {
|
|
1855
|
-
const authorizeUrl = new URL(`${apiUrl}/api/oauth/authorize/${provider}`);
|
|
1856
|
-
authorizeUrl.searchParams.set("credentialName", credentialName);
|
|
1857
|
-
authorizeUrl.searchParams.set("token", token);
|
|
1858
|
-
authorizeUrl.searchParams.set("orgId", organizationId);
|
|
1859
|
-
popup.location.href = authorizeUrl.toString();
|
|
1860
|
-
await waitForOAuthCallback(popup, Date.now());
|
|
1861
|
-
} catch (err) {
|
|
1862
|
-
const rawError = err instanceof Error ? err.message : "unknown";
|
|
1863
|
-
if (rawError.includes("popup_blocked")) {
|
|
1864
|
-
setError("Please enable popups and try again");
|
|
1865
|
-
} else if (rawError.includes("timeout")) {
|
|
1866
|
-
setError("Authorization timed out. Please try again.");
|
|
1867
|
-
} else if (rawError.includes("popup_closed")) {
|
|
1868
|
-
setError("Authorization was cancelled.");
|
|
1869
|
-
} else if (rawError.includes("token_exchange_failed")) {
|
|
1870
|
-
setError("Failed to obtain access token. Please try again.");
|
|
1871
|
-
} else if (rawError.includes("invalid_state")) {
|
|
1872
|
-
setError("Security validation failed. Please try again.");
|
|
1873
|
-
} else if (rawError.includes("state_expired")) {
|
|
1874
|
-
setError("Authorization expired. Please try again.");
|
|
1875
|
-
} else {
|
|
1876
|
-
setError(`Failed to connect: ${rawError}`);
|
|
1877
|
-
}
|
|
1878
|
-
throw err;
|
|
1879
|
-
} finally {
|
|
1880
|
-
setIsAuthorizing(false);
|
|
1881
|
-
}
|
|
1882
|
-
},
|
|
1883
|
-
[isReady, organizationId, apiUrl, getAccessToken]
|
|
1884
|
-
);
|
|
1885
|
-
const waitForOAuthCallback = (popup, startedAt) => {
|
|
1886
|
-
return new Promise((resolve, reject) => {
|
|
1887
|
-
let broadcastChannel = null;
|
|
1888
|
-
const complete = (data) => {
|
|
1889
|
-
const payload = parseOAuthCompletionPayload(data, startedAt);
|
|
1890
|
-
if (!payload) return;
|
|
1891
|
-
cleanup();
|
|
1892
|
-
if (payload.status === "success") {
|
|
1893
|
-
resolve();
|
|
1894
|
-
} else {
|
|
1895
|
-
reject(new Error(payload.error || "oauth_failed"));
|
|
1896
|
-
}
|
|
1897
|
-
};
|
|
1898
|
-
const messageHandler = (event) => {
|
|
1899
|
-
if (!isTrustedCallbackOrigin(event.origin)) return;
|
|
1900
|
-
complete(event.data);
|
|
1901
|
-
};
|
|
1902
|
-
const storageHandler = (event) => {
|
|
1903
|
-
if (event.key !== OAUTH_COMPLETION_STORAGE_KEY || !event.newValue) return;
|
|
1904
|
-
try {
|
|
1905
|
-
complete(JSON.parse(event.newValue));
|
|
1906
|
-
} catch {
|
|
1907
|
-
}
|
|
1908
|
-
};
|
|
1909
|
-
const rejectIfPopupClosed = () => {
|
|
1910
|
-
if (popup.closed) {
|
|
1911
|
-
cleanup();
|
|
1912
|
-
reject(new Error("popup_closed"));
|
|
1913
|
-
}
|
|
1914
|
-
};
|
|
1915
|
-
window.addEventListener("message", messageHandler);
|
|
1916
|
-
window.addEventListener("storage", storageHandler);
|
|
1917
|
-
window.addEventListener("focus", rejectIfPopupClosed);
|
|
1918
|
-
if ("BroadcastChannel" in window) {
|
|
1919
|
-
broadcastChannel = new BroadcastChannel(OAUTH_COMPLETION_CHANNEL);
|
|
1920
|
-
broadcastChannel.onmessage = (event) => complete(event.data);
|
|
1921
|
-
}
|
|
1922
|
-
const timeout = setTimeout(() => {
|
|
1923
|
-
cleanup();
|
|
1924
|
-
reject(new Error("timeout"));
|
|
1925
|
-
}, OAUTH_FLOW_TIMEOUT);
|
|
1926
|
-
const cleanup = () => {
|
|
1927
|
-
clearTimeout(timeout);
|
|
1928
|
-
broadcastChannel?.close();
|
|
1929
|
-
window.removeEventListener("focus", rejectIfPopupClosed);
|
|
1930
|
-
window.removeEventListener("storage", storageHandler);
|
|
1931
|
-
window.removeEventListener("message", messageHandler);
|
|
1932
|
-
};
|
|
1933
|
-
});
|
|
1934
|
-
};
|
|
1935
|
-
return { authorize, isAuthorizing, error };
|
|
1936
|
-
}
|
|
1937
|
-
function parseOAuthCompletionPayload(data, startedAt) {
|
|
1938
|
-
if (!data || typeof data !== "object") return null;
|
|
1939
|
-
const payload = data;
|
|
1940
|
-
if (payload.type !== "oauth-complete") return null;
|
|
1941
|
-
if (payload.completedAt && payload.completedAt < startedAt) return null;
|
|
1942
|
-
return payload;
|
|
1943
|
-
}
|
|
1944
|
-
function isTrustedCallbackOrigin(origin) {
|
|
1945
|
-
if (origin === window.location.origin || origin === "https://app.elevasis.io") {
|
|
1946
|
-
return true;
|
|
1947
|
-
}
|
|
1948
|
-
try {
|
|
1949
|
-
const url = new URL(origin);
|
|
1950
|
-
return url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
1951
|
-
} catch {
|
|
1952
|
-
return false;
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
1751
|
function EditCredentialModal({ apiUrl, credential, onClose }) {
|
|
1956
1752
|
const [credentialName, setCredentialName] = useState("");
|
|
1957
1753
|
const [nameError, setNameError] = useState(null);
|
|
@@ -2088,18 +1884,21 @@ function EditCredentialModal({ apiUrl, credential, onClose }) {
|
|
|
2088
1884
|
] }),
|
|
2089
1885
|
!isOAuth && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2090
1886
|
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "Current values are encrypted and cannot be displayed. Enter new values below to replace them." }),
|
|
2091
|
-
schema.fields?.map((field) =>
|
|
2092
|
-
PasswordInput
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
1887
|
+
schema.fields?.map((field) => {
|
|
1888
|
+
const Input = field.type === "text" ? TextInput : PasswordInput;
|
|
1889
|
+
return /* @__PURE__ */ jsx(
|
|
1890
|
+
Input,
|
|
1891
|
+
{
|
|
1892
|
+
label: `New ${field.label}`,
|
|
1893
|
+
placeholder: field.placeholder || `Enter new ${field.label.toLowerCase()}`,
|
|
1894
|
+
value: fieldValues[field.key] || "",
|
|
1895
|
+
onChange: (e) => handleFieldChange(field.key, e.target.value),
|
|
1896
|
+
description: field.description,
|
|
1897
|
+
disabled: isPending
|
|
1898
|
+
},
|
|
1899
|
+
field.key
|
|
1900
|
+
);
|
|
1901
|
+
}),
|
|
2103
1902
|
updateMutation.error && /* @__PURE__ */ jsx(Alert, { icon: /* @__PURE__ */ jsx(IconAlertCircle, { size: 16 }), color: "red", title: "Update Failed", children: "Failed to update credential" }),
|
|
2104
1903
|
/* @__PURE__ */ jsxs(Group, { justify: "space-between", gap: "sm", children: [
|
|
2105
1904
|
/* @__PURE__ */ jsx(Button, { variant: "light", onClick: handleClose, disabled: isPending, children: "Cancel" }),
|
|
@@ -2120,75 +1919,5 @@ function EditCredentialModal({ apiUrl, credential, onClose }) {
|
|
|
2120
1919
|
] })
|
|
2121
1920
|
] }) });
|
|
2122
1921
|
}
|
|
2123
|
-
var PROVIDER_ICONS = {
|
|
2124
|
-
"google-sheets": IconBrandGoogleDrive,
|
|
2125
|
-
dropbox: IconBrandDropbox
|
|
2126
|
-
};
|
|
2127
|
-
function OAuthIntegrationsCard({ apiUrl }) {
|
|
2128
|
-
const [selectedProviderId, setSelectedProviderId] = useState(null);
|
|
2129
|
-
const [modalOpened, setModalOpened] = useState(false);
|
|
2130
|
-
const { authorize, isAuthorizing, error } = useOAuthFlow({ apiUrl });
|
|
2131
|
-
const queryClient = useQueryClient();
|
|
2132
|
-
const handleConnect = async (credentialName) => {
|
|
2133
|
-
if (!selectedProviderId) return;
|
|
2134
|
-
try {
|
|
2135
|
-
await authorize(selectedProviderId, credentialName);
|
|
2136
|
-
setModalOpened(false);
|
|
2137
|
-
setSelectedProviderId(null);
|
|
2138
|
-
queryClient.invalidateQueries({ queryKey: ["credentials"] });
|
|
2139
|
-
} catch (err) {
|
|
2140
|
-
console.error("OAuth flow failed:", err);
|
|
2141
|
-
}
|
|
2142
|
-
};
|
|
2143
|
-
const handleOpenModal = (providerId) => {
|
|
2144
|
-
setSelectedProviderId(providerId);
|
|
2145
|
-
setModalOpened(true);
|
|
2146
|
-
};
|
|
2147
|
-
useEffect(() => {
|
|
2148
|
-
const params = new URLSearchParams(window.location.search);
|
|
2149
|
-
const requested = params.get("connect");
|
|
2150
|
-
if (requested && OAUTH_PROVIDERS[requested]) {
|
|
2151
|
-
handleOpenModal(requested);
|
|
2152
|
-
params.delete("connect");
|
|
2153
|
-
const remaining = params.toString();
|
|
2154
|
-
const newUrl = window.location.pathname + (remaining ? `?${remaining}` : "") + window.location.hash;
|
|
2155
|
-
window.history.replaceState({}, "", newUrl);
|
|
2156
|
-
}
|
|
2157
|
-
}, []);
|
|
2158
|
-
const providers = Object.values(OAUTH_PROVIDERS);
|
|
2159
|
-
const selectedProvider = selectedProviderId ? OAUTH_PROVIDERS[selectedProviderId] : null;
|
|
2160
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2161
|
-
/* @__PURE__ */ jsx(Paper, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
2162
|
-
/* @__PURE__ */ jsx(Group, { justify: "space-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
2163
|
-
/* @__PURE__ */ jsx(Title, { order: 3, children: "OAuth Integrations" }),
|
|
2164
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", mt: 4, children: "Connect third-party services using OAuth 2.0" })
|
|
2165
|
-
] }) }),
|
|
2166
|
-
/* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2, md: 3 }, children: providers.map((provider) => {
|
|
2167
|
-
const Icon = PROVIDER_ICONS[provider.id] || IconPlug;
|
|
2168
|
-
return /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { children: [
|
|
2169
|
-
/* @__PURE__ */ jsx(Group, { justify: "space-between", align: "flex-start", children: /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
|
|
2170
|
-
/* @__PURE__ */ jsx(ThemeIcon, { size: "lg", variant: "light", color: "blue", children: /* @__PURE__ */ jsx(Icon, { size: 20 }) }),
|
|
2171
|
-
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { fw: 600, style: { fontFamily: "var(--mantine-font-family-headings)" }, children: provider.name }) })
|
|
2172
|
-
] }) }),
|
|
2173
|
-
/* @__PURE__ */ jsx(Button, { variant: "filled", size: "sm", onClick: () => handleOpenModal(provider.id), fullWidth: true, children: "Connect" })
|
|
2174
|
-
] }) }, provider.id);
|
|
2175
|
-
}) })
|
|
2176
|
-
] }) }),
|
|
2177
|
-
/* @__PURE__ */ jsx(
|
|
2178
|
-
OAuthConnectModal,
|
|
2179
|
-
{
|
|
2180
|
-
opened: modalOpened,
|
|
2181
|
-
onClose: () => {
|
|
2182
|
-
setModalOpened(false);
|
|
2183
|
-
setSelectedProviderId(null);
|
|
2184
|
-
},
|
|
2185
|
-
provider: selectedProvider,
|
|
2186
|
-
onConnect: handleConnect,
|
|
2187
|
-
error,
|
|
2188
|
-
isLoading: isAuthorizing
|
|
2189
|
-
}
|
|
2190
|
-
)
|
|
2191
|
-
] });
|
|
2192
|
-
}
|
|
2193
1922
|
|
|
2194
|
-
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 };
|