@gorgo/medusa-integration 0.3.0 → 0.3.1
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 +934 -934
- package/.medusa/server/src/admin/index.mjs +936 -936
- package/README.md +2 -0
- package/package.json +1 -1
|
@@ -498,113 +498,399 @@ const IntegrationsBreadcrumb = () => {
|
|
|
498
498
|
const handle$2 = {
|
|
499
499
|
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(IntegrationsBreadcrumb, {})
|
|
500
500
|
};
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}) => {
|
|
507
|
-
const { t } = reactI18next.useTranslation();
|
|
508
|
-
if (field.secret || field.control === "secret") {
|
|
509
|
-
return secretConfigured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "••••••••" }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
510
|
-
}
|
|
511
|
-
if (field.control === "switch") {
|
|
512
|
-
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
513
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: value ? "green" : "grey", children: value ? t("integration.status.enabled") : t("integration.status.disabled") });
|
|
501
|
+
function isSafeHref(raw) {
|
|
502
|
+
try {
|
|
503
|
+
return new URL(raw).protocol === "https:";
|
|
504
|
+
} catch {
|
|
505
|
+
return false;
|
|
514
506
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
507
|
+
}
|
|
508
|
+
const INLINE_RE = /`([^`\n]+)`|\[([^\]\n]*)\]\(([^)\s]+)\)|\*\*([^*\n]+)\*\*|\*([^*\n]+)\*|_([^_\n]+)_/;
|
|
509
|
+
const MAX_DEPTH = 3;
|
|
510
|
+
function parseInline(src, depth = 0) {
|
|
511
|
+
if (!src) return [];
|
|
512
|
+
if (depth >= MAX_DEPTH) return [{ type: "text", value: src }];
|
|
513
|
+
const out = [];
|
|
514
|
+
const re = new RegExp(INLINE_RE, "g");
|
|
515
|
+
let last = 0;
|
|
516
|
+
let m;
|
|
517
|
+
while ((m = re.exec(src)) !== null) {
|
|
518
|
+
const [full, code, linkText, linkHref, strong, star, underscore] = m;
|
|
519
|
+
if (m.index > last) out.push({ type: "text", value: src.slice(last, m.index) });
|
|
520
|
+
last = m.index + full.length;
|
|
521
|
+
if (code !== void 0) {
|
|
522
|
+
out.push({ type: "code", value: code });
|
|
523
|
+
} else if (linkHref !== void 0) {
|
|
524
|
+
const isImage = m.index > 0 && src[m.index - 1] === "!";
|
|
525
|
+
if (!isImage && isSafeHref(linkHref)) {
|
|
526
|
+
out.push({ type: "link", href: linkHref, children: parseInline(linkText, depth + 1) });
|
|
527
|
+
} else {
|
|
528
|
+
out.push({ type: "text", value: full });
|
|
529
|
+
}
|
|
530
|
+
} else if (strong !== void 0) {
|
|
531
|
+
out.push({ type: "strong", children: parseInline(strong, depth + 1) });
|
|
532
|
+
} else {
|
|
533
|
+
out.push({ type: "em", children: parseInline(star ?? underscore, depth + 1) });
|
|
518
534
|
}
|
|
519
|
-
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
520
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.status.configured") });
|
|
521
|
-
}
|
|
522
|
-
if (value == null || value === "") return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
523
|
-
if (field.control === "select") {
|
|
524
|
-
const labelKey = field.optionLabels?.[String(value)];
|
|
525
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: labelKey ? t(labelKey) : String(value) });
|
|
526
|
-
}
|
|
527
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: String(value) });
|
|
528
|
-
};
|
|
529
|
-
var isCheckBoxInput = (element) => element.type === "checkbox";
|
|
530
|
-
var isFileInput = (element) => element.type === "file";
|
|
531
|
-
var isDateObject = (value) => value instanceof Date;
|
|
532
|
-
var isNullOrUndefined = (value) => value == null;
|
|
533
|
-
const isObjectType = (value) => typeof value === "object";
|
|
534
|
-
var isObject = (value) => !isNullOrUndefined(value) && !Array.isArray(value) && isObjectType(value) && !isDateObject(value);
|
|
535
|
-
var getEventValue = (event) => isObject(event) && event.target ? isCheckBoxInput(event.target) ? event.target.checked : isFileInput(event.target) ? event.target.files : event.target.value : event;
|
|
536
|
-
var isNameInFieldArray = (names, name) => name.split(".").some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join(".")));
|
|
537
|
-
var isPlainObject = (tempObject) => {
|
|
538
|
-
const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
|
|
539
|
-
return isObject(prototypeCopy) && prototypeCopy.hasOwnProperty("isPrototypeOf");
|
|
540
|
-
};
|
|
541
|
-
var isWeb = typeof window !== "undefined" && typeof window.HTMLElement !== "undefined" && typeof document !== "undefined";
|
|
542
|
-
function cloneObject(data) {
|
|
543
|
-
if (data instanceof Date) {
|
|
544
|
-
return new Date(data);
|
|
545
|
-
}
|
|
546
|
-
const isFileListInstance = typeof FileList !== "undefined" && data instanceof FileList;
|
|
547
|
-
if (isWeb && (data instanceof Blob || isFileListInstance)) {
|
|
548
|
-
return data;
|
|
549
|
-
}
|
|
550
|
-
const isArray = Array.isArray(data);
|
|
551
|
-
if (!isArray && !(isObject(data) && isPlainObject(data))) {
|
|
552
|
-
return data;
|
|
553
535
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
536
|
+
if (last < src.length) out.push({ type: "text", value: src.slice(last) });
|
|
537
|
+
return out;
|
|
538
|
+
}
|
|
539
|
+
const MD_MAX_CHARS = 8e3;
|
|
540
|
+
const MD_MAX_BLOCKS = 200;
|
|
541
|
+
const FENCE_RE = /^```(.*)$/;
|
|
542
|
+
const HEADING_RE = /^(#{1,3})\s+(.*)$/;
|
|
543
|
+
const HR_RE = /^-{3,}\s*$/;
|
|
544
|
+
const UL_RE = /^[-*]\s+(.*)$/;
|
|
545
|
+
const OL_RE = /^\d+\.\s+(.*)$/;
|
|
546
|
+
function fenceLabel(info) {
|
|
547
|
+
const first = info.trim().split(/\s+/)[0] ?? "";
|
|
548
|
+
return first.replace(/[^a-zA-Z0-9._/-]/g, "").slice(0, 40) || null;
|
|
549
|
+
}
|
|
550
|
+
function parseMarkdown(src) {
|
|
551
|
+
if (typeof src !== "string" || !src) return [];
|
|
552
|
+
if (src.length > MD_MAX_CHARS) return [{ type: "truncated" }];
|
|
553
|
+
const lines = src.replace(/\r\n?/g, "\n").split("\n");
|
|
554
|
+
const blocks = [];
|
|
555
|
+
let para = [];
|
|
556
|
+
const flushPara = () => {
|
|
557
|
+
if (!para.length) return;
|
|
558
|
+
blocks.push({ type: "paragraph", children: parseInline(para.join(" ")) });
|
|
559
|
+
para = [];
|
|
560
|
+
};
|
|
561
|
+
for (let i = 0; i < lines.length; i++) {
|
|
562
|
+
const line = lines[i];
|
|
563
|
+
const fence = FENCE_RE.exec(line);
|
|
564
|
+
if (fence) {
|
|
565
|
+
flushPara();
|
|
566
|
+
const body = [];
|
|
567
|
+
i++;
|
|
568
|
+
while (i < lines.length && !FENCE_RE.test(lines[i])) body.push(lines[i++]);
|
|
569
|
+
blocks.push({ type: "codeBlock", label: fenceLabel(fence[1]), value: body.join("\n") });
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (!line.trim()) {
|
|
573
|
+
flushPara();
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
if (HR_RE.test(line)) {
|
|
577
|
+
flushPara();
|
|
578
|
+
blocks.push({ type: "hr" });
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
const heading = HEADING_RE.exec(line);
|
|
582
|
+
if (heading) {
|
|
583
|
+
flushPara();
|
|
584
|
+
blocks.push({
|
|
585
|
+
type: "heading",
|
|
586
|
+
level: heading[1].length + 1,
|
|
587
|
+
children: parseInline(heading[2])
|
|
588
|
+
});
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
const ul = UL_RE.exec(line);
|
|
592
|
+
const ol = ul ? null : OL_RE.exec(line);
|
|
593
|
+
if (ul || ol) {
|
|
594
|
+
flushPara();
|
|
595
|
+
const ordered = !!ol;
|
|
596
|
+
const itemRe = ordered ? OL_RE : UL_RE;
|
|
597
|
+
const items = [];
|
|
598
|
+
while (i < lines.length) {
|
|
599
|
+
const item = itemRe.exec(lines[i]);
|
|
600
|
+
if (!item) break;
|
|
601
|
+
items.push(parseInline(item[1]));
|
|
602
|
+
i++;
|
|
603
|
+
}
|
|
604
|
+
i--;
|
|
605
|
+
blocks.push({ type: "list", ordered, items });
|
|
606
|
+
continue;
|
|
558
607
|
}
|
|
608
|
+
para.push(line);
|
|
559
609
|
}
|
|
560
|
-
|
|
610
|
+
flushPara();
|
|
611
|
+
const weight = blocks.reduce((n, b) => n + (b.type === "list" ? b.items.length : 1), 0);
|
|
612
|
+
return weight > MD_MAX_BLOCKS ? [{ type: "truncated" }] : blocks;
|
|
561
613
|
}
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
validate: "validate"
|
|
585
|
-
};
|
|
586
|
-
const ROOT_ERROR_TYPE = "root";
|
|
587
|
-
const PROTOTYPE_KEYWORDS = ["__proto__", "constructor", "prototype"];
|
|
588
|
-
const IS_KEY_RE = /^\w*$/;
|
|
589
|
-
var isKey = (value) => IS_KEY_RE.test(value);
|
|
590
|
-
var isUndefined = (val) => val === void 0;
|
|
591
|
-
const FIELD_PATH_RE = /[.[\]'"]/;
|
|
592
|
-
var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
|
|
593
|
-
var get = (object, path, defaultValue) => {
|
|
594
|
-
if (!path || !isObject(object)) {
|
|
595
|
-
return defaultValue;
|
|
614
|
+
const Inline = ({ nodes }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: nodes.map((node, i) => {
|
|
615
|
+
switch (node.type) {
|
|
616
|
+
case "text":
|
|
617
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.value }, i);
|
|
618
|
+
case "code":
|
|
619
|
+
return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "txt-compact-small bg-ui-bg-component rounded px-1 py-0.5", children: node.value }, i);
|
|
620
|
+
case "strong":
|
|
621
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-small-plus", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
622
|
+
case "em":
|
|
623
|
+
return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
624
|
+
case "link":
|
|
625
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
626
|
+
"a",
|
|
627
|
+
{
|
|
628
|
+
href: node.href,
|
|
629
|
+
target: "_blank",
|
|
630
|
+
rel: "noopener noreferrer",
|
|
631
|
+
className: "text-ui-fg-interactive hover:underline",
|
|
632
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children })
|
|
633
|
+
},
|
|
634
|
+
i
|
|
635
|
+
);
|
|
596
636
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
637
|
+
}) });
|
|
638
|
+
const Block = ({ block, truncatedLabel }) => {
|
|
639
|
+
switch (block.type) {
|
|
640
|
+
case "heading":
|
|
641
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: block.level === 2 ? "h2" : "h3", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
642
|
+
case "paragraph":
|
|
643
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
644
|
+
case "list": {
|
|
645
|
+
const List = block.ordered ? "ol" : "ul";
|
|
646
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
647
|
+
List,
|
|
648
|
+
{
|
|
649
|
+
className: `text-ui-fg-subtle txt-small flex flex-col gap-y-1 pl-5 ${block.ordered ? "list-decimal" : "list-disc"}`,
|
|
650
|
+
children: block.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: item }) }, i))
|
|
651
|
+
}
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
case "codeBlock":
|
|
655
|
+
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: [
|
|
656
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-2", children: [
|
|
657
|
+
block.label ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-subtle", children: block.label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
658
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: block.value })
|
|
659
|
+
] }),
|
|
660
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "txt-compact-small text-ui-fg-subtle overflow-x-auto whitespace-pre pb-2", children: block.value })
|
|
661
|
+
] });
|
|
662
|
+
case "hr":
|
|
663
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-ui-border-base border-t" });
|
|
664
|
+
case "truncated":
|
|
665
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: truncatedLabel });
|
|
600
666
|
}
|
|
601
|
-
const result = paths.reduce((result2, key) => {
|
|
602
|
-
return isNullOrUndefined(result2) ? void 0 : result2[key];
|
|
603
|
-
}, object);
|
|
604
|
-
return isUndefined(result) || result === object ? isUndefined(object[path]) ? defaultValue : object[path] : result;
|
|
605
667
|
};
|
|
606
|
-
|
|
607
|
-
|
|
668
|
+
const Markdown = ({
|
|
669
|
+
source,
|
|
670
|
+
truncatedLabel
|
|
671
|
+
}) => {
|
|
672
|
+
const blocks = React.useMemo(() => parseMarkdown(source), [source]);
|
|
673
|
+
if (!blocks.length) return null;
|
|
674
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-y-4", children: blocks.map((block, i) => /* @__PURE__ */ jsxRuntime.jsx(Block, { block, truncatedLabel }, i)) });
|
|
675
|
+
};
|
|
676
|
+
const PUBLISH_URL = "https://docs.gorgojs.com/medusa-modules/integration";
|
|
677
|
+
const API_URL = "https://gorgojs.com";
|
|
678
|
+
const iconSrc = (icon) => icon.startsWith("data:") ? icon : API_URL + icon;
|
|
679
|
+
const InstallModal = ({
|
|
680
|
+
item,
|
|
681
|
+
onOpenChange
|
|
682
|
+
}) => {
|
|
683
|
+
const { t, i18n } = reactI18next.useTranslation();
|
|
684
|
+
const lang = i18n.language.split("-")[0] === "ru" ? "ru" : "en";
|
|
685
|
+
const source = item?.docsSnippet?.[lang] || item?.docsSnippet?.en || item?.docsSnippet?.ru || "";
|
|
686
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: !!item, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
687
|
+
/* @__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: [
|
|
688
|
+
item && /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label, size: "small" }),
|
|
689
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: item?.label })
|
|
690
|
+
] }) }) }),
|
|
691
|
+
/* @__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") }) }),
|
|
692
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
693
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: t("integration.actions.cancel") }) }),
|
|
694
|
+
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: [
|
|
695
|
+
t("integration.browse.install_docs"),
|
|
696
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, {})
|
|
697
|
+
] }) })
|
|
698
|
+
] })
|
|
699
|
+
] }) });
|
|
700
|
+
};
|
|
701
|
+
const BrowsePage = () => {
|
|
702
|
+
const { t } = reactI18next.useTranslation();
|
|
703
|
+
const navigate = reactRouterDom.useNavigate();
|
|
704
|
+
const [activeTab, setActiveTab] = React.useState("all");
|
|
705
|
+
const [search, setSearch] = React.useState("");
|
|
706
|
+
const [installItem, setInstallItem] = React.useState(null);
|
|
707
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
708
|
+
queryKey: ["integration-catalog"],
|
|
709
|
+
queryFn: () => sdk.client.fetch("/admin/integrations/catalog")
|
|
710
|
+
});
|
|
711
|
+
const items = data?.integrations ?? [];
|
|
712
|
+
const categories = React.useMemo(() => [...new Set(items.map((i) => i.category))], [items]);
|
|
713
|
+
const q = search.trim().toLowerCase();
|
|
714
|
+
const filtered = items.filter((i) => {
|
|
715
|
+
if (activeTab !== "all" && i.category !== activeTab) return false;
|
|
716
|
+
if (q && ![i.label, i.shortDescription, i.npm].some((s) => s.toLowerCase().includes(q))) return false;
|
|
717
|
+
return true;
|
|
718
|
+
});
|
|
719
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3", children: [
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
721
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
723
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("integration.browse.title") }),
|
|
724
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.subtitle") })
|
|
725
|
+
] }),
|
|
726
|
+
false
|
|
727
|
+
] }),
|
|
728
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 px-6 py-4", children: [
|
|
729
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs, { value: activeTab, onValueChange: setActiveTab, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "all", children: t("integration.list.tabs.all") }),
|
|
731
|
+
categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: c, children: t(`integration.categories.${c}`) }, c))
|
|
732
|
+
] }) }),
|
|
733
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-64", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
734
|
+
ui.Input,
|
|
735
|
+
{
|
|
736
|
+
placeholder: t("integration.browse.searchPlaceholder"),
|
|
737
|
+
value: search,
|
|
738
|
+
onChange: (e) => setSearch(e.target.value)
|
|
739
|
+
}
|
|
740
|
+
) })
|
|
741
|
+
] })
|
|
742
|
+
] }),
|
|
743
|
+
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: [
|
|
744
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-y-3 p-4", children: [
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-x-2", children: [
|
|
746
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
747
|
+
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label }),
|
|
748
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
749
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: item.label }),
|
|
750
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: t("integration.browse.by", { author: item.authorLocalized }) })
|
|
751
|
+
] })
|
|
752
|
+
] }),
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: item.installed ? "green" : "grey", children: item.installed ? t("integration.browse.installed") : t("integration.browse.notInstalled") })
|
|
754
|
+
] }),
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle line-clamp-2", children: item.shortDescription })
|
|
756
|
+
] }),
|
|
757
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
|
|
758
|
+
item.installed && item.provider_id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
759
|
+
ui.Button,
|
|
760
|
+
{
|
|
761
|
+
size: "small",
|
|
762
|
+
variant: "primary",
|
|
763
|
+
onClick: () => navigate(`/settings/integrations/${item.provider_id}`),
|
|
764
|
+
children: t("integration.browse.settings")
|
|
765
|
+
}
|
|
766
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", variant: "secondary", onClick: () => setInstallItem(item), children: [
|
|
767
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
768
|
+
t("integration.browse.add")
|
|
769
|
+
] }),
|
|
770
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(`integration.categories.${item.category}`) })
|
|
771
|
+
] })
|
|
772
|
+
] }, item.integrationId)) }),
|
|
773
|
+
/* @__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: [
|
|
774
|
+
t("integration.browse.learnMore"),
|
|
775
|
+
" →"
|
|
776
|
+
] }) }),
|
|
777
|
+
/* @__PURE__ */ jsxRuntime.jsx(InstallModal, { item: installItem, onOpenChange: (open) => !open && setInstallItem(null) })
|
|
778
|
+
] });
|
|
779
|
+
};
|
|
780
|
+
const BrowseBreadcrumb = () => {
|
|
781
|
+
const { t } = reactI18next.useTranslation();
|
|
782
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("integration.browse.title") });
|
|
783
|
+
};
|
|
784
|
+
const handle$1 = {
|
|
785
|
+
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(BrowseBreadcrumb, {})
|
|
786
|
+
};
|
|
787
|
+
const Dash = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "text-ui-fg-subtle", children: "-" });
|
|
788
|
+
const IntegrationFieldValue = ({
|
|
789
|
+
field,
|
|
790
|
+
value,
|
|
791
|
+
secretConfigured = false
|
|
792
|
+
}) => {
|
|
793
|
+
const { t } = reactI18next.useTranslation();
|
|
794
|
+
if (field.secret || field.control === "secret") {
|
|
795
|
+
return secretConfigured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "••••••••" }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
796
|
+
}
|
|
797
|
+
if (field.control === "switch") {
|
|
798
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
799
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: value ? "green" : "grey", children: value ? t("integration.status.enabled") : t("integration.status.disabled") });
|
|
800
|
+
}
|
|
801
|
+
if (field.control === "json") {
|
|
802
|
+
if (Array.isArray(value)) {
|
|
803
|
+
return value.length ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.form.items", { count: value.length }) }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
804
|
+
}
|
|
805
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
806
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.status.configured") });
|
|
807
|
+
}
|
|
808
|
+
if (value == null || value === "") return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
809
|
+
if (field.control === "select") {
|
|
810
|
+
const labelKey = field.optionLabels?.[String(value)];
|
|
811
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: labelKey ? t(labelKey) : String(value) });
|
|
812
|
+
}
|
|
813
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: String(value) });
|
|
814
|
+
};
|
|
815
|
+
var isCheckBoxInput = (element) => element.type === "checkbox";
|
|
816
|
+
var isFileInput = (element) => element.type === "file";
|
|
817
|
+
var isDateObject = (value) => value instanceof Date;
|
|
818
|
+
var isNullOrUndefined = (value) => value == null;
|
|
819
|
+
const isObjectType = (value) => typeof value === "object";
|
|
820
|
+
var isObject = (value) => !isNullOrUndefined(value) && !Array.isArray(value) && isObjectType(value) && !isDateObject(value);
|
|
821
|
+
var getEventValue = (event) => isObject(event) && event.target ? isCheckBoxInput(event.target) ? event.target.checked : isFileInput(event.target) ? event.target.files : event.target.value : event;
|
|
822
|
+
var isNameInFieldArray = (names, name) => name.split(".").some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join(".")));
|
|
823
|
+
var isPlainObject = (tempObject) => {
|
|
824
|
+
const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
|
|
825
|
+
return isObject(prototypeCopy) && prototypeCopy.hasOwnProperty("isPrototypeOf");
|
|
826
|
+
};
|
|
827
|
+
var isWeb = typeof window !== "undefined" && typeof window.HTMLElement !== "undefined" && typeof document !== "undefined";
|
|
828
|
+
function cloneObject(data) {
|
|
829
|
+
if (data instanceof Date) {
|
|
830
|
+
return new Date(data);
|
|
831
|
+
}
|
|
832
|
+
const isFileListInstance = typeof FileList !== "undefined" && data instanceof FileList;
|
|
833
|
+
if (isWeb && (data instanceof Blob || isFileListInstance)) {
|
|
834
|
+
return data;
|
|
835
|
+
}
|
|
836
|
+
const isArray = Array.isArray(data);
|
|
837
|
+
if (!isArray && !(isObject(data) && isPlainObject(data))) {
|
|
838
|
+
return data;
|
|
839
|
+
}
|
|
840
|
+
const copy = isArray ? [] : Object.create(Object.getPrototypeOf(data));
|
|
841
|
+
for (const key in data) {
|
|
842
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
843
|
+
copy[key] = cloneObject(data[key]);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return copy;
|
|
847
|
+
}
|
|
848
|
+
const EVENTS = {
|
|
849
|
+
BLUR: "blur",
|
|
850
|
+
FOCUS_OUT: "focusout",
|
|
851
|
+
CHANGE: "change",
|
|
852
|
+
SUBMIT: "submit",
|
|
853
|
+
TRIGGER: "trigger",
|
|
854
|
+
VALID: "valid"
|
|
855
|
+
};
|
|
856
|
+
const VALIDATION_MODE = {
|
|
857
|
+
onBlur: "onBlur",
|
|
858
|
+
onChange: "onChange",
|
|
859
|
+
onSubmit: "onSubmit",
|
|
860
|
+
onTouched: "onTouched",
|
|
861
|
+
all: "all"
|
|
862
|
+
};
|
|
863
|
+
const INPUT_VALIDATION_RULES = {
|
|
864
|
+
max: "max",
|
|
865
|
+
min: "min",
|
|
866
|
+
maxLength: "maxLength",
|
|
867
|
+
minLength: "minLength",
|
|
868
|
+
pattern: "pattern",
|
|
869
|
+
required: "required",
|
|
870
|
+
validate: "validate"
|
|
871
|
+
};
|
|
872
|
+
const ROOT_ERROR_TYPE = "root";
|
|
873
|
+
const PROTOTYPE_KEYWORDS = ["__proto__", "constructor", "prototype"];
|
|
874
|
+
const IS_KEY_RE = /^\w*$/;
|
|
875
|
+
var isKey = (value) => IS_KEY_RE.test(value);
|
|
876
|
+
var isUndefined = (val) => val === void 0;
|
|
877
|
+
const FIELD_PATH_RE = /[.[\]'"]/;
|
|
878
|
+
var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
|
|
879
|
+
var get = (object, path, defaultValue) => {
|
|
880
|
+
if (!path || !isObject(object)) {
|
|
881
|
+
return defaultValue;
|
|
882
|
+
}
|
|
883
|
+
const paths = isKey(path) ? [path] : stringToPath(path);
|
|
884
|
+
if (paths.some((key) => PROTOTYPE_KEYWORDS.includes(key))) {
|
|
885
|
+
return defaultValue;
|
|
886
|
+
}
|
|
887
|
+
const result = paths.reduce((result2, key) => {
|
|
888
|
+
return isNullOrUndefined(result2) ? void 0 : result2[key];
|
|
889
|
+
}, object);
|
|
890
|
+
return isUndefined(result) || result === object ? isUndefined(object[path]) ? defaultValue : object[path] : result;
|
|
891
|
+
};
|
|
892
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
893
|
+
var isFunction = (value) => typeof value === "function";
|
|
608
894
|
var set = (object, path, value) => {
|
|
609
895
|
let index = -1;
|
|
610
896
|
const tempPath = isKey(path) ? [path] : stringToPath(path);
|
|
@@ -2630,881 +2916,595 @@ function createFormControl(props = {}) {
|
|
|
2630
2916
|
watch,
|
|
2631
2917
|
setValue,
|
|
2632
2918
|
setValues,
|
|
2633
|
-
getValues,
|
|
2634
|
-
reset,
|
|
2635
|
-
resetField,
|
|
2636
|
-
resetDefaultValues,
|
|
2637
|
-
clearErrors,
|
|
2638
|
-
unregister,
|
|
2639
|
-
setError,
|
|
2640
|
-
setFocus,
|
|
2641
|
-
getFieldState
|
|
2642
|
-
};
|
|
2643
|
-
return {
|
|
2644
|
-
...methods,
|
|
2645
|
-
formControl: methods
|
|
2646
|
-
};
|
|
2647
|
-
}
|
|
2648
|
-
function useForm(props = {}) {
|
|
2649
|
-
const _formControl = React__default.default.useRef(void 0);
|
|
2650
|
-
const _values = React__default.default.useRef(void 0);
|
|
2651
|
-
const _formControlProp = React__default.default.useRef(props.formControl);
|
|
2652
|
-
const [formState, updateFormState] = React__default.default.useState(() => ({
|
|
2653
|
-
...cloneObject(DEFAULT_FORM_STATE),
|
|
2654
|
-
isLoading: isFunction(props.defaultValues),
|
|
2655
|
-
errors: props.errors || {},
|
|
2656
|
-
disabled: props.disabled || false,
|
|
2657
|
-
defaultValues: isFunction(props.defaultValues) ? void 0 : props.defaultValues
|
|
2658
|
-
}));
|
|
2659
|
-
if (!_formControl.current || props.formControl && _formControlProp.current !== props.formControl) {
|
|
2660
|
-
_formControlProp.current = props.formControl;
|
|
2661
|
-
if (props.formControl) {
|
|
2662
|
-
_formControl.current = {
|
|
2663
|
-
...props.formControl,
|
|
2664
|
-
formState
|
|
2665
|
-
};
|
|
2666
|
-
if (props.defaultValues && !isFunction(props.defaultValues)) {
|
|
2667
|
-
props.formControl.reset(props.defaultValues, props.resetOptions);
|
|
2668
|
-
}
|
|
2669
|
-
} else {
|
|
2670
|
-
const { formControl, ...rest } = createFormControl(props);
|
|
2671
|
-
_formControl.current = {
|
|
2672
|
-
...rest,
|
|
2673
|
-
formState
|
|
2674
|
-
};
|
|
2675
|
-
}
|
|
2676
|
-
}
|
|
2677
|
-
const control = _formControl.current.control;
|
|
2678
|
-
control._options = props;
|
|
2679
|
-
useIsomorphicLayoutEffect(() => {
|
|
2680
|
-
const sub = control._subscribe({
|
|
2681
|
-
formState: control._proxyFormState,
|
|
2682
|
-
callback: () => updateFormState({
|
|
2683
|
-
...control._formState,
|
|
2684
|
-
defaultValues: control._defaultValues
|
|
2685
|
-
}),
|
|
2686
|
-
reRenderRoot: true
|
|
2687
|
-
});
|
|
2688
|
-
updateFormState((data) => ({
|
|
2689
|
-
...data,
|
|
2690
|
-
isReady: true
|
|
2691
|
-
}));
|
|
2692
|
-
control._formState.isReady = true;
|
|
2693
|
-
return sub;
|
|
2694
|
-
}, [control]);
|
|
2695
|
-
React__default.default.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
|
|
2696
|
-
React__default.default.useEffect(() => {
|
|
2697
|
-
if (props.mode) {
|
|
2698
|
-
control._options.mode = props.mode;
|
|
2699
|
-
}
|
|
2700
|
-
if (props.reValidateMode) {
|
|
2701
|
-
control._options.reValidateMode = props.reValidateMode;
|
|
2702
|
-
}
|
|
2703
|
-
}, [control, props.mode, props.reValidateMode]);
|
|
2704
|
-
React__default.default.useEffect(() => {
|
|
2705
|
-
if (props.errors) {
|
|
2706
|
-
control._setErrors(props.errors);
|
|
2707
|
-
control._focusError();
|
|
2708
|
-
}
|
|
2709
|
-
}, [control, props.errors]);
|
|
2710
|
-
React__default.default.useEffect(() => {
|
|
2711
|
-
props.shouldUnregister && control._subjects.state.next({
|
|
2712
|
-
values: control._getWatch()
|
|
2713
|
-
});
|
|
2714
|
-
}, [control, props.shouldUnregister]);
|
|
2715
|
-
React__default.default.useEffect(() => {
|
|
2716
|
-
if (control._proxyFormState.isDirty) {
|
|
2717
|
-
const isDirty = control._getDirty();
|
|
2718
|
-
if (isDirty !== formState.isDirty) {
|
|
2719
|
-
control._subjects.state.next({
|
|
2720
|
-
isDirty
|
|
2721
|
-
});
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
|
-
}, [control, formState.isDirty]);
|
|
2725
|
-
React__default.default.useEffect(() => {
|
|
2726
|
-
var _a;
|
|
2727
|
-
if (props.values && !deepEqual(props.values, _values.current)) {
|
|
2728
|
-
control._reset(props.values, {
|
|
2729
|
-
keepFieldsRef: true,
|
|
2730
|
-
...control._options.resetOptions
|
|
2731
|
-
});
|
|
2732
|
-
if (!((_a = control._options.resetOptions) === null || _a === void 0 ? void 0 : _a.keepIsValid)) {
|
|
2733
|
-
control._setValid();
|
|
2734
|
-
}
|
|
2735
|
-
_values.current = props.values;
|
|
2736
|
-
updateFormState((state) => ({ ...state }));
|
|
2737
|
-
} else {
|
|
2738
|
-
control._resetDefaultValues();
|
|
2739
|
-
}
|
|
2740
|
-
}, [control, props.values]);
|
|
2741
|
-
React__default.default.useEffect(() => {
|
|
2742
|
-
if (!control._state.mount) {
|
|
2743
|
-
control._setValid();
|
|
2744
|
-
control._state.mount = true;
|
|
2745
|
-
}
|
|
2746
|
-
if (control._state.watch) {
|
|
2747
|
-
control._state.watch = false;
|
|
2748
|
-
control._subjects.state.next({ ...control._formState });
|
|
2749
|
-
}
|
|
2750
|
-
control._removeUnmounted();
|
|
2751
|
-
});
|
|
2752
|
-
_formControl.current.formState = React__default.default.useMemo(() => getProxyFormState(formState, control), [control, formState]);
|
|
2753
|
-
return _formControl.current;
|
|
2754
|
-
}
|
|
2755
|
-
const jsonToText = (v) => v == null || v === "" ? "" : JSON.stringify(v, null, 2);
|
|
2756
|
-
const JsonField = ({ field, f }) => {
|
|
2757
|
-
const { t } = reactI18next.useTranslation();
|
|
2758
|
-
const [raw, setRaw] = React.useState(() => jsonToText(f.value));
|
|
2759
|
-
const [error, setError] = React.useState(null);
|
|
2760
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2761
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2762
|
-
ui.Textarea,
|
|
2763
|
-
{
|
|
2764
|
-
rows: 6,
|
|
2765
|
-
className: "font-mono",
|
|
2766
|
-
placeholder: field.placeholder ? t(field.placeholder) : "[]",
|
|
2767
|
-
value: raw,
|
|
2768
|
-
disabled: field.readonly === true,
|
|
2769
|
-
onBlur: f.onBlur,
|
|
2770
|
-
onChange: (e) => {
|
|
2771
|
-
const text2 = e.target.value;
|
|
2772
|
-
setRaw(text2);
|
|
2773
|
-
if (text2.trim() === "") {
|
|
2774
|
-
setError(null);
|
|
2775
|
-
f.onChange(null);
|
|
2776
|
-
return;
|
|
2777
|
-
}
|
|
2778
|
-
try {
|
|
2779
|
-
f.onChange(JSON.parse(text2));
|
|
2780
|
-
setError(null);
|
|
2781
|
-
} catch {
|
|
2782
|
-
setError(t("integration.form.invalidJson"));
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
|
-
),
|
|
2787
|
-
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: error })
|
|
2788
|
-
] });
|
|
2789
|
-
};
|
|
2790
|
-
const IntegrationField = ({
|
|
2791
|
-
field,
|
|
2792
|
-
control,
|
|
2793
|
-
secretConfigured = false
|
|
2794
|
-
}) => {
|
|
2795
|
-
const { t } = reactI18next.useTranslation();
|
|
2796
|
-
const enforceRequired = field.required && field.control !== "switch" && !(field.control === "secret" && secretConfigured);
|
|
2797
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2798
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { size: "small", weight: "plus", children: [
|
|
2799
|
-
t(field.label),
|
|
2800
|
-
field.required ? " *" : ""
|
|
2801
|
-
] }),
|
|
2802
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2803
|
-
Controller,
|
|
2804
|
-
{
|
|
2805
|
-
name: field.name,
|
|
2806
|
-
control,
|
|
2807
|
-
rules: {
|
|
2808
|
-
required: enforceRequired ? t("integration.form.required", { field: t(field.label) }) : false
|
|
2809
|
-
},
|
|
2810
|
-
render: ({ field: f, fieldState }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2811
|
-
renderControl(field, f, secretConfigured, t),
|
|
2812
|
-
fieldState.error?.message && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: fieldState.error.message })
|
|
2813
|
-
] })
|
|
2814
|
-
}
|
|
2815
|
-
),
|
|
2816
|
-
field.hint && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: t(field.hint) })
|
|
2817
|
-
] });
|
|
2818
|
-
};
|
|
2819
|
-
function renderControl(field, f, secretConfigured, t) {
|
|
2820
|
-
const placeholder = field.placeholder ? t(field.placeholder) : void 0;
|
|
2821
|
-
const disabled = field.readonly === true;
|
|
2822
|
-
switch (field.control) {
|
|
2823
|
-
case "switch":
|
|
2824
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: !!f.value, onCheckedChange: f.onChange, disabled });
|
|
2825
|
-
case "select": {
|
|
2826
|
-
const declared = field.options ?? [];
|
|
2827
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2828
|
-
ui.Select,
|
|
2829
|
-
{
|
|
2830
|
-
value: f.value == null ? "" : String(f.value),
|
|
2831
|
-
onValueChange: (next) => f.onChange(declared.find((o) => String(o) === next) ?? next),
|
|
2832
|
-
disabled,
|
|
2833
|
-
children: [
|
|
2834
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder }) }),
|
|
2835
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: declared.map((o) => {
|
|
2836
|
-
const labelKey = field.optionLabels?.[String(o)];
|
|
2837
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: String(o), children: labelKey ? t(labelKey) : String(o) }, String(o));
|
|
2838
|
-
}) })
|
|
2839
|
-
]
|
|
2840
|
-
}
|
|
2841
|
-
);
|
|
2842
|
-
}
|
|
2843
|
-
case "number":
|
|
2844
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2845
|
-
ui.Input,
|
|
2846
|
-
{
|
|
2847
|
-
type: "number",
|
|
2848
|
-
placeholder,
|
|
2849
|
-
value: f.value ?? "",
|
|
2850
|
-
disabled,
|
|
2851
|
-
onChange: (e) => f.onChange(e.target.value === "" ? null : e.target.valueAsNumber)
|
|
2852
|
-
}
|
|
2853
|
-
);
|
|
2854
|
-
case "url":
|
|
2855
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2856
|
-
ui.Input,
|
|
2857
|
-
{
|
|
2858
|
-
type: "url",
|
|
2859
|
-
placeholder,
|
|
2860
|
-
value: f.value ?? "",
|
|
2861
|
-
disabled,
|
|
2862
|
-
onChange: f.onChange
|
|
2863
|
-
}
|
|
2864
|
-
);
|
|
2865
|
-
case "secret":
|
|
2866
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2867
|
-
ui.Input,
|
|
2868
|
-
{
|
|
2869
|
-
type: "password",
|
|
2870
|
-
placeholder: secretConfigured ? t("integration.form.keepSecret") : placeholder ?? "",
|
|
2871
|
-
value: f.value ?? "",
|
|
2872
|
-
disabled,
|
|
2873
|
-
onChange: f.onChange,
|
|
2874
|
-
className: !f.value ? "[&~div]:hidden" : void 0
|
|
2875
|
-
}
|
|
2876
|
-
);
|
|
2877
|
-
case "json":
|
|
2878
|
-
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { field, f });
|
|
2879
|
-
default:
|
|
2880
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2881
|
-
ui.Input,
|
|
2882
|
-
{
|
|
2883
|
-
placeholder,
|
|
2884
|
-
value: f.value ?? "",
|
|
2885
|
-
disabled,
|
|
2886
|
-
onChange: f.onChange
|
|
2887
|
-
}
|
|
2888
|
-
);
|
|
2889
|
-
}
|
|
2890
|
-
}
|
|
2891
|
-
function isFieldVisible(field, values) {
|
|
2892
|
-
const rule = field.visibleWhen;
|
|
2893
|
-
if (!rule) return true;
|
|
2894
|
-
return values[rule.field] === rule.equals;
|
|
2895
|
-
}
|
|
2896
|
-
function sectionDefaults(section, values) {
|
|
2897
|
-
const out = {};
|
|
2898
|
-
for (const f of section.fields) {
|
|
2899
|
-
if (f.secret || f.control === "secret") out[f.name] = "";
|
|
2900
|
-
else if (f.control === "switch") out[f.name] = values?.[f.name] ?? f.default ?? false;
|
|
2901
|
-
else if (f.control === "json") out[f.name] = values?.[f.name] ?? f.default;
|
|
2902
|
-
else out[f.name] = values?.[f.name] ?? f.default ?? "";
|
|
2903
|
-
}
|
|
2904
|
-
return out;
|
|
2905
|
-
}
|
|
2906
|
-
const EditSectionDrawer = ({
|
|
2907
|
-
open,
|
|
2908
|
-
onOpenChange,
|
|
2909
|
-
providerId,
|
|
2910
|
-
section,
|
|
2911
|
-
values,
|
|
2912
|
-
configuredSecrets = []
|
|
2913
|
-
}) => {
|
|
2914
|
-
const { t } = reactI18next.useTranslation();
|
|
2915
|
-
const qc = reactQuery.useQueryClient();
|
|
2916
|
-
const configured = new Set(configuredSecrets);
|
|
2917
|
-
const [defaults] = React.useState(() => sectionDefaults(section, values));
|
|
2918
|
-
const form = useForm({ values: defaults });
|
|
2919
|
-
const current = form.watch();
|
|
2920
|
-
const visibilityValues = { ...values, ...current };
|
|
2921
|
-
const save = reactQuery.useMutation({
|
|
2922
|
-
mutationFn: (sectionValues) => sdk.client.fetch(`/admin/integrations/${providerId}`, {
|
|
2923
|
-
method: "POST",
|
|
2924
|
-
// Only this section's values + its id — the server validates just this section and
|
|
2925
|
-
// merges over the rest of the stored config (keeping blank/untouched secrets).
|
|
2926
|
-
body: { section_id: section.id, values: sectionValues }
|
|
2927
|
-
}),
|
|
2928
|
-
onSuccess: () => {
|
|
2929
|
-
ui.toast.success(t("integration.toast.saved"));
|
|
2930
|
-
qc.invalidateQueries({ queryKey: ["integration", providerId] });
|
|
2931
|
-
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
2932
|
-
onOpenChange(false);
|
|
2933
|
-
},
|
|
2934
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.saveFailed"))
|
|
2935
|
-
});
|
|
2936
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
2937
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: t(section.title) }) }),
|
|
2938
|
-
/* @__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(
|
|
2939
|
-
IntegrationField,
|
|
2940
|
-
{
|
|
2941
|
-
field: f,
|
|
2942
|
-
control: form.control,
|
|
2943
|
-
secretConfigured: configured.has(f.name)
|
|
2944
|
-
},
|
|
2945
|
-
f.name
|
|
2946
|
-
)) }),
|
|
2947
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
2948
|
-
/* @__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") }) }),
|
|
2949
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2950
|
-
ui.Button,
|
|
2951
|
-
{
|
|
2952
|
-
size: "small",
|
|
2953
|
-
isLoading: save.isPending,
|
|
2954
|
-
onClick: form.handleSubmit((v) => save.mutate(v)),
|
|
2955
|
-
children: t("integration.actions.save")
|
|
2956
|
-
}
|
|
2957
|
-
)
|
|
2958
|
-
] })
|
|
2959
|
-
] }) });
|
|
2960
|
-
};
|
|
2961
|
-
const SURFACE = {
|
|
2962
|
-
green: "bg-ui-bg-subtle border-ui-border-base",
|
|
2963
|
-
grey: "bg-ui-bg-subtle border-ui-border-base",
|
|
2964
|
-
orange: "bg-ui-tag-orange-bg border-ui-tag-orange-border",
|
|
2965
|
-
red: "bg-ui-tag-red-bg border-ui-tag-red-border"
|
|
2966
|
-
};
|
|
2967
|
-
const RECHECK = {
|
|
2968
|
-
green: void 0,
|
|
2969
|
-
grey: void 0,
|
|
2970
|
-
orange: "hover:bg-ui-tag-orange-bg-hover active:bg-ui-tag-orange-bg-hover",
|
|
2971
|
-
red: "hover:bg-ui-tag-red-bg-hover active:bg-ui-tag-red-bg-hover"
|
|
2972
|
-
};
|
|
2973
|
-
const LicenseSummary = ({ entry }) => {
|
|
2974
|
-
const { t } = reactI18next.useTranslation();
|
|
2975
|
-
const view = useDescribeLicense()(entry);
|
|
2976
|
-
const details = useLicenseDetails()(view);
|
|
2977
|
-
const recheck = useRecheckLicense();
|
|
2978
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.clx("flex items-center gap-x-4 rounded-lg border px-4 py-3", SURFACE[view.tone]), children: [
|
|
2979
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-y-0.5", children: [
|
|
2980
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-1", children: [
|
|
2981
|
-
/* @__PURE__ */ jsxRuntime.jsx(LicenseSquare, { tone: view.tone }),
|
|
2982
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", weight: "plus", className: "text-ui-fg-base", children: t(view.title.key, view.title.values) })
|
|
2983
|
-
] }),
|
|
2984
|
-
details && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: details })
|
|
2985
|
-
] }),
|
|
2986
|
-
entry.state !== "pending" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-x-2", children: [
|
|
2987
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2988
|
-
ui.Button,
|
|
2989
|
-
{
|
|
2990
|
-
size: "small",
|
|
2991
|
-
variant: "transparent",
|
|
2992
|
-
className: RECHECK[view.tone],
|
|
2993
|
-
isLoading: recheck.isPending,
|
|
2994
|
-
disabled: recheck.isPending,
|
|
2995
|
-
onClick: () => recheck.mutate(),
|
|
2996
|
-
children: t("integration.license.recheck")
|
|
2997
|
-
}
|
|
2998
|
-
),
|
|
2999
|
-
view.action && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: view.action.href, target: "_blank", rel: "noopener noreferrer", children: [
|
|
3000
|
-
t(view.action.key),
|
|
3001
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, {})
|
|
3002
|
-
] }) })
|
|
3003
|
-
] })
|
|
3004
|
-
] });
|
|
3005
|
-
};
|
|
3006
|
-
const Row = ({ label, children }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle grid grid-cols-2 px-6 py-4 [overflow-wrap:anywhere]", children: [
|
|
3007
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: label }),
|
|
3008
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-2", children })
|
|
3009
|
-
] });
|
|
3010
|
-
const EditPage = () => {
|
|
3011
|
-
const { t } = reactI18next.useTranslation();
|
|
3012
|
-
const { provider_id } = reactRouterDom.useParams();
|
|
3013
|
-
const qc = reactQuery.useQueryClient();
|
|
3014
|
-
const navigate = reactRouterDom.useNavigate();
|
|
3015
|
-
const prompt = ui.usePrompt();
|
|
3016
|
-
const [editingSection, setEditingSection] = React.useState(null);
|
|
3017
|
-
const { data, isLoading } = reactQuery.useQuery({
|
|
3018
|
-
queryKey: ["integration", provider_id],
|
|
3019
|
-
queryFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`)
|
|
3020
|
-
});
|
|
3021
|
-
const { data: licenseData } = useLicenseStatus();
|
|
3022
|
-
const descriptor = data?.descriptor;
|
|
3023
|
-
const record = data?.integration;
|
|
3024
|
-
const isConfigured = !!record;
|
|
3025
|
-
const isComplete = data?.is_complete ?? false;
|
|
3026
|
-
const invalidate = () => {
|
|
3027
|
-
qc.invalidateQueries({ queryKey: ["integration", provider_id] });
|
|
3028
|
-
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
3029
|
-
};
|
|
3030
|
-
const test = reactQuery.useMutation({
|
|
3031
|
-
mutationFn: () => sdk.client.fetch(
|
|
3032
|
-
`/admin/integrations/${provider_id}/test-connection`,
|
|
3033
|
-
{ method: "POST" }
|
|
3034
|
-
),
|
|
3035
|
-
onSuccess: (r) => {
|
|
3036
|
-
r.status === "passed" ? ui.toast.success(t("integration.toast.connectionOk")) : ui.toast.warning(r.message ?? r.status);
|
|
3037
|
-
invalidate();
|
|
3038
|
-
},
|
|
3039
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.testFailed"))
|
|
3040
|
-
});
|
|
3041
|
-
const toggleEnabled = reactQuery.useMutation({
|
|
3042
|
-
mutationFn: (next) => sdk.client.fetch(`/admin/integrations/${provider_id}/enable`, {
|
|
3043
|
-
method: "POST",
|
|
3044
|
-
body: { is_enabled: next }
|
|
3045
|
-
}),
|
|
3046
|
-
onSuccess: (_d, next) => {
|
|
3047
|
-
ui.toast.success(next ? t("integration.toast.enabled") : t("integration.toast.disabled"));
|
|
3048
|
-
invalidate();
|
|
3049
|
-
},
|
|
3050
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.updateFailed"))
|
|
3051
|
-
});
|
|
3052
|
-
const remove = reactQuery.useMutation({
|
|
3053
|
-
mutationFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`, { method: "DELETE" }),
|
|
3054
|
-
onSuccess: () => {
|
|
3055
|
-
ui.toast.success(t("integration.toast.deleted"));
|
|
3056
|
-
invalidate();
|
|
3057
|
-
navigate("/settings/integrations");
|
|
3058
|
-
},
|
|
3059
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.deleteFailed"))
|
|
3060
|
-
});
|
|
3061
|
-
const onDelete = async () => {
|
|
3062
|
-
const confirmed = await prompt({
|
|
3063
|
-
title: t("integration.delete.title"),
|
|
3064
|
-
description: t("integration.delete.description", {
|
|
3065
|
-
name: descriptor ? t(descriptor.displayName) : ""
|
|
3066
|
-
}),
|
|
3067
|
-
confirmText: t("integration.actions.delete"),
|
|
3068
|
-
cancelText: t("integration.actions.cancel")
|
|
3069
|
-
});
|
|
3070
|
-
if (confirmed) remove.mutate();
|
|
3071
|
-
};
|
|
3072
|
-
if (isLoading) {
|
|
3073
|
-
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" }) });
|
|
3074
|
-
}
|
|
3075
|
-
if (!descriptor) {
|
|
3076
|
-
throw new Medusa.FetchError(
|
|
3077
|
-
`Integration with provider_id: ${provider_id} was not found`,
|
|
3078
|
-
"There is no page at this address",
|
|
3079
|
-
404
|
|
3080
|
-
);
|
|
3081
|
-
}
|
|
3082
|
-
const version = data?.version ?? null;
|
|
3083
|
-
const author = data?.author ?? null;
|
|
3084
|
-
const authorUrl = data?.author_url ?? null;
|
|
3085
|
-
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") };
|
|
3086
|
-
const connection = descriptor.hasTestConnection ? record?.last_test_status === "passed" ? { color: "green", label: t("integration.status.connected") } : record?.last_test_status === "failed" ? { color: "red", label: t("integration.status.connectionFailed") } : null : null;
|
|
3087
|
-
const license = licenseData?.packages.find((entry) => entry.identifiers.includes(descriptor.identifier));
|
|
3088
|
-
const editing = descriptor.sections.find((s) => s.id === editingSection) ?? null;
|
|
3089
|
-
const configuredSecrets = new Set(record?.configured_secrets ?? []);
|
|
3090
|
-
const sectionData = {
|
|
3091
|
-
providerId: provider_id,
|
|
3092
|
-
identifier: descriptor.identifier,
|
|
3093
|
-
values: record?.values ?? {},
|
|
3094
|
-
isComplete
|
|
3095
|
-
};
|
|
3096
|
-
const renderSection = (section) => /* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: section.id, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
3097
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
3098
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t(section.title) }),
|
|
3099
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
3100
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
3101
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: () => setEditingSection(section.id), children: [
|
|
3102
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "text-ui-fg-subtle" }),
|
|
3103
|
-
t("integration.actions.edit")
|
|
3104
|
-
] }) })
|
|
3105
|
-
] })
|
|
3106
|
-
] }),
|
|
3107
|
-
section.fields.filter((f) => isFieldVisible(f, record?.values ?? {})).map((f) => /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t(f.label), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3108
|
-
IntegrationFieldValue,
|
|
3109
|
-
{
|
|
3110
|
-
field: f,
|
|
3111
|
-
value: record?.values?.[f.name] ?? f.default,
|
|
3112
|
-
secretConfigured: configuredSecrets.has(f.name)
|
|
3113
|
-
}
|
|
3114
|
-
) }, f.name))
|
|
3115
|
-
] }, section.id) });
|
|
3116
|
-
const mainSections = descriptor.sections.filter((s) => s.column !== "side");
|
|
3117
|
-
const sideSections = descriptor.sections.filter((s) => s.column === "side");
|
|
3118
|
-
const main = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3119
|
-
/* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: "main", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
3120
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 px-6 py-4", children: [
|
|
3121
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3122
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
3123
|
-
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: descriptor.icon, alt: t(descriptor.displayName) }),
|
|
3124
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
3125
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
3126
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: t(descriptor.displayName) }),
|
|
3127
|
-
provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: toModuleProviderId(provider_id, descriptor.category) })
|
|
3128
|
-
] }),
|
|
3129
|
-
version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
|
|
3130
|
-
t("integration.meta.version", { version }),
|
|
3131
|
-
author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3132
|
-
" • ",
|
|
3133
|
-
t("integration.meta.authorPrefix"),
|
|
3134
|
-
" ",
|
|
3135
|
-
authorUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3136
|
-
"a",
|
|
3137
|
-
{
|
|
3138
|
-
href: authorUrl,
|
|
3139
|
-
target: "_blank",
|
|
3140
|
-
rel: "noreferrer",
|
|
3141
|
-
className: "text-ui-fg-interactive",
|
|
3142
|
-
children: author
|
|
3143
|
-
}
|
|
3144
|
-
) : author
|
|
3145
|
-
] })
|
|
3146
|
-
] })
|
|
3147
|
-
] })
|
|
3148
|
-
] }),
|
|
3149
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
3150
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
|
|
3151
|
-
connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label }),
|
|
3152
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
3153
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
3154
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
|
|
3155
|
-
descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
|
|
3156
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3157
|
-
ui.DropdownMenu.Item,
|
|
3158
|
-
{
|
|
3159
|
-
disabled: toggleEnabled.isPending,
|
|
3160
|
-
onClick: () => toggleEnabled.mutate(!record.is_enabled),
|
|
3161
|
-
children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
|
|
3162
|
-
}
|
|
3163
|
-
),
|
|
3164
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
|
|
3165
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
|
|
3166
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
|
|
3167
|
-
t("integration.actions.delete")
|
|
3168
|
-
] })
|
|
3169
|
-
] })
|
|
3170
|
-
] })
|
|
3171
|
-
] })
|
|
3172
|
-
] }),
|
|
3173
|
-
descriptor.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(descriptor.description) }),
|
|
3174
|
-
license && /* @__PURE__ */ jsxRuntime.jsx(LicenseSummary, { entry: license })
|
|
3175
|
-
] }),
|
|
3176
|
-
/* @__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}`) }) })
|
|
3177
|
-
] }) }),
|
|
3178
|
-
mainSections.map(renderSection),
|
|
3179
|
-
editing && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3180
|
-
EditSectionDrawer,
|
|
3181
|
-
{
|
|
3182
|
-
open: true,
|
|
3183
|
-
onOpenChange: (open) => !open && setEditingSection(null),
|
|
3184
|
-
providerId: provider_id,
|
|
3185
|
-
section: editing,
|
|
3186
|
-
values: record?.values ?? {},
|
|
3187
|
-
configuredSecrets: record?.configured_secrets ?? []
|
|
3188
|
-
},
|
|
3189
|
-
editing.id
|
|
3190
|
-
)
|
|
3191
|
-
] });
|
|
3192
|
-
const side = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: sideSections.map(renderSection) });
|
|
3193
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3194
|
-
components.LayoutComposer,
|
|
3195
|
-
{
|
|
3196
|
-
widgetsZonePrefix: `gorgo.integration.${descriptor.identifier}`,
|
|
3197
|
-
preferredLayoutId: descriptor.preferredLayoutId,
|
|
3198
|
-
sections: { main, side },
|
|
3199
|
-
data: sectionData,
|
|
3200
|
-
hasOutlet: false
|
|
3201
|
-
}
|
|
3202
|
-
);
|
|
3203
|
-
};
|
|
3204
|
-
const IntegrationBreadcrumb = ({ params }) => {
|
|
3205
|
-
const { t } = reactI18next.useTranslation();
|
|
3206
|
-
const providerId = params.provider_id;
|
|
3207
|
-
const { data } = reactQuery.useQuery({
|
|
3208
|
-
queryKey: ["integration", providerId],
|
|
3209
|
-
queryFn: () => sdk.client.fetch(`/admin/integrations/${providerId}`),
|
|
3210
|
-
enabled: !!providerId
|
|
3211
|
-
});
|
|
3212
|
-
const descriptor = data?.descriptor;
|
|
3213
|
-
if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
3214
|
-
const name = t(descriptor.displayName);
|
|
3215
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3216
|
-
name + " ",
|
|
3217
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: providerId })
|
|
3218
|
-
] });
|
|
3219
|
-
};
|
|
3220
|
-
const handle$1 = {
|
|
3221
|
-
breadcrumb: ({ params }) => /* @__PURE__ */ jsxRuntime.jsx(IntegrationBreadcrumb, { params })
|
|
3222
|
-
};
|
|
3223
|
-
function isSafeHref(raw) {
|
|
3224
|
-
try {
|
|
3225
|
-
return new URL(raw).protocol === "https:";
|
|
3226
|
-
} catch {
|
|
3227
|
-
return false;
|
|
3228
|
-
}
|
|
3229
|
-
}
|
|
3230
|
-
const INLINE_RE = /`([^`\n]+)`|\[([^\]\n]*)\]\(([^)\s]+)\)|\*\*([^*\n]+)\*\*|\*([^*\n]+)\*|_([^_\n]+)_/;
|
|
3231
|
-
const MAX_DEPTH = 3;
|
|
3232
|
-
function parseInline(src, depth = 0) {
|
|
3233
|
-
if (!src) return [];
|
|
3234
|
-
if (depth >= MAX_DEPTH) return [{ type: "text", value: src }];
|
|
3235
|
-
const out = [];
|
|
3236
|
-
const re = new RegExp(INLINE_RE, "g");
|
|
3237
|
-
let last = 0;
|
|
3238
|
-
let m;
|
|
3239
|
-
while ((m = re.exec(src)) !== null) {
|
|
3240
|
-
const [full, code, linkText, linkHref, strong, star, underscore] = m;
|
|
3241
|
-
if (m.index > last) out.push({ type: "text", value: src.slice(last, m.index) });
|
|
3242
|
-
last = m.index + full.length;
|
|
3243
|
-
if (code !== void 0) {
|
|
3244
|
-
out.push({ type: "code", value: code });
|
|
3245
|
-
} else if (linkHref !== void 0) {
|
|
3246
|
-
const isImage = m.index > 0 && src[m.index - 1] === "!";
|
|
3247
|
-
if (!isImage && isSafeHref(linkHref)) {
|
|
3248
|
-
out.push({ type: "link", href: linkHref, children: parseInline(linkText, depth + 1) });
|
|
3249
|
-
} else {
|
|
3250
|
-
out.push({ type: "text", value: full });
|
|
2919
|
+
getValues,
|
|
2920
|
+
reset,
|
|
2921
|
+
resetField,
|
|
2922
|
+
resetDefaultValues,
|
|
2923
|
+
clearErrors,
|
|
2924
|
+
unregister,
|
|
2925
|
+
setError,
|
|
2926
|
+
setFocus,
|
|
2927
|
+
getFieldState
|
|
2928
|
+
};
|
|
2929
|
+
return {
|
|
2930
|
+
...methods,
|
|
2931
|
+
formControl: methods
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2934
|
+
function useForm(props = {}) {
|
|
2935
|
+
const _formControl = React__default.default.useRef(void 0);
|
|
2936
|
+
const _values = React__default.default.useRef(void 0);
|
|
2937
|
+
const _formControlProp = React__default.default.useRef(props.formControl);
|
|
2938
|
+
const [formState, updateFormState] = React__default.default.useState(() => ({
|
|
2939
|
+
...cloneObject(DEFAULT_FORM_STATE),
|
|
2940
|
+
isLoading: isFunction(props.defaultValues),
|
|
2941
|
+
errors: props.errors || {},
|
|
2942
|
+
disabled: props.disabled || false,
|
|
2943
|
+
defaultValues: isFunction(props.defaultValues) ? void 0 : props.defaultValues
|
|
2944
|
+
}));
|
|
2945
|
+
if (!_formControl.current || props.formControl && _formControlProp.current !== props.formControl) {
|
|
2946
|
+
_formControlProp.current = props.formControl;
|
|
2947
|
+
if (props.formControl) {
|
|
2948
|
+
_formControl.current = {
|
|
2949
|
+
...props.formControl,
|
|
2950
|
+
formState
|
|
2951
|
+
};
|
|
2952
|
+
if (props.defaultValues && !isFunction(props.defaultValues)) {
|
|
2953
|
+
props.formControl.reset(props.defaultValues, props.resetOptions);
|
|
3251
2954
|
}
|
|
3252
|
-
} else if (strong !== void 0) {
|
|
3253
|
-
out.push({ type: "strong", children: parseInline(strong, depth + 1) });
|
|
3254
2955
|
} else {
|
|
3255
|
-
|
|
2956
|
+
const { formControl, ...rest } = createFormControl(props);
|
|
2957
|
+
_formControl.current = {
|
|
2958
|
+
...rest,
|
|
2959
|
+
formState
|
|
2960
|
+
};
|
|
3256
2961
|
}
|
|
3257
2962
|
}
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
const
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
blocks.push({ type: "paragraph", children: parseInline(para.join(" ")) });
|
|
3281
|
-
para = [];
|
|
3282
|
-
};
|
|
3283
|
-
for (let i = 0; i < lines.length; i++) {
|
|
3284
|
-
const line = lines[i];
|
|
3285
|
-
const fence = FENCE_RE.exec(line);
|
|
3286
|
-
if (fence) {
|
|
3287
|
-
flushPara();
|
|
3288
|
-
const body = [];
|
|
3289
|
-
i++;
|
|
3290
|
-
while (i < lines.length && !FENCE_RE.test(lines[i])) body.push(lines[i++]);
|
|
3291
|
-
blocks.push({ type: "codeBlock", label: fenceLabel(fence[1]), value: body.join("\n") });
|
|
3292
|
-
continue;
|
|
2963
|
+
const control = _formControl.current.control;
|
|
2964
|
+
control._options = props;
|
|
2965
|
+
useIsomorphicLayoutEffect(() => {
|
|
2966
|
+
const sub = control._subscribe({
|
|
2967
|
+
formState: control._proxyFormState,
|
|
2968
|
+
callback: () => updateFormState({
|
|
2969
|
+
...control._formState,
|
|
2970
|
+
defaultValues: control._defaultValues
|
|
2971
|
+
}),
|
|
2972
|
+
reRenderRoot: true
|
|
2973
|
+
});
|
|
2974
|
+
updateFormState((data) => ({
|
|
2975
|
+
...data,
|
|
2976
|
+
isReady: true
|
|
2977
|
+
}));
|
|
2978
|
+
control._formState.isReady = true;
|
|
2979
|
+
return sub;
|
|
2980
|
+
}, [control]);
|
|
2981
|
+
React__default.default.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
|
|
2982
|
+
React__default.default.useEffect(() => {
|
|
2983
|
+
if (props.mode) {
|
|
2984
|
+
control._options.mode = props.mode;
|
|
3293
2985
|
}
|
|
3294
|
-
if (
|
|
3295
|
-
|
|
3296
|
-
continue;
|
|
2986
|
+
if (props.reValidateMode) {
|
|
2987
|
+
control._options.reValidateMode = props.reValidateMode;
|
|
3297
2988
|
}
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
2989
|
+
}, [control, props.mode, props.reValidateMode]);
|
|
2990
|
+
React__default.default.useEffect(() => {
|
|
2991
|
+
if (props.errors) {
|
|
2992
|
+
control._setErrors(props.errors);
|
|
2993
|
+
control._focusError();
|
|
3302
2994
|
}
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
2995
|
+
}, [control, props.errors]);
|
|
2996
|
+
React__default.default.useEffect(() => {
|
|
2997
|
+
props.shouldUnregister && control._subjects.state.next({
|
|
2998
|
+
values: control._getWatch()
|
|
2999
|
+
});
|
|
3000
|
+
}, [control, props.shouldUnregister]);
|
|
3001
|
+
React__default.default.useEffect(() => {
|
|
3002
|
+
if (control._proxyFormState.isDirty) {
|
|
3003
|
+
const isDirty = control._getDirty();
|
|
3004
|
+
if (isDirty !== formState.isDirty) {
|
|
3005
|
+
control._subjects.state.next({
|
|
3006
|
+
isDirty
|
|
3007
|
+
});
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
}, [control, formState.isDirty]);
|
|
3011
|
+
React__default.default.useEffect(() => {
|
|
3012
|
+
var _a;
|
|
3013
|
+
if (props.values && !deepEqual(props.values, _values.current)) {
|
|
3014
|
+
control._reset(props.values, {
|
|
3015
|
+
keepFieldsRef: true,
|
|
3016
|
+
...control._options.resetOptions
|
|
3310
3017
|
});
|
|
3311
|
-
|
|
3018
|
+
if (!((_a = control._options.resetOptions) === null || _a === void 0 ? void 0 : _a.keepIsValid)) {
|
|
3019
|
+
control._setValid();
|
|
3020
|
+
}
|
|
3021
|
+
_values.current = props.values;
|
|
3022
|
+
updateFormState((state) => ({ ...state }));
|
|
3023
|
+
} else {
|
|
3024
|
+
control._resetDefaultValues();
|
|
3312
3025
|
}
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
if (
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3026
|
+
}, [control, props.values]);
|
|
3027
|
+
React__default.default.useEffect(() => {
|
|
3028
|
+
if (!control._state.mount) {
|
|
3029
|
+
control._setValid();
|
|
3030
|
+
control._state.mount = true;
|
|
3031
|
+
}
|
|
3032
|
+
if (control._state.watch) {
|
|
3033
|
+
control._state.watch = false;
|
|
3034
|
+
control._subjects.state.next({ ...control._formState });
|
|
3035
|
+
}
|
|
3036
|
+
control._removeUnmounted();
|
|
3037
|
+
});
|
|
3038
|
+
_formControl.current.formState = React__default.default.useMemo(() => getProxyFormState(formState, control), [control, formState]);
|
|
3039
|
+
return _formControl.current;
|
|
3040
|
+
}
|
|
3041
|
+
const jsonToText = (v) => v == null || v === "" ? "" : JSON.stringify(v, null, 2);
|
|
3042
|
+
const JsonField = ({ field, f }) => {
|
|
3043
|
+
const { t } = reactI18next.useTranslation();
|
|
3044
|
+
const [raw, setRaw] = React.useState(() => jsonToText(f.value));
|
|
3045
|
+
const [error, setError] = React.useState(null);
|
|
3046
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3047
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3048
|
+
ui.Textarea,
|
|
3049
|
+
{
|
|
3050
|
+
rows: 6,
|
|
3051
|
+
className: "font-mono",
|
|
3052
|
+
placeholder: field.placeholder ? t(field.placeholder) : "[]",
|
|
3053
|
+
value: raw,
|
|
3054
|
+
disabled: field.readonly === true,
|
|
3055
|
+
onBlur: f.onBlur,
|
|
3056
|
+
onChange: (e) => {
|
|
3057
|
+
const text2 = e.target.value;
|
|
3058
|
+
setRaw(text2);
|
|
3059
|
+
if (text2.trim() === "") {
|
|
3060
|
+
setError(null);
|
|
3061
|
+
f.onChange(null);
|
|
3062
|
+
return;
|
|
3063
|
+
}
|
|
3064
|
+
try {
|
|
3065
|
+
f.onChange(JSON.parse(text2));
|
|
3066
|
+
setError(null);
|
|
3067
|
+
} catch {
|
|
3068
|
+
setError(t("integration.form.invalidJson"));
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3325
3071
|
}
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3072
|
+
),
|
|
3073
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: error })
|
|
3074
|
+
] });
|
|
3075
|
+
};
|
|
3076
|
+
const IntegrationField = ({
|
|
3077
|
+
field,
|
|
3078
|
+
control,
|
|
3079
|
+
secretConfigured = false
|
|
3080
|
+
}) => {
|
|
3081
|
+
const { t } = reactI18next.useTranslation();
|
|
3082
|
+
const enforceRequired = field.required && field.control !== "switch" && !(field.control === "secret" && secretConfigured);
|
|
3083
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
3084
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { size: "small", weight: "plus", children: [
|
|
3085
|
+
t(field.label),
|
|
3086
|
+
field.required ? " *" : ""
|
|
3087
|
+
] }),
|
|
3088
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3089
|
+
Controller,
|
|
3090
|
+
{
|
|
3091
|
+
name: field.name,
|
|
3092
|
+
control,
|
|
3093
|
+
rules: {
|
|
3094
|
+
required: enforceRequired ? t("integration.form.required", { field: t(field.label) }) : false
|
|
3095
|
+
},
|
|
3096
|
+
render: ({ field: f, fieldState }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3097
|
+
renderControl(field, f, secretConfigured, t),
|
|
3098
|
+
fieldState.error?.message && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: fieldState.error.message })
|
|
3099
|
+
] })
|
|
3100
|
+
}
|
|
3101
|
+
),
|
|
3102
|
+
field.hint && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: t(field.hint) })
|
|
3103
|
+
] });
|
|
3104
|
+
};
|
|
3105
|
+
function renderControl(field, f, secretConfigured, t) {
|
|
3106
|
+
const placeholder = field.placeholder ? t(field.placeholder) : void 0;
|
|
3107
|
+
const disabled = field.readonly === true;
|
|
3108
|
+
switch (field.control) {
|
|
3109
|
+
case "switch":
|
|
3110
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: !!f.value, onCheckedChange: f.onChange, disabled });
|
|
3111
|
+
case "select": {
|
|
3112
|
+
const declared = field.options ?? [];
|
|
3113
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3114
|
+
ui.Select,
|
|
3115
|
+
{
|
|
3116
|
+
value: f.value == null ? "" : String(f.value),
|
|
3117
|
+
onValueChange: (next) => f.onChange(declared.find((o) => String(o) === next) ?? next),
|
|
3118
|
+
disabled,
|
|
3119
|
+
children: [
|
|
3120
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder }) }),
|
|
3121
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: declared.map((o) => {
|
|
3122
|
+
const labelKey = field.optionLabels?.[String(o)];
|
|
3123
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: String(o), children: labelKey ? t(labelKey) : String(o) }, String(o));
|
|
3124
|
+
}) })
|
|
3125
|
+
]
|
|
3126
|
+
}
|
|
3127
|
+
);
|
|
3329
3128
|
}
|
|
3330
|
-
|
|
3331
|
-
}
|
|
3332
|
-
flushPara();
|
|
3333
|
-
const weight = blocks.reduce((n, b) => n + (b.type === "list" ? b.items.length : 1), 0);
|
|
3334
|
-
return weight > MD_MAX_BLOCKS ? [{ type: "truncated" }] : blocks;
|
|
3335
|
-
}
|
|
3336
|
-
const Inline = ({ nodes }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: nodes.map((node, i) => {
|
|
3337
|
-
switch (node.type) {
|
|
3338
|
-
case "text":
|
|
3339
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.value }, i);
|
|
3340
|
-
case "code":
|
|
3341
|
-
return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "txt-compact-small bg-ui-bg-component rounded px-1 py-0.5", children: node.value }, i);
|
|
3342
|
-
case "strong":
|
|
3343
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-small-plus", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
3344
|
-
case "em":
|
|
3345
|
-
return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
3346
|
-
case "link":
|
|
3129
|
+
case "number":
|
|
3347
3130
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3348
|
-
|
|
3131
|
+
ui.Input,
|
|
3349
3132
|
{
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
}
|
|
3356
|
-
i
|
|
3133
|
+
type: "number",
|
|
3134
|
+
placeholder,
|
|
3135
|
+
value: f.value ?? "",
|
|
3136
|
+
disabled,
|
|
3137
|
+
onChange: (e) => f.onChange(e.target.value === "" ? null : e.target.valueAsNumber)
|
|
3138
|
+
}
|
|
3357
3139
|
);
|
|
3358
|
-
|
|
3359
|
-
}) });
|
|
3360
|
-
const Block = ({ block, truncatedLabel }) => {
|
|
3361
|
-
switch (block.type) {
|
|
3362
|
-
case "heading":
|
|
3363
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: block.level === 2 ? "h2" : "h3", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
3364
|
-
case "paragraph":
|
|
3365
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
3366
|
-
case "list": {
|
|
3367
|
-
const List = block.ordered ? "ol" : "ul";
|
|
3140
|
+
case "url":
|
|
3368
3141
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3369
|
-
|
|
3142
|
+
ui.Input,
|
|
3370
3143
|
{
|
|
3371
|
-
|
|
3372
|
-
|
|
3144
|
+
type: "url",
|
|
3145
|
+
placeholder,
|
|
3146
|
+
value: f.value ?? "",
|
|
3147
|
+
disabled,
|
|
3148
|
+
onChange: f.onChange
|
|
3149
|
+
}
|
|
3150
|
+
);
|
|
3151
|
+
case "secret":
|
|
3152
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3153
|
+
ui.Input,
|
|
3154
|
+
{
|
|
3155
|
+
type: "password",
|
|
3156
|
+
placeholder: secretConfigured ? t("integration.form.keepSecret") : placeholder ?? "",
|
|
3157
|
+
value: f.value ?? "",
|
|
3158
|
+
disabled,
|
|
3159
|
+
onChange: f.onChange,
|
|
3160
|
+
className: !f.value ? "[&~div]:hidden" : void 0
|
|
3161
|
+
}
|
|
3162
|
+
);
|
|
3163
|
+
case "json":
|
|
3164
|
+
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { field, f });
|
|
3165
|
+
default:
|
|
3166
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3167
|
+
ui.Input,
|
|
3168
|
+
{
|
|
3169
|
+
placeholder,
|
|
3170
|
+
value: f.value ?? "",
|
|
3171
|
+
disabled,
|
|
3172
|
+
onChange: f.onChange
|
|
3373
3173
|
}
|
|
3374
3174
|
);
|
|
3375
|
-
}
|
|
3376
|
-
case "codeBlock":
|
|
3377
|
-
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: [
|
|
3378
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-2", children: [
|
|
3379
|
-
block.label ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-subtle", children: block.label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
3380
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: block.value })
|
|
3381
|
-
] }),
|
|
3382
|
-
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "txt-compact-small text-ui-fg-subtle overflow-x-auto whitespace-pre pb-2", children: block.value })
|
|
3383
|
-
] });
|
|
3384
|
-
case "hr":
|
|
3385
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-ui-border-base border-t" });
|
|
3386
|
-
case "truncated":
|
|
3387
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: truncatedLabel });
|
|
3388
3175
|
}
|
|
3389
|
-
}
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3176
|
+
}
|
|
3177
|
+
function isFieldVisible(field, values) {
|
|
3178
|
+
const rule = field.visibleWhen;
|
|
3179
|
+
if (!rule) return true;
|
|
3180
|
+
return values[rule.field] === rule.equals;
|
|
3181
|
+
}
|
|
3182
|
+
function sectionDefaults(section, values) {
|
|
3183
|
+
const out = {};
|
|
3184
|
+
for (const f of section.fields) {
|
|
3185
|
+
if (f.secret || f.control === "secret") out[f.name] = "";
|
|
3186
|
+
else if (f.control === "switch") out[f.name] = values?.[f.name] ?? f.default ?? false;
|
|
3187
|
+
else if (f.control === "json") out[f.name] = values?.[f.name] ?? f.default;
|
|
3188
|
+
else out[f.name] = values?.[f.name] ?? f.default ?? "";
|
|
3189
|
+
}
|
|
3190
|
+
return out;
|
|
3191
|
+
}
|
|
3192
|
+
const EditSectionDrawer = ({
|
|
3193
|
+
open,
|
|
3194
|
+
onOpenChange,
|
|
3195
|
+
providerId,
|
|
3196
|
+
section,
|
|
3197
|
+
values,
|
|
3198
|
+
configuredSecrets = []
|
|
3404
3199
|
}) => {
|
|
3405
|
-
const { t
|
|
3406
|
-
const
|
|
3407
|
-
const
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3200
|
+
const { t } = reactI18next.useTranslation();
|
|
3201
|
+
const qc = reactQuery.useQueryClient();
|
|
3202
|
+
const configured = new Set(configuredSecrets);
|
|
3203
|
+
const [defaults] = React.useState(() => sectionDefaults(section, values));
|
|
3204
|
+
const form = useForm({ values: defaults });
|
|
3205
|
+
const current = form.watch();
|
|
3206
|
+
const visibilityValues = { ...values, ...current };
|
|
3207
|
+
const save = reactQuery.useMutation({
|
|
3208
|
+
mutationFn: (sectionValues) => sdk.client.fetch(`/admin/integrations/${providerId}`, {
|
|
3209
|
+
method: "POST",
|
|
3210
|
+
// Only this section's values + its id — the server validates just this section and
|
|
3211
|
+
// merges over the rest of the stored config (keeping blank/untouched secrets).
|
|
3212
|
+
body: { section_id: section.id, values: sectionValues }
|
|
3213
|
+
}),
|
|
3214
|
+
onSuccess: () => {
|
|
3215
|
+
ui.toast.success(t("integration.toast.saved"));
|
|
3216
|
+
qc.invalidateQueries({ queryKey: ["integration", providerId] });
|
|
3217
|
+
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
3218
|
+
onOpenChange(false);
|
|
3219
|
+
},
|
|
3220
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.saveFailed"))
|
|
3221
|
+
});
|
|
3222
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
3223
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: t(section.title) }) }),
|
|
3224
|
+
/* @__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(
|
|
3225
|
+
IntegrationField,
|
|
3226
|
+
{
|
|
3227
|
+
field: f,
|
|
3228
|
+
control: form.control,
|
|
3229
|
+
secretConfigured: configured.has(f.name)
|
|
3230
|
+
},
|
|
3231
|
+
f.name
|
|
3232
|
+
)) }),
|
|
3414
3233
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
3415
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: t("integration.actions.cancel") }) }),
|
|
3416
|
-
|
|
3417
|
-
|
|
3234
|
+
/* @__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") }) }),
|
|
3235
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3236
|
+
ui.Button,
|
|
3237
|
+
{
|
|
3238
|
+
size: "small",
|
|
3239
|
+
isLoading: save.isPending,
|
|
3240
|
+
onClick: form.handleSubmit((v) => save.mutate(v)),
|
|
3241
|
+
children: t("integration.actions.save")
|
|
3242
|
+
}
|
|
3243
|
+
)
|
|
3244
|
+
] })
|
|
3245
|
+
] }) });
|
|
3246
|
+
};
|
|
3247
|
+
const SURFACE = {
|
|
3248
|
+
green: "bg-ui-bg-subtle border-ui-border-base",
|
|
3249
|
+
grey: "bg-ui-bg-subtle border-ui-border-base",
|
|
3250
|
+
orange: "bg-ui-tag-orange-bg border-ui-tag-orange-border",
|
|
3251
|
+
red: "bg-ui-tag-red-bg border-ui-tag-red-border"
|
|
3252
|
+
};
|
|
3253
|
+
const RECHECK = {
|
|
3254
|
+
green: void 0,
|
|
3255
|
+
grey: void 0,
|
|
3256
|
+
orange: "hover:bg-ui-tag-orange-bg-hover active:bg-ui-tag-orange-bg-hover",
|
|
3257
|
+
red: "hover:bg-ui-tag-red-bg-hover active:bg-ui-tag-red-bg-hover"
|
|
3258
|
+
};
|
|
3259
|
+
const LicenseSummary = ({ entry }) => {
|
|
3260
|
+
const { t } = reactI18next.useTranslation();
|
|
3261
|
+
const view = useDescribeLicense()(entry);
|
|
3262
|
+
const details = useLicenseDetails()(view);
|
|
3263
|
+
const recheck = useRecheckLicense();
|
|
3264
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.clx("flex items-center gap-x-4 rounded-lg border px-4 py-3", SURFACE[view.tone]), children: [
|
|
3265
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-y-0.5", children: [
|
|
3266
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-1", children: [
|
|
3267
|
+
/* @__PURE__ */ jsxRuntime.jsx(LicenseSquare, { tone: view.tone }),
|
|
3268
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", weight: "plus", className: "text-ui-fg-base", children: t(view.title.key, view.title.values) })
|
|
3269
|
+
] }),
|
|
3270
|
+
details && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: details })
|
|
3271
|
+
] }),
|
|
3272
|
+
entry.state !== "pending" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-x-2", children: [
|
|
3273
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3274
|
+
ui.Button,
|
|
3275
|
+
{
|
|
3276
|
+
size: "small",
|
|
3277
|
+
variant: "transparent",
|
|
3278
|
+
className: RECHECK[view.tone],
|
|
3279
|
+
isLoading: recheck.isPending,
|
|
3280
|
+
disabled: recheck.isPending,
|
|
3281
|
+
onClick: () => recheck.mutate(),
|
|
3282
|
+
children: t("integration.license.recheck")
|
|
3283
|
+
}
|
|
3284
|
+
),
|
|
3285
|
+
view.action && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: view.action.href, target: "_blank", rel: "noopener noreferrer", children: [
|
|
3286
|
+
t(view.action.key),
|
|
3418
3287
|
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, {})
|
|
3419
3288
|
] }) })
|
|
3420
3289
|
] })
|
|
3421
|
-
] })
|
|
3290
|
+
] });
|
|
3422
3291
|
};
|
|
3423
|
-
const
|
|
3292
|
+
const Row = ({ label, children }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle grid grid-cols-2 px-6 py-4 [overflow-wrap:anywhere]", children: [
|
|
3293
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: label }),
|
|
3294
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-2", children })
|
|
3295
|
+
] });
|
|
3296
|
+
const EditPage = () => {
|
|
3424
3297
|
const { t } = reactI18next.useTranslation();
|
|
3298
|
+
const { provider_id } = reactRouterDom.useParams();
|
|
3299
|
+
const qc = reactQuery.useQueryClient();
|
|
3425
3300
|
const navigate = reactRouterDom.useNavigate();
|
|
3426
|
-
const
|
|
3427
|
-
const [
|
|
3428
|
-
const [installItem, setInstallItem] = React.useState(null);
|
|
3301
|
+
const prompt = ui.usePrompt();
|
|
3302
|
+
const [editingSection, setEditingSection] = React.useState(null);
|
|
3429
3303
|
const { data, isLoading } = reactQuery.useQuery({
|
|
3430
|
-
queryKey: ["integration
|
|
3431
|
-
queryFn: () => sdk.client.fetch(
|
|
3304
|
+
queryKey: ["integration", provider_id],
|
|
3305
|
+
queryFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`)
|
|
3432
3306
|
});
|
|
3433
|
-
const
|
|
3434
|
-
const
|
|
3435
|
-
const
|
|
3436
|
-
const
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3307
|
+
const { data: licenseData } = useLicenseStatus();
|
|
3308
|
+
const descriptor = data?.descriptor;
|
|
3309
|
+
const record = data?.integration;
|
|
3310
|
+
const isConfigured = !!record;
|
|
3311
|
+
const isComplete = data?.is_complete ?? false;
|
|
3312
|
+
const invalidate = () => {
|
|
3313
|
+
qc.invalidateQueries({ queryKey: ["integration", provider_id] });
|
|
3314
|
+
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
3315
|
+
};
|
|
3316
|
+
const test = reactQuery.useMutation({
|
|
3317
|
+
mutationFn: () => sdk.client.fetch(
|
|
3318
|
+
`/admin/integrations/${provider_id}/test-connection`,
|
|
3319
|
+
{ method: "POST" }
|
|
3320
|
+
),
|
|
3321
|
+
onSuccess: (r) => {
|
|
3322
|
+
r.status === "passed" ? ui.toast.success(t("integration.toast.connectionOk")) : ui.toast.warning(r.message ?? r.status);
|
|
3323
|
+
invalidate();
|
|
3324
|
+
},
|
|
3325
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.testFailed"))
|
|
3440
3326
|
});
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3327
|
+
const toggleEnabled = reactQuery.useMutation({
|
|
3328
|
+
mutationFn: (next) => sdk.client.fetch(`/admin/integrations/${provider_id}/enable`, {
|
|
3329
|
+
method: "POST",
|
|
3330
|
+
body: { is_enabled: next }
|
|
3331
|
+
}),
|
|
3332
|
+
onSuccess: (_d, next) => {
|
|
3333
|
+
ui.toast.success(next ? t("integration.toast.enabled") : t("integration.toast.disabled"));
|
|
3334
|
+
invalidate();
|
|
3335
|
+
},
|
|
3336
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.updateFailed"))
|
|
3337
|
+
});
|
|
3338
|
+
const remove = reactQuery.useMutation({
|
|
3339
|
+
mutationFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`, { method: "DELETE" }),
|
|
3340
|
+
onSuccess: () => {
|
|
3341
|
+
ui.toast.success(t("integration.toast.deleted"));
|
|
3342
|
+
invalidate();
|
|
3343
|
+
navigate("/settings/integrations");
|
|
3344
|
+
},
|
|
3345
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.deleteFailed"))
|
|
3346
|
+
});
|
|
3347
|
+
const onDelete = async () => {
|
|
3348
|
+
const confirmed = await prompt({
|
|
3349
|
+
title: t("integration.delete.title"),
|
|
3350
|
+
description: t("integration.delete.description", {
|
|
3351
|
+
name: descriptor ? t(descriptor.displayName) : ""
|
|
3352
|
+
}),
|
|
3353
|
+
confirmText: t("integration.actions.delete"),
|
|
3354
|
+
cancelText: t("integration.actions.cancel")
|
|
3355
|
+
});
|
|
3356
|
+
if (confirmed) remove.mutate();
|
|
3357
|
+
};
|
|
3358
|
+
if (isLoading) {
|
|
3359
|
+
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" }) });
|
|
3360
|
+
}
|
|
3361
|
+
if (!descriptor) {
|
|
3362
|
+
throw new Medusa.FetchError(
|
|
3363
|
+
`Integration with provider_id: ${provider_id} was not found`,
|
|
3364
|
+
"There is no page at this address",
|
|
3365
|
+
404
|
|
3366
|
+
);
|
|
3367
|
+
}
|
|
3368
|
+
const version = data?.version ?? null;
|
|
3369
|
+
const author = data?.author ?? null;
|
|
3370
|
+
const authorUrl = data?.author_url ?? null;
|
|
3371
|
+
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") };
|
|
3372
|
+
const connection = descriptor.hasTestConnection ? record?.last_test_status === "passed" ? { color: "green", label: t("integration.status.connected") } : record?.last_test_status === "failed" ? { color: "red", label: t("integration.status.connectionFailed") } : null : null;
|
|
3373
|
+
const license = licenseData?.packages.find((entry) => entry.identifiers.includes(descriptor.identifier));
|
|
3374
|
+
const editing = descriptor.sections.find((s) => s.id === editingSection) ?? null;
|
|
3375
|
+
const configuredSecrets = new Set(record?.configured_secrets ?? []);
|
|
3376
|
+
const sectionData = {
|
|
3377
|
+
providerId: provider_id,
|
|
3378
|
+
identifier: descriptor.identifier,
|
|
3379
|
+
values: record?.values ?? {},
|
|
3380
|
+
isComplete
|
|
3381
|
+
};
|
|
3382
|
+
const renderSection = (section) => /* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: section.id, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
3383
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
3384
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t(section.title) }),
|
|
3385
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
3386
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
3387
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: () => setEditingSection(section.id), children: [
|
|
3388
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "text-ui-fg-subtle" }),
|
|
3389
|
+
t("integration.actions.edit")
|
|
3390
|
+
] }) })
|
|
3463
3391
|
] })
|
|
3464
3392
|
] }),
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3393
|
+
section.fields.filter((f) => isFieldVisible(f, record?.values ?? {})).map((f) => /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t(f.label), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3394
|
+
IntegrationFieldValue,
|
|
3395
|
+
{
|
|
3396
|
+
field: f,
|
|
3397
|
+
value: record?.values?.[f.name] ?? f.default,
|
|
3398
|
+
secretConfigured: configuredSecrets.has(f.name)
|
|
3399
|
+
}
|
|
3400
|
+
) }, f.name))
|
|
3401
|
+
] }, section.id) });
|
|
3402
|
+
const mainSections = descriptor.sections.filter((s) => s.column !== "side");
|
|
3403
|
+
const sideSections = descriptor.sections.filter((s) => s.column === "side");
|
|
3404
|
+
const main = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3405
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: "main", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
3406
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 px-6 py-4", children: [
|
|
3407
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3468
3408
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
3469
|
-
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src:
|
|
3470
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
3471
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3472
|
-
|
|
3409
|
+
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: descriptor.icon, alt: t(descriptor.displayName) }),
|
|
3410
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
3411
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
3412
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: t(descriptor.displayName) }),
|
|
3413
|
+
provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: toModuleProviderId(provider_id, descriptor.category) })
|
|
3414
|
+
] }),
|
|
3415
|
+
version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
|
|
3416
|
+
t("integration.meta.version", { version }),
|
|
3417
|
+
author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3418
|
+
" • ",
|
|
3419
|
+
t("integration.meta.authorPrefix"),
|
|
3420
|
+
" ",
|
|
3421
|
+
authorUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3422
|
+
"a",
|
|
3423
|
+
{
|
|
3424
|
+
href: authorUrl,
|
|
3425
|
+
target: "_blank",
|
|
3426
|
+
rel: "noreferrer",
|
|
3427
|
+
className: "text-ui-fg-interactive",
|
|
3428
|
+
children: author
|
|
3429
|
+
}
|
|
3430
|
+
) : author
|
|
3431
|
+
] })
|
|
3432
|
+
] })
|
|
3473
3433
|
] })
|
|
3474
3434
|
] }),
|
|
3475
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3435
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
3436
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
|
|
3437
|
+
connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label }),
|
|
3438
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
3439
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
3440
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
|
|
3441
|
+
descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
|
|
3442
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3443
|
+
ui.DropdownMenu.Item,
|
|
3444
|
+
{
|
|
3445
|
+
disabled: toggleEnabled.isPending,
|
|
3446
|
+
onClick: () => toggleEnabled.mutate(!record.is_enabled),
|
|
3447
|
+
children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
|
|
3448
|
+
}
|
|
3449
|
+
),
|
|
3450
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
|
|
3451
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
|
|
3452
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
|
|
3453
|
+
t("integration.actions.delete")
|
|
3454
|
+
] })
|
|
3455
|
+
] })
|
|
3456
|
+
] })
|
|
3457
|
+
] })
|
|
3476
3458
|
] }),
|
|
3477
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle
|
|
3459
|
+
descriptor.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(descriptor.description) }),
|
|
3460
|
+
license && /* @__PURE__ */ jsxRuntime.jsx(LicenseSummary, { entry: license })
|
|
3478
3461
|
] }),
|
|
3479
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3480
|
-
item.installed && item.provider_id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3481
|
-
ui.Button,
|
|
3482
|
-
{
|
|
3483
|
-
size: "small",
|
|
3484
|
-
variant: "primary",
|
|
3485
|
-
onClick: () => navigate(`/settings/integrations/${item.provider_id}`),
|
|
3486
|
-
children: t("integration.browse.settings")
|
|
3487
|
-
}
|
|
3488
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", variant: "secondary", onClick: () => setInstallItem(item), children: [
|
|
3489
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
3490
|
-
t("integration.browse.add")
|
|
3491
|
-
] }),
|
|
3492
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(`integration.categories.${item.category}`) })
|
|
3493
|
-
] })
|
|
3494
|
-
] }, item.integrationId)) }),
|
|
3495
|
-
/* @__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: [
|
|
3496
|
-
t("integration.browse.learnMore"),
|
|
3497
|
-
" →"
|
|
3462
|
+
/* @__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}`) }) })
|
|
3498
3463
|
] }) }),
|
|
3499
|
-
|
|
3464
|
+
mainSections.map(renderSection),
|
|
3465
|
+
editing && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3466
|
+
EditSectionDrawer,
|
|
3467
|
+
{
|
|
3468
|
+
open: true,
|
|
3469
|
+
onOpenChange: (open) => !open && setEditingSection(null),
|
|
3470
|
+
providerId: provider_id,
|
|
3471
|
+
section: editing,
|
|
3472
|
+
values: record?.values ?? {},
|
|
3473
|
+
configuredSecrets: record?.configured_secrets ?? []
|
|
3474
|
+
},
|
|
3475
|
+
editing.id
|
|
3476
|
+
)
|
|
3500
3477
|
] });
|
|
3478
|
+
const side = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: sideSections.map(renderSection) });
|
|
3479
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3480
|
+
components.LayoutComposer,
|
|
3481
|
+
{
|
|
3482
|
+
widgetsZonePrefix: `gorgo.integration.${descriptor.identifier}`,
|
|
3483
|
+
preferredLayoutId: descriptor.preferredLayoutId,
|
|
3484
|
+
sections: { main, side },
|
|
3485
|
+
data: sectionData,
|
|
3486
|
+
hasOutlet: false
|
|
3487
|
+
}
|
|
3488
|
+
);
|
|
3501
3489
|
};
|
|
3502
|
-
const
|
|
3490
|
+
const IntegrationBreadcrumb = ({ params }) => {
|
|
3503
3491
|
const { t } = reactI18next.useTranslation();
|
|
3504
|
-
|
|
3492
|
+
const providerId = params.provider_id;
|
|
3493
|
+
const { data } = reactQuery.useQuery({
|
|
3494
|
+
queryKey: ["integration", providerId],
|
|
3495
|
+
queryFn: () => sdk.client.fetch(`/admin/integrations/${providerId}`),
|
|
3496
|
+
enabled: !!providerId
|
|
3497
|
+
});
|
|
3498
|
+
const descriptor = data?.descriptor;
|
|
3499
|
+
if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
3500
|
+
const name = t(descriptor.displayName);
|
|
3501
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3502
|
+
name + " ",
|
|
3503
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: providerId })
|
|
3504
|
+
] });
|
|
3505
3505
|
};
|
|
3506
3506
|
const handle = {
|
|
3507
|
-
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3507
|
+
breadcrumb: ({ params }) => /* @__PURE__ */ jsxRuntime.jsx(IntegrationBreadcrumb, { params })
|
|
3508
3508
|
};
|
|
3509
3509
|
const integration$x = { "list": { "title": "التكاملات", "subtitle": "إدارة إعدادات التكاملات", "browseIntegrations": "تصفح التكاملات", "learnMore": "تعرّف على كيفية إنشاء التكاملات", "searchPlaceholder": "البحث في التكاملات", "empty": "لا توجد تكاملات مسجلة. عرّف المزودين ضمن options.providers الخاصة بإضافة التكاملات في medusa-config.", "emptyFiltered": "لا توجد تكاملات مطابقة لعوامل التصفية.", "tested": "تم الاختبار", "testFailed": "فشل الاختبار", "tabs": { "all": "الكل" } }, "browse": { "title": "التصفح", "subtitle": "ابحث عن التكاملات المتاحة", "publish": "إضافة تكامل", "learnMore": "تعرّف على كيفية إنشاء ونشر تكاملك الخاص", "searchPlaceholder": "البحث في التكاملات", "by": "بواسطة {{author}}", "installed": "مثبَّت", "notInstalled": "غير مثبَّت", "add": "إضافة", "settings": "الإعدادات", "install_docs": "قراءة التوثيق", "install_empty": "تعليمات الإعداد لهذا التكامل غير متوفرة بعد.", "install_truncated": "هذه التعليمات أطول من أن تُعرض هنا — افتح التوثيق." }, "columns": { "integration": "التكامل", "description": "الوصف", "category": "الفئة", "status": "الحالة" }, "status": { "notConfigured": "غير مُهيّأ", "incomplete": "غير مكتمل", "enabled": "ممكّن", "disabled": "معطل", "configured": "مُهيّأ", "connected": "متصل", "connectionFailed": "فشل الاتصال" }, "meta": { "version": "v{{version}}", "authorPrefix": "بواسطة" }, "labels": { "category": "الفئة" }, "categories": { "payment": "الدفع", "notification": "الإشعارات", "fulfillment": "التنفيذ", "search": "البحث", "content": "المحتوى", "analytics": "التحليلات", "authentication": "المصادقة", "erp": "ERP", "crm": "CRM", "pim": "PIM", "marketplace": "الأسواق", "feed": "الخلاصات", "file_providers": "مزودو الملفات", "other": "أخرى" }, "actions": { "test": "اختبار الاتصال", "enable": "تمكين", "disable": "تعطيل", "delete": "حذف", "edit": "تحرير", "save": "حفظ", "cancel": "إلغاء" }, "delete": { "title": "حذف التهيئة", "description": "هل تريد إزالة التهيئة المحفوظة لـ {{name}}؟ لا يمكن التراجع عن هذا الإجراء." }, "form": { "required": "{{field}} مطلوب", "invalidJson": "JSON غير صالح", "keepSecret": "•••••• – اتركه فارغًا للإبقاء عليه", "items": "{{count}} عناصر" }, "toast": { "saved": "تم الحفظ", "saveFailed": "فشل الحفظ", "connectionOk": "الاتصال ناجح", "testFailed": "فشل الاختبار", "enabled": "تم التمكين", "disabled": "تم التعطيل", "deleted": "تم حذف التهيئة", "deleteFailed": "فشل الحذف", "updateFailed": "فشل التحديث" }, "unknown": 'تكامل غير معروف "{{id}}".', "back": "العودة إلى التكاملات", "license": { "title": { "pending": "Checking license", "active": "License active", "renews": "License active, renews {{date}}", "until": "License until {{date}}", "ending": "License ends {{when}}", "ended": "Subscription ended {{date}}", "missing": "License key not set", "malformed": "License key has the wrong format", "invalid_token": "License key not accepted", "no_subscription": "No subscription for this package", "domain_not_allowed": "Domain not in the project", "version_after_cutoff": "Version released after the subscription ended", "version_unverifiable": "Version could not be verified", "unknown": "License refused", "unreachable": "License not checked" }, "hint": { "pending": "The check runs at startup and takes a few seconds", "ending": "The installed version keeps working. Renew to get new versions", "ended": "The license covers versions up to {{version}}", "missing": "Add GORGO_LICENSE to the environment of the Medusa server and worker, then restart them", "malformed": "GORGO_LICENSE does not look like a project key", "invalid_token": "The key is unknown or revoked", "no_subscription": "Project “{{project}}” has no subscription for this package", "domain_not_allowed": "{{host}} is not among the domains of project “{{project}}”", "version_after_cutoff": "Install {{version}} or renew the subscription", "version_unverifiable": "The check will run again automatically", "unknown": "The license service refused the key", "unreachable": "The license service is unreachable. The check will run again in 15 minutes" }, "action": { "account": "Open account", "renew": "Renew", "getKey": "Get a key", "reissueKey": "Reissue key", "subscribe": "Subscribe", "addDomain": "Add domain" }, "checked": "checked {{when}}", "local": "local", "recheck": "Check now", "recheckFailed": "Could not check the license" } };
|
|
3510
3510
|
const ar = {
|
|
@@ -3687,13 +3687,13 @@ const routeModule = {
|
|
|
3687
3687
|
handle: { label: config.label, translationNs: config.translationNs, ...handle$2 }
|
|
3688
3688
|
},
|
|
3689
3689
|
{
|
|
3690
|
-
Component:
|
|
3691
|
-
path: "/settings/integrations
|
|
3690
|
+
Component: BrowsePage,
|
|
3691
|
+
path: "/settings/integrations/browse",
|
|
3692
3692
|
handle: handle$1
|
|
3693
3693
|
},
|
|
3694
3694
|
{
|
|
3695
|
-
Component:
|
|
3696
|
-
path: "/settings/integrations
|
|
3695
|
+
Component: EditPage,
|
|
3696
|
+
path: "/settings/integrations/:provider_id",
|
|
3697
3697
|
handle
|
|
3698
3698
|
}
|
|
3699
3699
|
]
|