@gorgo/medusa-integration 0.1.4 → 0.1.5
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/.medusa/server/src/admin/index.js +698 -688
- package/.medusa/server/src/admin/index.mjs +700 -690
- package/.medusa/server/src/modules/integration/descriptor/__tests__/build-zod.unit.spec.js +30 -1
- package/.medusa/server/src/modules/integration/descriptor/__tests__/define.unit.spec.js +34 -1
- package/.medusa/server/src/modules/integration/descriptor/build-zod.js +11 -3
- package/.medusa/server/src/modules/integration/descriptor/define.js +13 -1
- package/.medusa/server/src/modules/integration/descriptor/option.d.ts +2 -2
- package/.medusa/server/src/types/integration/common.d.ts +1 -1
- package/README.md +2 -2
- package/package.json +1 -1
|
@@ -291,372 +291,85 @@ const IntegrationsBreadcrumb = () => {
|
|
|
291
291
|
const handle$2 = {
|
|
292
292
|
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(IntegrationsBreadcrumb, {})
|
|
293
293
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
const Dash = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "text-ui-fg-subtle", children: "-" });
|
|
295
|
+
const IntegrationFieldValue = ({
|
|
296
|
+
field,
|
|
297
|
+
value,
|
|
298
|
+
secretConfigured = false
|
|
299
|
+
}) => {
|
|
300
|
+
var _a;
|
|
301
|
+
const { t } = reactI18next.useTranslation();
|
|
302
|
+
if (field.secret || field.control === "secret") {
|
|
303
|
+
return secretConfigured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "••••••••" }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
299
304
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
function parseInline(src, depth = 0) {
|
|
304
|
-
if (!src) return [];
|
|
305
|
-
if (depth >= MAX_DEPTH) return [{ type: "text", value: src }];
|
|
306
|
-
const out = [];
|
|
307
|
-
const re = new RegExp(INLINE_RE, "g");
|
|
308
|
-
let last = 0;
|
|
309
|
-
let m;
|
|
310
|
-
while ((m = re.exec(src)) !== null) {
|
|
311
|
-
const [full, code, linkText, linkHref, strong, star, underscore] = m;
|
|
312
|
-
if (m.index > last) out.push({ type: "text", value: src.slice(last, m.index) });
|
|
313
|
-
last = m.index + full.length;
|
|
314
|
-
if (code !== void 0) {
|
|
315
|
-
out.push({ type: "code", value: code });
|
|
316
|
-
} else if (linkHref !== void 0) {
|
|
317
|
-
const isImage = m.index > 0 && src[m.index - 1] === "!";
|
|
318
|
-
if (!isImage && isSafeHref(linkHref)) {
|
|
319
|
-
out.push({ type: "link", href: linkHref, children: parseInline(linkText, depth + 1) });
|
|
320
|
-
} else {
|
|
321
|
-
out.push({ type: "text", value: full });
|
|
322
|
-
}
|
|
323
|
-
} else if (strong !== void 0) {
|
|
324
|
-
out.push({ type: "strong", children: parseInline(strong, depth + 1) });
|
|
325
|
-
} else {
|
|
326
|
-
out.push({ type: "em", children: parseInline(star ?? underscore, depth + 1) });
|
|
327
|
-
}
|
|
305
|
+
if (field.control === "switch") {
|
|
306
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
307
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: value ? "green" : "grey", children: value ? t("integration.status.enabled") : t("integration.status.disabled") });
|
|
328
308
|
}
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
const MD_MAX_CHARS = 8e3;
|
|
333
|
-
const MD_MAX_BLOCKS = 200;
|
|
334
|
-
const FENCE_RE = /^```(.*)$/;
|
|
335
|
-
const HEADING_RE = /^(#{1,3})\s+(.*)$/;
|
|
336
|
-
const HR_RE = /^-{3,}\s*$/;
|
|
337
|
-
const UL_RE = /^[-*]\s+(.*)$/;
|
|
338
|
-
const OL_RE = /^\d+\.\s+(.*)$/;
|
|
339
|
-
function fenceLabel(info) {
|
|
340
|
-
const first = info.trim().split(/\s+/)[0] ?? "";
|
|
341
|
-
return first.replace(/[^a-zA-Z0-9._/-]/g, "").slice(0, 40) || null;
|
|
342
|
-
}
|
|
343
|
-
function parseMarkdown(src) {
|
|
344
|
-
if (typeof src !== "string" || !src) return [];
|
|
345
|
-
if (src.length > MD_MAX_CHARS) return [{ type: "truncated" }];
|
|
346
|
-
const lines = src.replace(/\r\n?/g, "\n").split("\n");
|
|
347
|
-
const blocks = [];
|
|
348
|
-
let para = [];
|
|
349
|
-
const flushPara = () => {
|
|
350
|
-
if (!para.length) return;
|
|
351
|
-
blocks.push({ type: "paragraph", children: parseInline(para.join(" ")) });
|
|
352
|
-
para = [];
|
|
353
|
-
};
|
|
354
|
-
for (let i = 0; i < lines.length; i++) {
|
|
355
|
-
const line = lines[i];
|
|
356
|
-
const fence = FENCE_RE.exec(line);
|
|
357
|
-
if (fence) {
|
|
358
|
-
flushPara();
|
|
359
|
-
const body = [];
|
|
360
|
-
i++;
|
|
361
|
-
while (i < lines.length && !FENCE_RE.test(lines[i])) body.push(lines[i++]);
|
|
362
|
-
blocks.push({ type: "codeBlock", label: fenceLabel(fence[1]), value: body.join("\n") });
|
|
363
|
-
continue;
|
|
364
|
-
}
|
|
365
|
-
if (!line.trim()) {
|
|
366
|
-
flushPara();
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
if (HR_RE.test(line)) {
|
|
370
|
-
flushPara();
|
|
371
|
-
blocks.push({ type: "hr" });
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
const heading = HEADING_RE.exec(line);
|
|
375
|
-
if (heading) {
|
|
376
|
-
flushPara();
|
|
377
|
-
blocks.push({
|
|
378
|
-
type: "heading",
|
|
379
|
-
level: heading[1].length + 1,
|
|
380
|
-
children: parseInline(heading[2])
|
|
381
|
-
});
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
const ul = UL_RE.exec(line);
|
|
385
|
-
const ol = ul ? null : OL_RE.exec(line);
|
|
386
|
-
if (ul || ol) {
|
|
387
|
-
flushPara();
|
|
388
|
-
const ordered = !!ol;
|
|
389
|
-
const itemRe = ordered ? OL_RE : UL_RE;
|
|
390
|
-
const items = [];
|
|
391
|
-
while (i < lines.length) {
|
|
392
|
-
const item = itemRe.exec(lines[i]);
|
|
393
|
-
if (!item) break;
|
|
394
|
-
items.push(parseInline(item[1]));
|
|
395
|
-
i++;
|
|
396
|
-
}
|
|
397
|
-
i--;
|
|
398
|
-
blocks.push({ type: "list", ordered, items });
|
|
399
|
-
continue;
|
|
309
|
+
if (field.control === "json") {
|
|
310
|
+
if (Array.isArray(value)) {
|
|
311
|
+
return value.length ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.form.items", { count: value.length }) }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
400
312
|
}
|
|
401
|
-
|
|
313
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
314
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.status.configured") });
|
|
402
315
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
const Inline = ({ nodes }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: nodes.map((node, i) => {
|
|
408
|
-
switch (node.type) {
|
|
409
|
-
case "text":
|
|
410
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.value }, i);
|
|
411
|
-
case "code":
|
|
412
|
-
return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "txt-compact-small bg-ui-bg-component rounded px-1 py-0.5", children: node.value }, i);
|
|
413
|
-
case "strong":
|
|
414
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-small-plus", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
415
|
-
case "em":
|
|
416
|
-
return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
417
|
-
case "link":
|
|
418
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
419
|
-
"a",
|
|
420
|
-
{
|
|
421
|
-
href: node.href,
|
|
422
|
-
target: "_blank",
|
|
423
|
-
rel: "noopener noreferrer",
|
|
424
|
-
className: "text-ui-fg-interactive hover:underline",
|
|
425
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children })
|
|
426
|
-
},
|
|
427
|
-
i
|
|
428
|
-
);
|
|
316
|
+
if (value == null || value === "") return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
317
|
+
if (field.control === "select") {
|
|
318
|
+
const labelKey = (_a = field.optionLabels) == null ? void 0 : _a[String(value)];
|
|
319
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: labelKey ? t(labelKey) : String(value) });
|
|
429
320
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
321
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: String(value) });
|
|
322
|
+
};
|
|
323
|
+
var isCheckBoxInput = (element) => element.type === "checkbox";
|
|
324
|
+
var isDateObject = (value) => value instanceof Date;
|
|
325
|
+
var isNullOrUndefined = (value) => value == null;
|
|
326
|
+
const isObjectType = (value) => typeof value === "object";
|
|
327
|
+
var isObject = (value) => !isNullOrUndefined(value) && !Array.isArray(value) && isObjectType(value) && !isDateObject(value);
|
|
328
|
+
var getEventValue = (event) => isObject(event) && event.target ? isCheckBoxInput(event.target) ? event.target.checked : event.target.value : event;
|
|
329
|
+
var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
|
|
330
|
+
var isNameInFieldArray = (names, name) => names.has(getNodeParentName(name));
|
|
331
|
+
var isPlainObject = (tempObject) => {
|
|
332
|
+
const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
|
|
333
|
+
return isObject(prototypeCopy) && prototypeCopy.hasOwnProperty("isPrototypeOf");
|
|
334
|
+
};
|
|
335
|
+
var isWeb = typeof window !== "undefined" && typeof window.HTMLElement !== "undefined" && typeof document !== "undefined";
|
|
336
|
+
function cloneObject(data) {
|
|
337
|
+
let copy;
|
|
338
|
+
const isArray = Array.isArray(data);
|
|
339
|
+
if (data instanceof Date) {
|
|
340
|
+
copy = new Date(data);
|
|
341
|
+
} else if (data instanceof Set) {
|
|
342
|
+
copy = new Set(data);
|
|
343
|
+
} else if (!(isWeb && (data instanceof Blob || data instanceof FileList)) && (isArray || isObject(data))) {
|
|
344
|
+
copy = isArray ? [] : {};
|
|
345
|
+
if (!isArray && !isPlainObject(data)) {
|
|
346
|
+
copy = data;
|
|
347
|
+
} else {
|
|
348
|
+
for (const key in data) {
|
|
349
|
+
if (data.hasOwnProperty(key)) {
|
|
350
|
+
copy[key] = cloneObject(data[key]);
|
|
444
351
|
}
|
|
445
|
-
|
|
352
|
+
}
|
|
446
353
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
case "truncated":
|
|
458
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: truncatedLabel });
|
|
354
|
+
} else {
|
|
355
|
+
return data;
|
|
356
|
+
}
|
|
357
|
+
return copy;
|
|
358
|
+
}
|
|
359
|
+
var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
|
|
360
|
+
var isUndefined = (val) => val === void 0;
|
|
361
|
+
var get = (object, path, defaultValue) => {
|
|
362
|
+
if (!path || !isObject(object)) {
|
|
363
|
+
return defaultValue;
|
|
459
364
|
}
|
|
365
|
+
const result = compact(path.split(/[,[\].]+?/)).reduce((result2, key) => isNullOrUndefined(result2) ? result2 : result2[key], object);
|
|
366
|
+
return isUndefined(result) || result === object ? isUndefined(object[path]) ? defaultValue : object[path] : result;
|
|
460
367
|
};
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (!blocks.length) return null;
|
|
467
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-y-4", children: blocks.map((block, i) => /* @__PURE__ */ jsxRuntime.jsx(Block, { block, truncatedLabel }, i)) });
|
|
468
|
-
};
|
|
469
|
-
const PUBLISH_URL = "https://docs.gorgojs.com/medusa-modules/integration";
|
|
470
|
-
const API_URL = "https://gorgojs.com";
|
|
471
|
-
const iconSrc = (icon) => icon.startsWith("data:") ? icon : API_URL + icon;
|
|
472
|
-
const InstallModal = ({
|
|
473
|
-
item,
|
|
474
|
-
onOpenChange
|
|
475
|
-
}) => {
|
|
476
|
-
var _a, _b, _c;
|
|
477
|
-
const { t, i18n } = reactI18next.useTranslation();
|
|
478
|
-
const lang = i18n.language.split("-")[0] === "ru" ? "ru" : "en";
|
|
479
|
-
const source = ((_a = item == null ? void 0 : item.docsSnippet) == null ? void 0 : _a[lang]) || ((_b = item == null ? void 0 : item.docsSnippet) == null ? void 0 : _b.en) || ((_c = item == null ? void 0 : item.docsSnippet) == null ? void 0 : _c.ru) || "";
|
|
480
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: !!item, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
481
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
482
|
-
item && /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label, size: "small" }),
|
|
483
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: item == null ? void 0 : item.label })
|
|
484
|
-
] }) }) }),
|
|
485
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { className: "flex flex-1 flex-col gap-y-4 overflow-auto", children: source.trim() ? /* @__PURE__ */ jsxRuntime.jsx(Markdown, { source, truncatedLabel: t("integration.browse.install_truncated") }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.install_empty") }) }),
|
|
486
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
487
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: t("integration.actions.cancel") }) }),
|
|
488
|
-
(item == null ? void 0 : item.docsUrl) && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "primary", asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: item.docsUrl, target: "_blank", rel: "noopener noreferrer", children: [
|
|
489
|
-
t("integration.browse.install_docs"),
|
|
490
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, {})
|
|
491
|
-
] }) })
|
|
492
|
-
] })
|
|
493
|
-
] }) });
|
|
494
|
-
};
|
|
495
|
-
const BrowsePage = () => {
|
|
496
|
-
const { t } = reactI18next.useTranslation();
|
|
497
|
-
const navigate = reactRouterDom.useNavigate();
|
|
498
|
-
const [activeTab, setActiveTab] = React.useState("all");
|
|
499
|
-
const [search, setSearch] = React.useState("");
|
|
500
|
-
const [installItem, setInstallItem] = React.useState(null);
|
|
501
|
-
const { data, isLoading } = reactQuery.useQuery({
|
|
502
|
-
queryKey: ["integration-catalog"],
|
|
503
|
-
queryFn: () => sdk.client.fetch("/admin/integrations/catalog")
|
|
504
|
-
});
|
|
505
|
-
const items = (data == null ? void 0 : data.integrations) ?? [];
|
|
506
|
-
const categories = React.useMemo(() => [...new Set(items.map((i) => i.category))], [items]);
|
|
507
|
-
const q = search.trim().toLowerCase();
|
|
508
|
-
const filtered = items.filter((i) => {
|
|
509
|
-
if (activeTab !== "all" && i.category !== activeTab) return false;
|
|
510
|
-
if (q && ![i.label, i.shortDescription, i.npm].some((s) => s.toLowerCase().includes(q))) return false;
|
|
511
|
-
return true;
|
|
512
|
-
});
|
|
513
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3", children: [
|
|
514
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
515
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
516
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
517
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("integration.browse.title") }),
|
|
518
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.subtitle") })
|
|
519
|
-
] }),
|
|
520
|
-
false
|
|
521
|
-
] }),
|
|
522
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 px-6 py-4", children: [
|
|
523
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs, { value: activeTab, onValueChange: setActiveTab, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
|
|
524
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "all", children: t("integration.list.tabs.all") }),
|
|
525
|
-
categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: c, children: t(`integration.categories.${c}`) }, c))
|
|
526
|
-
] }) }),
|
|
527
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-64", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
528
|
-
ui.Input,
|
|
529
|
-
{
|
|
530
|
-
placeholder: t("integration.browse.searchPlaceholder"),
|
|
531
|
-
value: search,
|
|
532
|
-
onChange: (e) => setSearch(e.target.value)
|
|
533
|
-
}
|
|
534
|
-
) })
|
|
535
|
-
] })
|
|
536
|
-
] }),
|
|
537
|
-
isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Spinner, { className: "text-ui-fg-subtle animate-spin" }) }) : filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.list.emptyFiltered") }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4", children: filtered.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shadow-borders-base bg-ui-bg-base flex flex-col rounded-lg", children: [
|
|
538
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-y-3 p-4", children: [
|
|
539
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-x-2", children: [
|
|
540
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
541
|
-
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label }),
|
|
542
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
543
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: item.label }),
|
|
544
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: t("integration.browse.by", { author: item.authorLocalized }) })
|
|
545
|
-
] })
|
|
546
|
-
] }),
|
|
547
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: item.installed ? "green" : "grey", children: item.installed ? t("integration.browse.installed") : t("integration.browse.notInstalled") })
|
|
548
|
-
] }),
|
|
549
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle line-clamp-2", children: item.shortDescription })
|
|
550
|
-
] }),
|
|
551
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
|
|
552
|
-
item.installed && item.provider_id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
553
|
-
ui.Button,
|
|
554
|
-
{
|
|
555
|
-
size: "small",
|
|
556
|
-
variant: "primary",
|
|
557
|
-
onClick: () => navigate(`/settings/integrations/${item.provider_id}`),
|
|
558
|
-
children: t("integration.browse.settings")
|
|
559
|
-
}
|
|
560
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", variant: "secondary", onClick: () => setInstallItem(item), children: [
|
|
561
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
562
|
-
t("integration.browse.add")
|
|
563
|
-
] }),
|
|
564
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(`integration.categories.${item.category}`) })
|
|
565
|
-
] })
|
|
566
|
-
] }, item.integrationId)) }),
|
|
567
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: PUBLISH_URL, target: "_blank", rel: "noreferrer", className: "text-ui-fg-base txt-small", children: [
|
|
568
|
-
t("integration.browse.learnMore"),
|
|
569
|
-
" →"
|
|
570
|
-
] }) }),
|
|
571
|
-
/* @__PURE__ */ jsxRuntime.jsx(InstallModal, { item: installItem, onOpenChange: (open) => !open && setInstallItem(null) })
|
|
572
|
-
] });
|
|
573
|
-
};
|
|
574
|
-
const BrowseBreadcrumb = () => {
|
|
575
|
-
const { t } = reactI18next.useTranslation();
|
|
576
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("integration.browse.title") });
|
|
577
|
-
};
|
|
578
|
-
const handle$1 = {
|
|
579
|
-
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(BrowseBreadcrumb, {})
|
|
580
|
-
};
|
|
581
|
-
const Dash = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "text-ui-fg-subtle", children: "-" });
|
|
582
|
-
const IntegrationFieldValue = ({
|
|
583
|
-
field,
|
|
584
|
-
value,
|
|
585
|
-
secretConfigured = false
|
|
586
|
-
}) => {
|
|
587
|
-
var _a;
|
|
588
|
-
const { t } = reactI18next.useTranslation();
|
|
589
|
-
if (field.secret || field.control === "secret") {
|
|
590
|
-
return secretConfigured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "••••••••" }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
591
|
-
}
|
|
592
|
-
if (field.control === "switch") {
|
|
593
|
-
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
594
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: value ? "green" : "grey", children: value ? t("integration.status.enabled") : t("integration.status.disabled") });
|
|
595
|
-
}
|
|
596
|
-
if (field.control === "json") {
|
|
597
|
-
if (Array.isArray(value)) {
|
|
598
|
-
return value.length ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.form.items", { count: value.length }) }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
599
|
-
}
|
|
600
|
-
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
601
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.status.configured") });
|
|
602
|
-
}
|
|
603
|
-
if (value == null || value === "") return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
604
|
-
if (field.control === "select") {
|
|
605
|
-
const labelKey = (_a = field.optionLabels) == null ? void 0 : _a[String(value)];
|
|
606
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: labelKey ? t(labelKey) : String(value) });
|
|
607
|
-
}
|
|
608
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: String(value) });
|
|
609
|
-
};
|
|
610
|
-
var isCheckBoxInput = (element) => element.type === "checkbox";
|
|
611
|
-
var isDateObject = (value) => value instanceof Date;
|
|
612
|
-
var isNullOrUndefined = (value) => value == null;
|
|
613
|
-
const isObjectType = (value) => typeof value === "object";
|
|
614
|
-
var isObject = (value) => !isNullOrUndefined(value) && !Array.isArray(value) && isObjectType(value) && !isDateObject(value);
|
|
615
|
-
var getEventValue = (event) => isObject(event) && event.target ? isCheckBoxInput(event.target) ? event.target.checked : event.target.value : event;
|
|
616
|
-
var getNodeParentName = (name) => name.substring(0, name.search(/\.\d+(\.|$)/)) || name;
|
|
617
|
-
var isNameInFieldArray = (names, name) => names.has(getNodeParentName(name));
|
|
618
|
-
var isPlainObject = (tempObject) => {
|
|
619
|
-
const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
|
|
620
|
-
return isObject(prototypeCopy) && prototypeCopy.hasOwnProperty("isPrototypeOf");
|
|
621
|
-
};
|
|
622
|
-
var isWeb = typeof window !== "undefined" && typeof window.HTMLElement !== "undefined" && typeof document !== "undefined";
|
|
623
|
-
function cloneObject(data) {
|
|
624
|
-
let copy;
|
|
625
|
-
const isArray = Array.isArray(data);
|
|
626
|
-
if (data instanceof Date) {
|
|
627
|
-
copy = new Date(data);
|
|
628
|
-
} else if (data instanceof Set) {
|
|
629
|
-
copy = new Set(data);
|
|
630
|
-
} else if (!(isWeb && (data instanceof Blob || data instanceof FileList)) && (isArray || isObject(data))) {
|
|
631
|
-
copy = isArray ? [] : {};
|
|
632
|
-
if (!isArray && !isPlainObject(data)) {
|
|
633
|
-
copy = data;
|
|
634
|
-
} else {
|
|
635
|
-
for (const key in data) {
|
|
636
|
-
if (data.hasOwnProperty(key)) {
|
|
637
|
-
copy[key] = cloneObject(data[key]);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
} else {
|
|
642
|
-
return data;
|
|
643
|
-
}
|
|
644
|
-
return copy;
|
|
645
|
-
}
|
|
646
|
-
var compact = (value) => Array.isArray(value) ? value.filter(Boolean) : [];
|
|
647
|
-
var isUndefined = (val) => val === void 0;
|
|
648
|
-
var get = (object, path, defaultValue) => {
|
|
649
|
-
if (!path || !isObject(object)) {
|
|
650
|
-
return defaultValue;
|
|
651
|
-
}
|
|
652
|
-
const result = compact(path.split(/[,[\].]+?/)).reduce((result2, key) => isNullOrUndefined(result2) ? result2 : result2[key], object);
|
|
653
|
-
return isUndefined(result) || result === object ? isUndefined(object[path]) ? defaultValue : object[path] : result;
|
|
654
|
-
};
|
|
655
|
-
var isBoolean = (value) => typeof value === "boolean";
|
|
656
|
-
const EVENTS = {
|
|
657
|
-
BLUR: "blur",
|
|
658
|
-
FOCUS_OUT: "focusout",
|
|
659
|
-
CHANGE: "change"
|
|
368
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
369
|
+
const EVENTS = {
|
|
370
|
+
BLUR: "blur",
|
|
371
|
+
FOCUS_OUT: "focusout",
|
|
372
|
+
CHANGE: "change"
|
|
660
373
|
};
|
|
661
374
|
const VALIDATION_MODE = {
|
|
662
375
|
onBlur: "onBlur",
|
|
@@ -2234,358 +1947,655 @@ const IntegrationField = ({
|
|
|
2234
1947
|
] });
|
|
2235
1948
|
}
|
|
2236
1949
|
}
|
|
2237
|
-
),
|
|
2238
|
-
field.hint && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: t(field.hint) })
|
|
2239
|
-
] });
|
|
2240
|
-
};
|
|
2241
|
-
function renderControl(field, f, secretConfigured, t) {
|
|
2242
|
-
const placeholder = field.placeholder ? t(field.placeholder) : void 0;
|
|
2243
|
-
const disabled = field.readonly === true;
|
|
2244
|
-
switch (field.control) {
|
|
2245
|
-
case "switch":
|
|
2246
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: !!f.value, onCheckedChange: f.onChange, disabled });
|
|
2247
|
-
case "select":
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
1950
|
+
),
|
|
1951
|
+
field.hint && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: t(field.hint) })
|
|
1952
|
+
] });
|
|
1953
|
+
};
|
|
1954
|
+
function renderControl(field, f, secretConfigured, t) {
|
|
1955
|
+
const placeholder = field.placeholder ? t(field.placeholder) : void 0;
|
|
1956
|
+
const disabled = field.readonly === true;
|
|
1957
|
+
switch (field.control) {
|
|
1958
|
+
case "switch":
|
|
1959
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: !!f.value, onCheckedChange: f.onChange, disabled });
|
|
1960
|
+
case "select": {
|
|
1961
|
+
const declared = field.options ?? [];
|
|
1962
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1963
|
+
ui.Select,
|
|
1964
|
+
{
|
|
1965
|
+
value: f.value == null ? "" : String(f.value),
|
|
1966
|
+
onValueChange: (next) => f.onChange(declared.find((o) => String(o) === next) ?? next),
|
|
1967
|
+
disabled,
|
|
1968
|
+
children: [
|
|
1969
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder }) }),
|
|
1970
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: declared.map((o) => {
|
|
1971
|
+
var _a;
|
|
1972
|
+
const labelKey = (_a = field.optionLabels) == null ? void 0 : _a[String(o)];
|
|
1973
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: String(o), children: labelKey ? t(labelKey) : String(o) }, String(o));
|
|
1974
|
+
}) })
|
|
1975
|
+
]
|
|
1976
|
+
}
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
case "number":
|
|
1980
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1981
|
+
ui.Input,
|
|
1982
|
+
{
|
|
1983
|
+
type: "number",
|
|
1984
|
+
placeholder,
|
|
1985
|
+
value: f.value ?? "",
|
|
1986
|
+
disabled,
|
|
1987
|
+
onChange: (e) => f.onChange(e.target.value === "" ? null : e.target.valueAsNumber)
|
|
1988
|
+
}
|
|
1989
|
+
);
|
|
1990
|
+
case "url":
|
|
1991
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1992
|
+
ui.Input,
|
|
1993
|
+
{
|
|
1994
|
+
type: "url",
|
|
1995
|
+
placeholder,
|
|
1996
|
+
value: f.value ?? "",
|
|
1997
|
+
disabled,
|
|
1998
|
+
onChange: f.onChange
|
|
1999
|
+
}
|
|
2000
|
+
);
|
|
2001
|
+
case "secret":
|
|
2002
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2003
|
+
ui.Input,
|
|
2004
|
+
{
|
|
2005
|
+
type: "password",
|
|
2006
|
+
placeholder: secretConfigured ? t("integration.form.keepSecret") : placeholder ?? "",
|
|
2007
|
+
value: f.value ?? "",
|
|
2008
|
+
disabled,
|
|
2009
|
+
onChange: f.onChange,
|
|
2010
|
+
className: !f.value ? "[&~div]:hidden" : void 0
|
|
2011
|
+
}
|
|
2012
|
+
);
|
|
2013
|
+
case "json":
|
|
2014
|
+
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { field, f });
|
|
2015
|
+
default:
|
|
2016
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2017
|
+
ui.Input,
|
|
2018
|
+
{
|
|
2019
|
+
placeholder,
|
|
2020
|
+
value: f.value ?? "",
|
|
2021
|
+
disabled,
|
|
2022
|
+
onChange: f.onChange
|
|
2023
|
+
}
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
function isFieldVisible(field, values) {
|
|
2028
|
+
const rule = field.visibleWhen;
|
|
2029
|
+
if (!rule) return true;
|
|
2030
|
+
return values[rule.field] === rule.equals;
|
|
2031
|
+
}
|
|
2032
|
+
function sectionDefaults(section, values) {
|
|
2033
|
+
const out = {};
|
|
2034
|
+
for (const f of section.fields) {
|
|
2035
|
+
if (f.secret || f.control === "secret") out[f.name] = "";
|
|
2036
|
+
else if (f.control === "switch") out[f.name] = (values == null ? void 0 : values[f.name]) ?? f.default ?? false;
|
|
2037
|
+
else if (f.control === "json") out[f.name] = (values == null ? void 0 : values[f.name]) ?? f.default;
|
|
2038
|
+
else out[f.name] = (values == null ? void 0 : values[f.name]) ?? f.default ?? "";
|
|
2039
|
+
}
|
|
2040
|
+
return out;
|
|
2041
|
+
}
|
|
2042
|
+
const EditSectionDrawer = ({
|
|
2043
|
+
open,
|
|
2044
|
+
onOpenChange,
|
|
2045
|
+
providerId,
|
|
2046
|
+
section,
|
|
2047
|
+
values,
|
|
2048
|
+
configuredSecrets = []
|
|
2049
|
+
}) => {
|
|
2050
|
+
const { t } = reactI18next.useTranslation();
|
|
2051
|
+
const qc = reactQuery.useQueryClient();
|
|
2052
|
+
const configured = new Set(configuredSecrets);
|
|
2053
|
+
const [defaults] = React.useState(() => sectionDefaults(section, values));
|
|
2054
|
+
const form = useForm({ values: defaults });
|
|
2055
|
+
const current = form.watch();
|
|
2056
|
+
const visibilityValues = { ...values, ...current };
|
|
2057
|
+
const save = reactQuery.useMutation({
|
|
2058
|
+
mutationFn: (sectionValues) => sdk.client.fetch(`/admin/integrations/${providerId}`, {
|
|
2059
|
+
method: "POST",
|
|
2060
|
+
// Only this section's values + its id — the server validates just this section and
|
|
2061
|
+
// merges over the rest of the stored config (keeping blank/untouched secrets).
|
|
2062
|
+
body: { section_id: section.id, values: sectionValues }
|
|
2063
|
+
}),
|
|
2064
|
+
onSuccess: () => {
|
|
2065
|
+
ui.toast.success(t("integration.toast.saved"));
|
|
2066
|
+
qc.invalidateQueries({ queryKey: ["integration", providerId] });
|
|
2067
|
+
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
2068
|
+
onOpenChange(false);
|
|
2069
|
+
},
|
|
2070
|
+
onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.saveFailed"))
|
|
2071
|
+
});
|
|
2072
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
2073
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: t(section.title) }) }),
|
|
2074
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { className: "flex flex-1 flex-col gap-y-4 overflow-auto", children: section.fields.filter((f) => isFieldVisible(f, visibilityValues)).map((f) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2075
|
+
IntegrationField,
|
|
2076
|
+
{
|
|
2077
|
+
field: f,
|
|
2078
|
+
control: form.control,
|
|
2079
|
+
secretConfigured: configured.has(f.name)
|
|
2080
|
+
},
|
|
2081
|
+
f.name
|
|
2082
|
+
)) }),
|
|
2083
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
2084
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", disabled: save.isPending, children: t("integration.actions.cancel") }) }),
|
|
2085
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2086
|
+
ui.Button,
|
|
2087
|
+
{
|
|
2088
|
+
size: "small",
|
|
2089
|
+
isLoading: save.isPending,
|
|
2090
|
+
onClick: form.handleSubmit((v) => save.mutate(v)),
|
|
2091
|
+
children: t("integration.actions.save")
|
|
2092
|
+
}
|
|
2093
|
+
)
|
|
2094
|
+
] })
|
|
2095
|
+
] }) });
|
|
2096
|
+
};
|
|
2097
|
+
const Row = ({ label, children }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle grid grid-cols-2 px-6 py-4", children: [
|
|
2098
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: label }),
|
|
2099
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-2", children })
|
|
2100
|
+
] });
|
|
2101
|
+
const EditPage = () => {
|
|
2102
|
+
const { t } = reactI18next.useTranslation();
|
|
2103
|
+
const { provider_id } = reactRouterDom.useParams();
|
|
2104
|
+
const qc = reactQuery.useQueryClient();
|
|
2105
|
+
const navigate = reactRouterDom.useNavigate();
|
|
2106
|
+
const prompt = ui.usePrompt();
|
|
2107
|
+
const [editingSection, setEditingSection] = React.useState(null);
|
|
2108
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
2109
|
+
queryKey: ["integration", provider_id],
|
|
2110
|
+
queryFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`)
|
|
2111
|
+
});
|
|
2112
|
+
const descriptor = data == null ? void 0 : data.descriptor;
|
|
2113
|
+
const record = data == null ? void 0 : data.integration;
|
|
2114
|
+
const isConfigured = !!record;
|
|
2115
|
+
const isComplete = (data == null ? void 0 : data.is_complete) ?? false;
|
|
2116
|
+
const invalidate = () => {
|
|
2117
|
+
qc.invalidateQueries({ queryKey: ["integration", provider_id] });
|
|
2118
|
+
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
2119
|
+
};
|
|
2120
|
+
const test = reactQuery.useMutation({
|
|
2121
|
+
mutationFn: () => sdk.client.fetch(
|
|
2122
|
+
`/admin/integrations/${provider_id}/test-connection`,
|
|
2123
|
+
{ method: "POST" }
|
|
2124
|
+
),
|
|
2125
|
+
onSuccess: (r) => {
|
|
2126
|
+
r.status === "passed" ? ui.toast.success(t("integration.toast.connectionOk")) : ui.toast.warning(r.message ?? r.status);
|
|
2127
|
+
invalidate();
|
|
2128
|
+
},
|
|
2129
|
+
onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.testFailed"))
|
|
2130
|
+
});
|
|
2131
|
+
const toggleEnabled = reactQuery.useMutation({
|
|
2132
|
+
mutationFn: (next) => sdk.client.fetch(`/admin/integrations/${provider_id}/enable`, {
|
|
2133
|
+
method: "POST",
|
|
2134
|
+
body: { is_enabled: next }
|
|
2135
|
+
}),
|
|
2136
|
+
onSuccess: (_d, next) => {
|
|
2137
|
+
ui.toast.success(next ? t("integration.toast.enabled") : t("integration.toast.disabled"));
|
|
2138
|
+
invalidate();
|
|
2139
|
+
},
|
|
2140
|
+
onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.updateFailed"))
|
|
2141
|
+
});
|
|
2142
|
+
const remove = reactQuery.useMutation({
|
|
2143
|
+
mutationFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`, { method: "DELETE" }),
|
|
2144
|
+
onSuccess: () => {
|
|
2145
|
+
ui.toast.success(t("integration.toast.deleted"));
|
|
2146
|
+
invalidate();
|
|
2147
|
+
navigate("/settings/integrations");
|
|
2148
|
+
},
|
|
2149
|
+
onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.deleteFailed"))
|
|
2150
|
+
});
|
|
2151
|
+
const onDelete = async () => {
|
|
2152
|
+
const confirmed = await prompt({
|
|
2153
|
+
title: t("integration.delete.title"),
|
|
2154
|
+
description: t("integration.delete.description", {
|
|
2155
|
+
name: descriptor ? t(descriptor.displayName) : ""
|
|
2156
|
+
}),
|
|
2157
|
+
confirmText: t("integration.actions.delete"),
|
|
2158
|
+
cancelText: t("integration.actions.cancel")
|
|
2159
|
+
});
|
|
2160
|
+
if (confirmed) remove.mutate();
|
|
2161
|
+
};
|
|
2162
|
+
if (isLoading) {
|
|
2163
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "flex items-center justify-center p-8", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Spinner, { className: "text-ui-fg-subtle animate-spin" }) });
|
|
2164
|
+
}
|
|
2165
|
+
if (!descriptor) {
|
|
2166
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6", children: [
|
|
2167
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.unknown", { id: provider_id }) }),
|
|
2168
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: "/settings/integrations", className: "text-ui-fg-base", children: t("integration.back") })
|
|
2169
|
+
] });
|
|
2170
|
+
}
|
|
2171
|
+
const version = (data == null ? void 0 : data.version) ?? null;
|
|
2172
|
+
const author = (data == null ? void 0 : data.author) ?? null;
|
|
2173
|
+
const authorUrl = (data == null ? void 0 : data.author_url) ?? null;
|
|
2174
|
+
const lifecycle = !isConfigured ? { color: "grey", label: t("integration.status.notConfigured") } : !isComplete ? { color: "orange", label: t("integration.status.incomplete") } : record.is_enabled ? { color: "green", label: t("integration.status.enabled") } : { color: "grey", label: t("integration.status.disabled") };
|
|
2175
|
+
const connection = descriptor.hasTestConnection ? (record == null ? void 0 : record.last_test_status) === "passed" ? { color: "green", label: t("integration.status.connected") } : (record == null ? void 0 : record.last_test_status) === "failed" ? { color: "red", label: t("integration.status.connectionFailed") } : null : null;
|
|
2176
|
+
const editing = descriptor.sections.find((s) => s.id === editingSection) ?? null;
|
|
2177
|
+
const configuredSecrets = new Set((record == null ? void 0 : record.configured_secrets) ?? []);
|
|
2178
|
+
const sectionData = {
|
|
2179
|
+
providerId: provider_id,
|
|
2180
|
+
identifier: descriptor.identifier,
|
|
2181
|
+
values: (record == null ? void 0 : record.values) ?? {},
|
|
2182
|
+
isComplete
|
|
2183
|
+
};
|
|
2184
|
+
const renderSection = (section) => /* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: section.id, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
2185
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
2186
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t(section.title) }),
|
|
2187
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
2188
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
2189
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: () => setEditingSection(section.id), children: [
|
|
2190
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "text-ui-fg-subtle" }),
|
|
2191
|
+
t("integration.actions.edit")
|
|
2192
|
+
] }) })
|
|
2193
|
+
] })
|
|
2194
|
+
] }),
|
|
2195
|
+
section.fields.filter((f) => isFieldVisible(f, (record == null ? void 0 : record.values) ?? {})).map((f) => {
|
|
2196
|
+
var _a;
|
|
2197
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t(f.label), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2198
|
+
IntegrationFieldValue,
|
|
2199
|
+
{
|
|
2200
|
+
field: f,
|
|
2201
|
+
value: ((_a = record == null ? void 0 : record.values) == null ? void 0 : _a[f.name]) ?? f.default,
|
|
2202
|
+
secretConfigured: configuredSecrets.has(f.name)
|
|
2203
|
+
}
|
|
2204
|
+
) }, f.name);
|
|
2205
|
+
})
|
|
2206
|
+
] }, section.id) });
|
|
2207
|
+
const mainSections = descriptor.sections.filter((s) => s.column !== "side");
|
|
2208
|
+
const sideSections = descriptor.sections.filter((s) => s.column === "side");
|
|
2209
|
+
const main = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2210
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: "main", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
2211
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 px-6 py-4", children: [
|
|
2212
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2213
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
2214
|
+
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: descriptor.icon, alt: t(descriptor.displayName) }),
|
|
2215
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
2216
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2217
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: t(descriptor.displayName) }),
|
|
2218
|
+
provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: toModuleProviderId(provider_id, descriptor.category) })
|
|
2219
|
+
] }),
|
|
2220
|
+
version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
|
|
2221
|
+
t("integration.meta.version", { version }),
|
|
2222
|
+
author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2223
|
+
" • ",
|
|
2224
|
+
t("integration.meta.authorPrefix"),
|
|
2225
|
+
" ",
|
|
2226
|
+
authorUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2227
|
+
"a",
|
|
2228
|
+
{
|
|
2229
|
+
href: authorUrl,
|
|
2230
|
+
target: "_blank",
|
|
2231
|
+
rel: "noreferrer",
|
|
2232
|
+
className: "text-ui-fg-interactive",
|
|
2233
|
+
children: author
|
|
2234
|
+
}
|
|
2235
|
+
) : author
|
|
2236
|
+
] })
|
|
2237
|
+
] })
|
|
2238
|
+
] })
|
|
2239
|
+
] }),
|
|
2240
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2241
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
|
|
2242
|
+
connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label }),
|
|
2243
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
2244
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
2245
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
|
|
2246
|
+
descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
|
|
2247
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2248
|
+
ui.DropdownMenu.Item,
|
|
2249
|
+
{
|
|
2250
|
+
disabled: toggleEnabled.isPending,
|
|
2251
|
+
onClick: () => toggleEnabled.mutate(!record.is_enabled),
|
|
2252
|
+
children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
|
|
2253
|
+
}
|
|
2254
|
+
),
|
|
2255
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
|
|
2256
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
|
|
2257
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
|
|
2258
|
+
t("integration.actions.delete")
|
|
2259
|
+
] })
|
|
2260
|
+
] })
|
|
2261
|
+
] })
|
|
2262
|
+
] })
|
|
2263
|
+
] }),
|
|
2264
|
+
descriptor.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(descriptor.description) })
|
|
2265
|
+
] }),
|
|
2266
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("integration.labels.category"), children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: t(`integration.categories.${descriptor.category}`) }) })
|
|
2267
|
+
] }) }),
|
|
2268
|
+
mainSections.map(renderSection),
|
|
2269
|
+
editing && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2270
|
+
EditSectionDrawer,
|
|
2271
|
+
{
|
|
2272
|
+
open: true,
|
|
2273
|
+
onOpenChange: (open) => !open && setEditingSection(null),
|
|
2274
|
+
providerId: provider_id,
|
|
2275
|
+
section: editing,
|
|
2276
|
+
values: (record == null ? void 0 : record.values) ?? {},
|
|
2277
|
+
configuredSecrets: (record == null ? void 0 : record.configured_secrets) ?? []
|
|
2278
|
+
},
|
|
2279
|
+
editing.id
|
|
2280
|
+
)
|
|
2281
|
+
] });
|
|
2282
|
+
const side = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: sideSections.map(renderSection) });
|
|
2283
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2284
|
+
components.LayoutComposer,
|
|
2285
|
+
{
|
|
2286
|
+
widgetsZonePrefix: `gorgo.integration.${descriptor.identifier}`,
|
|
2287
|
+
preferredLayoutId: descriptor.preferredLayoutId,
|
|
2288
|
+
sections: { main, side },
|
|
2289
|
+
data: sectionData,
|
|
2290
|
+
hasOutlet: false
|
|
2291
|
+
}
|
|
2292
|
+
);
|
|
2293
|
+
};
|
|
2294
|
+
const IntegrationBreadcrumb = ({ params }) => {
|
|
2295
|
+
const { t } = reactI18next.useTranslation();
|
|
2296
|
+
const providerId = params.provider_id;
|
|
2297
|
+
const { data } = reactQuery.useQuery({
|
|
2298
|
+
queryKey: ["integration", providerId],
|
|
2299
|
+
queryFn: () => sdk.client.fetch(`/admin/integrations/${providerId}`),
|
|
2300
|
+
enabled: !!providerId
|
|
2301
|
+
});
|
|
2302
|
+
const descriptor = data == null ? void 0 : data.descriptor;
|
|
2303
|
+
if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
2304
|
+
const name = t(descriptor.displayName);
|
|
2305
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2306
|
+
name + " ",
|
|
2307
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: providerId })
|
|
2308
|
+
] });
|
|
2309
|
+
};
|
|
2310
|
+
const handle$1 = {
|
|
2311
|
+
breadcrumb: ({ params }) => /* @__PURE__ */ jsxRuntime.jsx(IntegrationBreadcrumb, { params })
|
|
2312
|
+
};
|
|
2313
|
+
function isSafeHref(raw) {
|
|
2314
|
+
try {
|
|
2315
|
+
return new URL(raw).protocol === "https:";
|
|
2316
|
+
} catch {
|
|
2317
|
+
return false;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
const INLINE_RE = /`([^`\n]+)`|\[([^\]\n]*)\]\(([^)\s]+)\)|\*\*([^*\n]+)\*\*|\*([^*\n]+)\*|_([^_\n]+)_/;
|
|
2321
|
+
const MAX_DEPTH = 3;
|
|
2322
|
+
function parseInline(src, depth = 0) {
|
|
2323
|
+
if (!src) return [];
|
|
2324
|
+
if (depth >= MAX_DEPTH) return [{ type: "text", value: src }];
|
|
2325
|
+
const out = [];
|
|
2326
|
+
const re = new RegExp(INLINE_RE, "g");
|
|
2327
|
+
let last = 0;
|
|
2328
|
+
let m;
|
|
2329
|
+
while ((m = re.exec(src)) !== null) {
|
|
2330
|
+
const [full, code, linkText, linkHref, strong, star, underscore] = m;
|
|
2331
|
+
if (m.index > last) out.push({ type: "text", value: src.slice(last, m.index) });
|
|
2332
|
+
last = m.index + full.length;
|
|
2333
|
+
if (code !== void 0) {
|
|
2334
|
+
out.push({ type: "code", value: code });
|
|
2335
|
+
} else if (linkHref !== void 0) {
|
|
2336
|
+
const isImage = m.index > 0 && src[m.index - 1] === "!";
|
|
2337
|
+
if (!isImage && isSafeHref(linkHref)) {
|
|
2338
|
+
out.push({ type: "link", href: linkHref, children: parseInline(linkText, depth + 1) });
|
|
2339
|
+
} else {
|
|
2340
|
+
out.push({ type: "text", value: full });
|
|
2341
|
+
}
|
|
2342
|
+
} else if (strong !== void 0) {
|
|
2343
|
+
out.push({ type: "strong", children: parseInline(strong, depth + 1) });
|
|
2344
|
+
} else {
|
|
2345
|
+
out.push({ type: "em", children: parseInline(star ?? underscore, depth + 1) });
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
if (last < src.length) out.push({ type: "text", value: src.slice(last) });
|
|
2349
|
+
return out;
|
|
2350
|
+
}
|
|
2351
|
+
const MD_MAX_CHARS = 8e3;
|
|
2352
|
+
const MD_MAX_BLOCKS = 200;
|
|
2353
|
+
const FENCE_RE = /^```(.*)$/;
|
|
2354
|
+
const HEADING_RE = /^(#{1,3})\s+(.*)$/;
|
|
2355
|
+
const HR_RE = /^-{3,}\s*$/;
|
|
2356
|
+
const UL_RE = /^[-*]\s+(.*)$/;
|
|
2357
|
+
const OL_RE = /^\d+\.\s+(.*)$/;
|
|
2358
|
+
function fenceLabel(info) {
|
|
2359
|
+
const first = info.trim().split(/\s+/)[0] ?? "";
|
|
2360
|
+
return first.replace(/[^a-zA-Z0-9._/-]/g, "").slice(0, 40) || null;
|
|
2361
|
+
}
|
|
2362
|
+
function parseMarkdown(src) {
|
|
2363
|
+
if (typeof src !== "string" || !src) return [];
|
|
2364
|
+
if (src.length > MD_MAX_CHARS) return [{ type: "truncated" }];
|
|
2365
|
+
const lines = src.replace(/\r\n?/g, "\n").split("\n");
|
|
2366
|
+
const blocks = [];
|
|
2367
|
+
let para = [];
|
|
2368
|
+
const flushPara = () => {
|
|
2369
|
+
if (!para.length) return;
|
|
2370
|
+
blocks.push({ type: "paragraph", children: parseInline(para.join(" ")) });
|
|
2371
|
+
para = [];
|
|
2372
|
+
};
|
|
2373
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2374
|
+
const line = lines[i];
|
|
2375
|
+
const fence = FENCE_RE.exec(line);
|
|
2376
|
+
if (fence) {
|
|
2377
|
+
flushPara();
|
|
2378
|
+
const body = [];
|
|
2379
|
+
i++;
|
|
2380
|
+
while (i < lines.length && !FENCE_RE.test(lines[i])) body.push(lines[i++]);
|
|
2381
|
+
blocks.push({ type: "codeBlock", label: fenceLabel(fence[1]), value: body.join("\n") });
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
if (!line.trim()) {
|
|
2385
|
+
flushPara();
|
|
2386
|
+
continue;
|
|
2387
|
+
}
|
|
2388
|
+
if (HR_RE.test(line)) {
|
|
2389
|
+
flushPara();
|
|
2390
|
+
blocks.push({ type: "hr" });
|
|
2391
|
+
continue;
|
|
2392
|
+
}
|
|
2393
|
+
const heading = HEADING_RE.exec(line);
|
|
2394
|
+
if (heading) {
|
|
2395
|
+
flushPara();
|
|
2396
|
+
blocks.push({
|
|
2397
|
+
type: "heading",
|
|
2398
|
+
level: heading[1].length + 1,
|
|
2399
|
+
children: parseInline(heading[2])
|
|
2400
|
+
});
|
|
2401
|
+
continue;
|
|
2402
|
+
}
|
|
2403
|
+
const ul = UL_RE.exec(line);
|
|
2404
|
+
const ol = ul ? null : OL_RE.exec(line);
|
|
2405
|
+
if (ul || ol) {
|
|
2406
|
+
flushPara();
|
|
2407
|
+
const ordered = !!ol;
|
|
2408
|
+
const itemRe = ordered ? OL_RE : UL_RE;
|
|
2409
|
+
const items = [];
|
|
2410
|
+
while (i < lines.length) {
|
|
2411
|
+
const item = itemRe.exec(lines[i]);
|
|
2412
|
+
if (!item) break;
|
|
2413
|
+
items.push(parseInline(item[1]));
|
|
2414
|
+
i++;
|
|
2415
|
+
}
|
|
2416
|
+
i--;
|
|
2417
|
+
blocks.push({ type: "list", ordered, items });
|
|
2418
|
+
continue;
|
|
2419
|
+
}
|
|
2420
|
+
para.push(line);
|
|
2421
|
+
}
|
|
2422
|
+
flushPara();
|
|
2423
|
+
const weight = blocks.reduce((n, b) => n + (b.type === "list" ? b.items.length : 1), 0);
|
|
2424
|
+
return weight > MD_MAX_BLOCKS ? [{ type: "truncated" }] : blocks;
|
|
2425
|
+
}
|
|
2426
|
+
const Inline = ({ nodes }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: nodes.map((node, i) => {
|
|
2427
|
+
switch (node.type) {
|
|
2428
|
+
case "text":
|
|
2429
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.value }, i);
|
|
2430
|
+
case "code":
|
|
2431
|
+
return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "txt-compact-small bg-ui-bg-component rounded px-1 py-0.5", children: node.value }, i);
|
|
2432
|
+
case "strong":
|
|
2433
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-small-plus", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
2434
|
+
case "em":
|
|
2435
|
+
return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
2436
|
+
case "link":
|
|
2279
2437
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2280
|
-
|
|
2438
|
+
"a",
|
|
2281
2439
|
{
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2440
|
+
href: node.href,
|
|
2441
|
+
target: "_blank",
|
|
2442
|
+
rel: "noopener noreferrer",
|
|
2443
|
+
className: "text-ui-fg-interactive hover:underline",
|
|
2444
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children })
|
|
2445
|
+
},
|
|
2446
|
+
i
|
|
2289
2447
|
);
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2448
|
+
}
|
|
2449
|
+
}) });
|
|
2450
|
+
const Block = ({ block, truncatedLabel }) => {
|
|
2451
|
+
switch (block.type) {
|
|
2452
|
+
case "heading":
|
|
2453
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: block.level === 2 ? "h2" : "h3", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
2454
|
+
case "paragraph":
|
|
2455
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
2456
|
+
case "list": {
|
|
2457
|
+
const List = block.ordered ? "ol" : "ul";
|
|
2293
2458
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2294
|
-
|
|
2459
|
+
List,
|
|
2295
2460
|
{
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
disabled,
|
|
2299
|
-
onChange: f.onChange
|
|
2461
|
+
className: `text-ui-fg-subtle txt-small flex flex-col gap-y-1 pl-5 ${block.ordered ? "list-decimal" : "list-disc"}`,
|
|
2462
|
+
children: block.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: item }) }, i))
|
|
2300
2463
|
}
|
|
2301
2464
|
);
|
|
2465
|
+
}
|
|
2466
|
+
case "codeBlock":
|
|
2467
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle flex flex-col gap-y-2 rounded-md px-3 pb-1 pt-2", children: [
|
|
2468
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-2", children: [
|
|
2469
|
+
block.label ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-subtle", children: block.label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
2470
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: block.value })
|
|
2471
|
+
] }),
|
|
2472
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "txt-compact-small text-ui-fg-subtle overflow-x-auto whitespace-pre pb-2", children: block.value })
|
|
2473
|
+
] });
|
|
2474
|
+
case "hr":
|
|
2475
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-ui-border-base border-t" });
|
|
2476
|
+
case "truncated":
|
|
2477
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: truncatedLabel });
|
|
2302
2478
|
}
|
|
2303
|
-
}
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
return values[rule.field] === rule.equals;
|
|
2308
|
-
}
|
|
2309
|
-
function sectionDefaults(section, values) {
|
|
2310
|
-
const out = {};
|
|
2311
|
-
for (const f of section.fields) {
|
|
2312
|
-
if (f.secret || f.control === "secret") out[f.name] = "";
|
|
2313
|
-
else if (f.control === "switch") out[f.name] = (values == null ? void 0 : values[f.name]) ?? f.default ?? false;
|
|
2314
|
-
else if (f.control === "json") out[f.name] = (values == null ? void 0 : values[f.name]) ?? f.default;
|
|
2315
|
-
else out[f.name] = (values == null ? void 0 : values[f.name]) ?? f.default ?? "";
|
|
2316
|
-
}
|
|
2317
|
-
return out;
|
|
2318
|
-
}
|
|
2319
|
-
const EditSectionDrawer = ({
|
|
2320
|
-
open,
|
|
2321
|
-
onOpenChange,
|
|
2322
|
-
providerId,
|
|
2323
|
-
section,
|
|
2324
|
-
values,
|
|
2325
|
-
configuredSecrets = []
|
|
2479
|
+
};
|
|
2480
|
+
const Markdown = ({
|
|
2481
|
+
source,
|
|
2482
|
+
truncatedLabel
|
|
2326
2483
|
}) => {
|
|
2327
|
-
const
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
const [defaults] = React.useState(() => sectionDefaults(section, values));
|
|
2331
|
-
const form = useForm({ values: defaults });
|
|
2332
|
-
const current = form.watch();
|
|
2333
|
-
const visibilityValues = { ...values, ...current };
|
|
2334
|
-
const save = reactQuery.useMutation({
|
|
2335
|
-
mutationFn: (sectionValues) => sdk.client.fetch(`/admin/integrations/${providerId}`, {
|
|
2336
|
-
method: "POST",
|
|
2337
|
-
// Only this section's values + its id — the server validates just this section and
|
|
2338
|
-
// merges over the rest of the stored config (keeping blank/untouched secrets).
|
|
2339
|
-
body: { section_id: section.id, values: sectionValues }
|
|
2340
|
-
}),
|
|
2341
|
-
onSuccess: () => {
|
|
2342
|
-
ui.toast.success(t("integration.toast.saved"));
|
|
2343
|
-
qc.invalidateQueries({ queryKey: ["integration", providerId] });
|
|
2344
|
-
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
2345
|
-
onOpenChange(false);
|
|
2346
|
-
},
|
|
2347
|
-
onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.saveFailed"))
|
|
2348
|
-
});
|
|
2349
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
2350
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: t(section.title) }) }),
|
|
2351
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { className: "flex flex-1 flex-col gap-y-4 overflow-auto", children: section.fields.filter((f) => isFieldVisible(f, visibilityValues)).map((f) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2352
|
-
IntegrationField,
|
|
2353
|
-
{
|
|
2354
|
-
field: f,
|
|
2355
|
-
control: form.control,
|
|
2356
|
-
secretConfigured: configured.has(f.name)
|
|
2357
|
-
},
|
|
2358
|
-
f.name
|
|
2359
|
-
)) }),
|
|
2360
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
2361
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", disabled: save.isPending, children: t("integration.actions.cancel") }) }),
|
|
2362
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2363
|
-
ui.Button,
|
|
2364
|
-
{
|
|
2365
|
-
size: "small",
|
|
2366
|
-
isLoading: save.isPending,
|
|
2367
|
-
onClick: form.handleSubmit((v) => save.mutate(v)),
|
|
2368
|
-
children: t("integration.actions.save")
|
|
2369
|
-
}
|
|
2370
|
-
)
|
|
2371
|
-
] })
|
|
2372
|
-
] }) });
|
|
2484
|
+
const blocks = React.useMemo(() => parseMarkdown(source), [source]);
|
|
2485
|
+
if (!blocks.length) return null;
|
|
2486
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-y-4", children: blocks.map((block, i) => /* @__PURE__ */ jsxRuntime.jsx(Block, { block, truncatedLabel }, i)) });
|
|
2373
2487
|
};
|
|
2374
|
-
const
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
const
|
|
2383
|
-
const
|
|
2384
|
-
const [
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
mutationFn: (next) => sdk.client.fetch(`/admin/integrations/${provider_id}/enable`, {
|
|
2410
|
-
method: "POST",
|
|
2411
|
-
body: { is_enabled: next }
|
|
2412
|
-
}),
|
|
2413
|
-
onSuccess: (_d, next) => {
|
|
2414
|
-
ui.toast.success(next ? t("integration.toast.enabled") : t("integration.toast.disabled"));
|
|
2415
|
-
invalidate();
|
|
2416
|
-
},
|
|
2417
|
-
onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.updateFailed"))
|
|
2488
|
+
const PUBLISH_URL = "https://docs.gorgojs.com/medusa-modules/integration";
|
|
2489
|
+
const API_URL = "https://gorgojs.com";
|
|
2490
|
+
const iconSrc = (icon) => icon.startsWith("data:") ? icon : API_URL + icon;
|
|
2491
|
+
const InstallModal = ({
|
|
2492
|
+
item,
|
|
2493
|
+
onOpenChange
|
|
2494
|
+
}) => {
|
|
2495
|
+
var _a, _b, _c;
|
|
2496
|
+
const { t, i18n } = reactI18next.useTranslation();
|
|
2497
|
+
const lang = i18n.language.split("-")[0] === "ru" ? "ru" : "en";
|
|
2498
|
+
const source = ((_a = item == null ? void 0 : item.docsSnippet) == null ? void 0 : _a[lang]) || ((_b = item == null ? void 0 : item.docsSnippet) == null ? void 0 : _b.en) || ((_c = item == null ? void 0 : item.docsSnippet) == null ? void 0 : _c.ru) || "";
|
|
2499
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: !!item, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
2500
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2501
|
+
item && /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label, size: "small" }),
|
|
2502
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: item == null ? void 0 : item.label })
|
|
2503
|
+
] }) }) }),
|
|
2504
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { className: "flex flex-1 flex-col gap-y-4 overflow-auto", children: source.trim() ? /* @__PURE__ */ jsxRuntime.jsx(Markdown, { source, truncatedLabel: t("integration.browse.install_truncated") }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.install_empty") }) }),
|
|
2505
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
2506
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: t("integration.actions.cancel") }) }),
|
|
2507
|
+
(item == null ? void 0 : item.docsUrl) && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "primary", asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: item.docsUrl, target: "_blank", rel: "noopener noreferrer", children: [
|
|
2508
|
+
t("integration.browse.install_docs"),
|
|
2509
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, {})
|
|
2510
|
+
] }) })
|
|
2511
|
+
] })
|
|
2512
|
+
] }) });
|
|
2513
|
+
};
|
|
2514
|
+
const BrowsePage = () => {
|
|
2515
|
+
const { t } = reactI18next.useTranslation();
|
|
2516
|
+
const navigate = reactRouterDom.useNavigate();
|
|
2517
|
+
const [activeTab, setActiveTab] = React.useState("all");
|
|
2518
|
+
const [search, setSearch] = React.useState("");
|
|
2519
|
+
const [installItem, setInstallItem] = React.useState(null);
|
|
2520
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
2521
|
+
queryKey: ["integration-catalog"],
|
|
2522
|
+
queryFn: () => sdk.client.fetch("/admin/integrations/catalog")
|
|
2418
2523
|
});
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.deleteFailed"))
|
|
2524
|
+
const items = (data == null ? void 0 : data.integrations) ?? [];
|
|
2525
|
+
const categories = React.useMemo(() => [...new Set(items.map((i) => i.category))], [items]);
|
|
2526
|
+
const q = search.trim().toLowerCase();
|
|
2527
|
+
const filtered = items.filter((i) => {
|
|
2528
|
+
if (activeTab !== "all" && i.category !== activeTab) return false;
|
|
2529
|
+
if (q && ![i.label, i.shortDescription, i.npm].some((s) => s.toLowerCase().includes(q))) return false;
|
|
2530
|
+
return true;
|
|
2427
2531
|
});
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
const authorUrl = (data == null ? void 0 : data.author_url) ?? null;
|
|
2451
|
-
const lifecycle = !isConfigured ? { color: "grey", label: t("integration.status.notConfigured") } : !isComplete ? { color: "orange", label: t("integration.status.incomplete") } : record.is_enabled ? { color: "green", label: t("integration.status.enabled") } : { color: "grey", label: t("integration.status.disabled") };
|
|
2452
|
-
const connection = descriptor.hasTestConnection ? (record == null ? void 0 : record.last_test_status) === "passed" ? { color: "green", label: t("integration.status.connected") } : (record == null ? void 0 : record.last_test_status) === "failed" ? { color: "red", label: t("integration.status.connectionFailed") } : null : null;
|
|
2453
|
-
const editing = descriptor.sections.find((s) => s.id === editingSection) ?? null;
|
|
2454
|
-
const configuredSecrets = new Set((record == null ? void 0 : record.configured_secrets) ?? []);
|
|
2455
|
-
const sectionData = {
|
|
2456
|
-
providerId: provider_id,
|
|
2457
|
-
identifier: descriptor.identifier,
|
|
2458
|
-
values: (record == null ? void 0 : record.values) ?? {},
|
|
2459
|
-
isComplete
|
|
2460
|
-
};
|
|
2461
|
-
const renderSection = (section) => /* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: section.id, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
2462
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
2463
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t(section.title) }),
|
|
2464
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
2465
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
2466
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: () => setEditingSection(section.id), children: [
|
|
2467
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "text-ui-fg-subtle" }),
|
|
2468
|
-
t("integration.actions.edit")
|
|
2469
|
-
] }) })
|
|
2532
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3", children: [
|
|
2533
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
2534
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
2535
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2536
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("integration.browse.title") }),
|
|
2537
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.subtitle") })
|
|
2538
|
+
] }),
|
|
2539
|
+
false
|
|
2540
|
+
] }),
|
|
2541
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 px-6 py-4", children: [
|
|
2542
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs, { value: activeTab, onValueChange: setActiveTab, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
|
|
2543
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "all", children: t("integration.list.tabs.all") }),
|
|
2544
|
+
categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: c, children: t(`integration.categories.${c}`) }, c))
|
|
2545
|
+
] }) }),
|
|
2546
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-64", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2547
|
+
ui.Input,
|
|
2548
|
+
{
|
|
2549
|
+
placeholder: t("integration.browse.searchPlaceholder"),
|
|
2550
|
+
value: search,
|
|
2551
|
+
onChange: (e) => setSearch(e.target.value)
|
|
2552
|
+
}
|
|
2553
|
+
) })
|
|
2470
2554
|
] })
|
|
2471
2555
|
] }),
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
IntegrationFieldValue,
|
|
2476
|
-
{
|
|
2477
|
-
field: f,
|
|
2478
|
-
value: ((_a = record == null ? void 0 : record.values) == null ? void 0 : _a[f.name]) ?? f.default,
|
|
2479
|
-
secretConfigured: configuredSecrets.has(f.name)
|
|
2480
|
-
}
|
|
2481
|
-
) }, f.name);
|
|
2482
|
-
})
|
|
2483
|
-
] }, section.id) });
|
|
2484
|
-
const mainSections = descriptor.sections.filter((s) => s.column !== "side");
|
|
2485
|
-
const sideSections = descriptor.sections.filter((s) => s.column === "side");
|
|
2486
|
-
const main = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2487
|
-
/* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: "main", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
2488
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 px-6 py-4", children: [
|
|
2489
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2556
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Spinner, { className: "text-ui-fg-subtle animate-spin" }) }) : filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.list.emptyFiltered") }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4", children: filtered.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shadow-borders-base bg-ui-bg-base flex flex-col rounded-lg", children: [
|
|
2557
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-y-3 p-4", children: [
|
|
2558
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-x-2", children: [
|
|
2490
2559
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
2491
|
-
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src:
|
|
2492
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col
|
|
2493
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2494
|
-
|
|
2495
|
-
provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: toModuleProviderId(provider_id, descriptor.category) })
|
|
2496
|
-
] }),
|
|
2497
|
-
version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
|
|
2498
|
-
t("integration.meta.version", { version }),
|
|
2499
|
-
author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2500
|
-
" • ",
|
|
2501
|
-
t("integration.meta.authorPrefix"),
|
|
2502
|
-
" ",
|
|
2503
|
-
authorUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2504
|
-
"a",
|
|
2505
|
-
{
|
|
2506
|
-
href: authorUrl,
|
|
2507
|
-
target: "_blank",
|
|
2508
|
-
rel: "noreferrer",
|
|
2509
|
-
className: "text-ui-fg-interactive",
|
|
2510
|
-
children: author
|
|
2511
|
-
}
|
|
2512
|
-
) : author
|
|
2513
|
-
] })
|
|
2514
|
-
] })
|
|
2560
|
+
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label }),
|
|
2561
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
2562
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: item.label }),
|
|
2563
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: t("integration.browse.by", { author: item.authorLocalized }) })
|
|
2515
2564
|
] })
|
|
2516
2565
|
] }),
|
|
2517
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2518
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
|
|
2519
|
-
connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label }),
|
|
2520
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
2521
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
2522
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
|
|
2523
|
-
descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
|
|
2524
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2525
|
-
ui.DropdownMenu.Item,
|
|
2526
|
-
{
|
|
2527
|
-
disabled: toggleEnabled.isPending,
|
|
2528
|
-
onClick: () => toggleEnabled.mutate(!record.is_enabled),
|
|
2529
|
-
children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
|
|
2530
|
-
}
|
|
2531
|
-
),
|
|
2532
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
|
|
2533
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
|
|
2534
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
|
|
2535
|
-
t("integration.actions.delete")
|
|
2536
|
-
] })
|
|
2537
|
-
] })
|
|
2538
|
-
] })
|
|
2539
|
-
] })
|
|
2566
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: item.installed ? "green" : "grey", children: item.installed ? t("integration.browse.installed") : t("integration.browse.notInstalled") })
|
|
2540
2567
|
] }),
|
|
2541
|
-
|
|
2568
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle line-clamp-2", children: item.shortDescription })
|
|
2542
2569
|
] }),
|
|
2543
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2570
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
|
|
2571
|
+
item.installed && item.provider_id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2572
|
+
ui.Button,
|
|
2573
|
+
{
|
|
2574
|
+
size: "small",
|
|
2575
|
+
variant: "primary",
|
|
2576
|
+
onClick: () => navigate(`/settings/integrations/${item.provider_id}`),
|
|
2577
|
+
children: t("integration.browse.settings")
|
|
2578
|
+
}
|
|
2579
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", variant: "secondary", onClick: () => setInstallItem(item), children: [
|
|
2580
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
2581
|
+
t("integration.browse.add")
|
|
2582
|
+
] }),
|
|
2583
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(`integration.categories.${item.category}`) })
|
|
2584
|
+
] })
|
|
2585
|
+
] }, item.integrationId)) }),
|
|
2586
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: PUBLISH_URL, target: "_blank", rel: "noreferrer", className: "text-ui-fg-base txt-small", children: [
|
|
2587
|
+
t("integration.browse.learnMore"),
|
|
2588
|
+
" →"
|
|
2544
2589
|
] }) }),
|
|
2545
|
-
|
|
2546
|
-
editing && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2547
|
-
EditSectionDrawer,
|
|
2548
|
-
{
|
|
2549
|
-
open: true,
|
|
2550
|
-
onOpenChange: (open) => !open && setEditingSection(null),
|
|
2551
|
-
providerId: provider_id,
|
|
2552
|
-
section: editing,
|
|
2553
|
-
values: (record == null ? void 0 : record.values) ?? {},
|
|
2554
|
-
configuredSecrets: (record == null ? void 0 : record.configured_secrets) ?? []
|
|
2555
|
-
},
|
|
2556
|
-
editing.id
|
|
2557
|
-
)
|
|
2590
|
+
/* @__PURE__ */ jsxRuntime.jsx(InstallModal, { item: installItem, onOpenChange: (open) => !open && setInstallItem(null) })
|
|
2558
2591
|
] });
|
|
2559
|
-
const side = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: sideSections.map(renderSection) });
|
|
2560
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2561
|
-
components.LayoutComposer,
|
|
2562
|
-
{
|
|
2563
|
-
widgetsZonePrefix: `gorgo.integration.${descriptor.identifier}`,
|
|
2564
|
-
preferredLayoutId: descriptor.preferredLayoutId,
|
|
2565
|
-
sections: { main, side },
|
|
2566
|
-
data: sectionData,
|
|
2567
|
-
hasOutlet: false
|
|
2568
|
-
}
|
|
2569
|
-
);
|
|
2570
2592
|
};
|
|
2571
|
-
const
|
|
2593
|
+
const BrowseBreadcrumb = () => {
|
|
2572
2594
|
const { t } = reactI18next.useTranslation();
|
|
2573
|
-
|
|
2574
|
-
const { data } = reactQuery.useQuery({
|
|
2575
|
-
queryKey: ["integration", providerId],
|
|
2576
|
-
queryFn: () => sdk.client.fetch(`/admin/integrations/${providerId}`),
|
|
2577
|
-
enabled: !!providerId
|
|
2578
|
-
});
|
|
2579
|
-
const descriptor = data == null ? void 0 : data.descriptor;
|
|
2580
|
-
if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
2581
|
-
const name = t(descriptor.displayName);
|
|
2582
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2583
|
-
name + " ",
|
|
2584
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: providerId })
|
|
2585
|
-
] });
|
|
2595
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("integration.browse.title") });
|
|
2586
2596
|
};
|
|
2587
2597
|
const handle = {
|
|
2588
|
-
breadcrumb: (
|
|
2598
|
+
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(BrowseBreadcrumb, {})
|
|
2589
2599
|
};
|
|
2590
2600
|
const integration$1 = {
|
|
2591
2601
|
list: {
|
|
@@ -2808,13 +2818,13 @@ const routeModule = {
|
|
|
2808
2818
|
handle: { label: config.label, translationNs: config.translationNs, ...handle$2 }
|
|
2809
2819
|
},
|
|
2810
2820
|
{
|
|
2811
|
-
Component:
|
|
2812
|
-
path: "/settings/integrations
|
|
2821
|
+
Component: EditPage,
|
|
2822
|
+
path: "/settings/integrations/:provider_id",
|
|
2813
2823
|
handle: handle$1
|
|
2814
2824
|
},
|
|
2815
2825
|
{
|
|
2816
|
-
Component:
|
|
2817
|
-
path: "/settings/integrations
|
|
2826
|
+
Component: BrowsePage,
|
|
2827
|
+
path: "/settings/integrations/browse",
|
|
2818
2828
|
handle
|
|
2819
2829
|
}
|
|
2820
2830
|
]
|