@gorgo/medusa-integration 0.3.0-beta.1 → 0.3.0-beta.2
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 +911 -911
- package/.medusa/server/src/admin/index.mjs +913 -913
- package/.medusa/server/src/modules/integration/loaders/__tests__/check-licenses.unit.spec.js +5 -2
- package/.medusa/server/src/modules/integration/loaders/check-licenses.js +5 -3
- package/package.json +1 -1
|
@@ -291,113 +291,399 @@ const IntegrationsBreadcrumb = () => {
|
|
|
291
291
|
const handle$2 = {
|
|
292
292
|
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(IntegrationsBreadcrumb, {})
|
|
293
293
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}) => {
|
|
300
|
-
const { t } = reactI18next.useTranslation();
|
|
301
|
-
if (field.secret || field.control === "secret") {
|
|
302
|
-
return secretConfigured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "••••••••" }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
303
|
-
}
|
|
304
|
-
if (field.control === "switch") {
|
|
305
|
-
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
306
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: value ? "green" : "grey", children: value ? t("integration.status.enabled") : t("integration.status.disabled") });
|
|
294
|
+
function isSafeHref(raw) {
|
|
295
|
+
try {
|
|
296
|
+
return new URL(raw).protocol === "https:";
|
|
297
|
+
} catch {
|
|
298
|
+
return false;
|
|
307
299
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
300
|
+
}
|
|
301
|
+
const INLINE_RE = /`([^`\n]+)`|\[([^\]\n]*)\]\(([^)\s]+)\)|\*\*([^*\n]+)\*\*|\*([^*\n]+)\*|_([^_\n]+)_/;
|
|
302
|
+
const MAX_DEPTH = 3;
|
|
303
|
+
function parseInline(src, depth = 0) {
|
|
304
|
+
if (!src) return [];
|
|
305
|
+
if (depth >= MAX_DEPTH) return [{ type: "text", value: src }];
|
|
306
|
+
const out = [];
|
|
307
|
+
const re = new RegExp(INLINE_RE, "g");
|
|
308
|
+
let last = 0;
|
|
309
|
+
let m;
|
|
310
|
+
while ((m = re.exec(src)) !== null) {
|
|
311
|
+
const [full, code, linkText, linkHref, strong, star, underscore] = m;
|
|
312
|
+
if (m.index > last) out.push({ type: "text", value: src.slice(last, m.index) });
|
|
313
|
+
last = m.index + full.length;
|
|
314
|
+
if (code !== void 0) {
|
|
315
|
+
out.push({ type: "code", value: code });
|
|
316
|
+
} else if (linkHref !== void 0) {
|
|
317
|
+
const isImage = m.index > 0 && src[m.index - 1] === "!";
|
|
318
|
+
if (!isImage && isSafeHref(linkHref)) {
|
|
319
|
+
out.push({ type: "link", href: linkHref, children: parseInline(linkText, depth + 1) });
|
|
320
|
+
} else {
|
|
321
|
+
out.push({ type: "text", value: full });
|
|
322
|
+
}
|
|
323
|
+
} else if (strong !== void 0) {
|
|
324
|
+
out.push({ type: "strong", children: parseInline(strong, depth + 1) });
|
|
325
|
+
} else {
|
|
326
|
+
out.push({ type: "em", children: parseInline(star ?? underscore, depth + 1) });
|
|
311
327
|
}
|
|
312
|
-
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
313
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.status.configured") });
|
|
314
|
-
}
|
|
315
|
-
if (value == null || value === "") return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
316
|
-
if (field.control === "select") {
|
|
317
|
-
const labelKey = field.optionLabels?.[String(value)];
|
|
318
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: labelKey ? t(labelKey) : String(value) });
|
|
319
|
-
}
|
|
320
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: String(value) });
|
|
321
|
-
};
|
|
322
|
-
var isCheckBoxInput = (element) => element.type === "checkbox";
|
|
323
|
-
var isFileInput = (element) => element.type === "file";
|
|
324
|
-
var isDateObject = (value) => value instanceof Date;
|
|
325
|
-
var isNullOrUndefined = (value) => value == null;
|
|
326
|
-
const isObjectType = (value) => typeof value === "object";
|
|
327
|
-
var isObject = (value) => !isNullOrUndefined(value) && !Array.isArray(value) && isObjectType(value) && !isDateObject(value);
|
|
328
|
-
var getEventValue = (event) => isObject(event) && event.target ? isCheckBoxInput(event.target) ? event.target.checked : isFileInput(event.target) ? event.target.files : event.target.value : event;
|
|
329
|
-
var isNameInFieldArray = (names, name) => name.split(".").some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join(".")));
|
|
330
|
-
var isPlainObject = (tempObject) => {
|
|
331
|
-
const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
|
|
332
|
-
return isObject(prototypeCopy) && prototypeCopy.hasOwnProperty("isPrototypeOf");
|
|
333
|
-
};
|
|
334
|
-
var isWeb = typeof window !== "undefined" && typeof window.HTMLElement !== "undefined" && typeof document !== "undefined";
|
|
335
|
-
function cloneObject(data) {
|
|
336
|
-
if (data instanceof Date) {
|
|
337
|
-
return new Date(data);
|
|
338
|
-
}
|
|
339
|
-
const isFileListInstance = typeof FileList !== "undefined" && data instanceof FileList;
|
|
340
|
-
if (isWeb && (data instanceof Blob || isFileListInstance)) {
|
|
341
|
-
return data;
|
|
342
|
-
}
|
|
343
|
-
const isArray = Array.isArray(data);
|
|
344
|
-
if (!isArray && !(isObject(data) && isPlainObject(data))) {
|
|
345
|
-
return data;
|
|
346
328
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
329
|
+
if (last < src.length) out.push({ type: "text", value: src.slice(last) });
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
332
|
+
const MD_MAX_CHARS = 8e3;
|
|
333
|
+
const MD_MAX_BLOCKS = 200;
|
|
334
|
+
const FENCE_RE = /^```(.*)$/;
|
|
335
|
+
const HEADING_RE = /^(#{1,3})\s+(.*)$/;
|
|
336
|
+
const HR_RE = /^-{3,}\s*$/;
|
|
337
|
+
const UL_RE = /^[-*]\s+(.*)$/;
|
|
338
|
+
const OL_RE = /^\d+\.\s+(.*)$/;
|
|
339
|
+
function fenceLabel(info) {
|
|
340
|
+
const first = info.trim().split(/\s+/)[0] ?? "";
|
|
341
|
+
return first.replace(/[^a-zA-Z0-9._/-]/g, "").slice(0, 40) || null;
|
|
342
|
+
}
|
|
343
|
+
function parseMarkdown(src) {
|
|
344
|
+
if (typeof src !== "string" || !src) return [];
|
|
345
|
+
if (src.length > MD_MAX_CHARS) return [{ type: "truncated" }];
|
|
346
|
+
const lines = src.replace(/\r\n?/g, "\n").split("\n");
|
|
347
|
+
const blocks = [];
|
|
348
|
+
let para = [];
|
|
349
|
+
const flushPara = () => {
|
|
350
|
+
if (!para.length) return;
|
|
351
|
+
blocks.push({ type: "paragraph", children: parseInline(para.join(" ")) });
|
|
352
|
+
para = [];
|
|
353
|
+
};
|
|
354
|
+
for (let i = 0; i < lines.length; i++) {
|
|
355
|
+
const line = lines[i];
|
|
356
|
+
const fence = FENCE_RE.exec(line);
|
|
357
|
+
if (fence) {
|
|
358
|
+
flushPara();
|
|
359
|
+
const body = [];
|
|
360
|
+
i++;
|
|
361
|
+
while (i < lines.length && !FENCE_RE.test(lines[i])) body.push(lines[i++]);
|
|
362
|
+
blocks.push({ type: "codeBlock", label: fenceLabel(fence[1]), value: body.join("\n") });
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (!line.trim()) {
|
|
366
|
+
flushPara();
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (HR_RE.test(line)) {
|
|
370
|
+
flushPara();
|
|
371
|
+
blocks.push({ type: "hr" });
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const heading = HEADING_RE.exec(line);
|
|
375
|
+
if (heading) {
|
|
376
|
+
flushPara();
|
|
377
|
+
blocks.push({
|
|
378
|
+
type: "heading",
|
|
379
|
+
level: heading[1].length + 1,
|
|
380
|
+
children: parseInline(heading[2])
|
|
381
|
+
});
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
const ul = UL_RE.exec(line);
|
|
385
|
+
const ol = ul ? null : OL_RE.exec(line);
|
|
386
|
+
if (ul || ol) {
|
|
387
|
+
flushPara();
|
|
388
|
+
const ordered = !!ol;
|
|
389
|
+
const itemRe = ordered ? OL_RE : UL_RE;
|
|
390
|
+
const items = [];
|
|
391
|
+
while (i < lines.length) {
|
|
392
|
+
const item = itemRe.exec(lines[i]);
|
|
393
|
+
if (!item) break;
|
|
394
|
+
items.push(parseInline(item[1]));
|
|
395
|
+
i++;
|
|
396
|
+
}
|
|
397
|
+
i--;
|
|
398
|
+
blocks.push({ type: "list", ordered, items });
|
|
399
|
+
continue;
|
|
351
400
|
}
|
|
401
|
+
para.push(line);
|
|
352
402
|
}
|
|
353
|
-
|
|
403
|
+
flushPara();
|
|
404
|
+
const weight = blocks.reduce((n, b) => n + (b.type === "list" ? b.items.length : 1), 0);
|
|
405
|
+
return weight > MD_MAX_BLOCKS ? [{ type: "truncated" }] : blocks;
|
|
354
406
|
}
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
validate: "validate"
|
|
378
|
-
};
|
|
379
|
-
const ROOT_ERROR_TYPE = "root";
|
|
380
|
-
const PROTOTYPE_KEYWORDS = ["__proto__", "constructor", "prototype"];
|
|
381
|
-
const IS_KEY_RE = /^\w*$/;
|
|
382
|
-
var isKey = (value) => IS_KEY_RE.test(value);
|
|
383
|
-
var isUndefined = (val) => val === void 0;
|
|
384
|
-
const FIELD_PATH_RE = /[.[\]'"]/;
|
|
385
|
-
var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
|
|
386
|
-
var get = (object, path, defaultValue) => {
|
|
387
|
-
if (!path || !isObject(object)) {
|
|
388
|
-
return defaultValue;
|
|
407
|
+
const Inline = ({ nodes }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: nodes.map((node, i) => {
|
|
408
|
+
switch (node.type) {
|
|
409
|
+
case "text":
|
|
410
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.value }, i);
|
|
411
|
+
case "code":
|
|
412
|
+
return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "txt-compact-small bg-ui-bg-component rounded px-1 py-0.5", children: node.value }, i);
|
|
413
|
+
case "strong":
|
|
414
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-small-plus", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
415
|
+
case "em":
|
|
416
|
+
return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
417
|
+
case "link":
|
|
418
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
419
|
+
"a",
|
|
420
|
+
{
|
|
421
|
+
href: node.href,
|
|
422
|
+
target: "_blank",
|
|
423
|
+
rel: "noopener noreferrer",
|
|
424
|
+
className: "text-ui-fg-interactive hover:underline",
|
|
425
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children })
|
|
426
|
+
},
|
|
427
|
+
i
|
|
428
|
+
);
|
|
389
429
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
430
|
+
}) });
|
|
431
|
+
const Block = ({ block, truncatedLabel }) => {
|
|
432
|
+
switch (block.type) {
|
|
433
|
+
case "heading":
|
|
434
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: block.level === 2 ? "h2" : "h3", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
435
|
+
case "paragraph":
|
|
436
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
437
|
+
case "list": {
|
|
438
|
+
const List = block.ordered ? "ol" : "ul";
|
|
439
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
440
|
+
List,
|
|
441
|
+
{
|
|
442
|
+
className: `text-ui-fg-subtle txt-small flex flex-col gap-y-1 pl-5 ${block.ordered ? "list-decimal" : "list-disc"}`,
|
|
443
|
+
children: block.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: item }) }, i))
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
case "codeBlock":
|
|
448
|
+
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: [
|
|
449
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-2", children: [
|
|
450
|
+
block.label ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-subtle", children: block.label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
451
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: block.value })
|
|
452
|
+
] }),
|
|
453
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "txt-compact-small text-ui-fg-subtle overflow-x-auto whitespace-pre pb-2", children: block.value })
|
|
454
|
+
] });
|
|
455
|
+
case "hr":
|
|
456
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-ui-border-base border-t" });
|
|
457
|
+
case "truncated":
|
|
458
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: truncatedLabel });
|
|
393
459
|
}
|
|
394
|
-
const result = paths.reduce((result2, key) => {
|
|
395
|
-
return isNullOrUndefined(result2) ? void 0 : result2[key];
|
|
396
|
-
}, object);
|
|
397
|
-
return isUndefined(result) || result === object ? isUndefined(object[path]) ? defaultValue : object[path] : result;
|
|
398
460
|
};
|
|
399
|
-
|
|
400
|
-
|
|
461
|
+
const Markdown = ({
|
|
462
|
+
source,
|
|
463
|
+
truncatedLabel
|
|
464
|
+
}) => {
|
|
465
|
+
const blocks = React.useMemo(() => parseMarkdown(source), [source]);
|
|
466
|
+
if (!blocks.length) return null;
|
|
467
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-y-4", children: blocks.map((block, i) => /* @__PURE__ */ jsxRuntime.jsx(Block, { block, truncatedLabel }, i)) });
|
|
468
|
+
};
|
|
469
|
+
const PUBLISH_URL = "https://docs.gorgojs.com/medusa-modules/integration";
|
|
470
|
+
const API_URL = "https://gorgojs.com";
|
|
471
|
+
const iconSrc = (icon) => icon.startsWith("data:") ? icon : API_URL + icon;
|
|
472
|
+
const InstallModal = ({
|
|
473
|
+
item,
|
|
474
|
+
onOpenChange
|
|
475
|
+
}) => {
|
|
476
|
+
const { t, i18n } = reactI18next.useTranslation();
|
|
477
|
+
const lang = i18n.language.split("-")[0] === "ru" ? "ru" : "en";
|
|
478
|
+
const source = item?.docsSnippet?.[lang] || item?.docsSnippet?.en || item?.docsSnippet?.ru || "";
|
|
479
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: !!item, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
480
|
+
/* @__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: [
|
|
481
|
+
item && /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label, size: "small" }),
|
|
482
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: item?.label })
|
|
483
|
+
] }) }) }),
|
|
484
|
+
/* @__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") }) }),
|
|
485
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
486
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: t("integration.actions.cancel") }) }),
|
|
487
|
+
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: [
|
|
488
|
+
t("integration.browse.install_docs"),
|
|
489
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, {})
|
|
490
|
+
] }) })
|
|
491
|
+
] })
|
|
492
|
+
] }) });
|
|
493
|
+
};
|
|
494
|
+
const BrowsePage = () => {
|
|
495
|
+
const { t } = reactI18next.useTranslation();
|
|
496
|
+
const navigate = reactRouterDom.useNavigate();
|
|
497
|
+
const [activeTab, setActiveTab] = React.useState("all");
|
|
498
|
+
const [search, setSearch] = React.useState("");
|
|
499
|
+
const [installItem, setInstallItem] = React.useState(null);
|
|
500
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
501
|
+
queryKey: ["integration-catalog"],
|
|
502
|
+
queryFn: () => sdk.client.fetch("/admin/integrations/catalog")
|
|
503
|
+
});
|
|
504
|
+
const items = data?.integrations ?? [];
|
|
505
|
+
const categories = React.useMemo(() => [...new Set(items.map((i) => i.category))], [items]);
|
|
506
|
+
const q = search.trim().toLowerCase();
|
|
507
|
+
const filtered = items.filter((i) => {
|
|
508
|
+
if (activeTab !== "all" && i.category !== activeTab) return false;
|
|
509
|
+
if (q && ![i.label, i.shortDescription, i.npm].some((s) => s.toLowerCase().includes(q))) return false;
|
|
510
|
+
return true;
|
|
511
|
+
});
|
|
512
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3", children: [
|
|
513
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
514
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
|
|
515
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
516
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("integration.browse.title") }),
|
|
517
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.subtitle") })
|
|
518
|
+
] }),
|
|
519
|
+
false
|
|
520
|
+
] }),
|
|
521
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 px-6 py-4", children: [
|
|
522
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs, { value: activeTab, onValueChange: setActiveTab, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
|
|
523
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "all", children: t("integration.list.tabs.all") }),
|
|
524
|
+
categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: c, children: t(`integration.categories.${c}`) }, c))
|
|
525
|
+
] }) }),
|
|
526
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-64", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
527
|
+
ui.Input,
|
|
528
|
+
{
|
|
529
|
+
placeholder: t("integration.browse.searchPlaceholder"),
|
|
530
|
+
value: search,
|
|
531
|
+
onChange: (e) => setSearch(e.target.value)
|
|
532
|
+
}
|
|
533
|
+
) })
|
|
534
|
+
] })
|
|
535
|
+
] }),
|
|
536
|
+
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: [
|
|
537
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-y-3 p-4", children: [
|
|
538
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-x-2", children: [
|
|
539
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
540
|
+
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: iconSrc(item.icon), alt: item.label }),
|
|
541
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
542
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: item.label }),
|
|
543
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: t("integration.browse.by", { author: item.authorLocalized }) })
|
|
544
|
+
] })
|
|
545
|
+
] }),
|
|
546
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: item.installed ? "green" : "grey", children: item.installed ? t("integration.browse.installed") : t("integration.browse.notInstalled") })
|
|
547
|
+
] }),
|
|
548
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle line-clamp-2", children: item.shortDescription })
|
|
549
|
+
] }),
|
|
550
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
|
|
551
|
+
item.installed && item.provider_id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
552
|
+
ui.Button,
|
|
553
|
+
{
|
|
554
|
+
size: "small",
|
|
555
|
+
variant: "primary",
|
|
556
|
+
onClick: () => navigate(`/settings/integrations/${item.provider_id}`),
|
|
557
|
+
children: t("integration.browse.settings")
|
|
558
|
+
}
|
|
559
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", variant: "secondary", onClick: () => setInstallItem(item), children: [
|
|
560
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
561
|
+
t("integration.browse.add")
|
|
562
|
+
] }),
|
|
563
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(`integration.categories.${item.category}`) })
|
|
564
|
+
] })
|
|
565
|
+
] }, item.integrationId)) }),
|
|
566
|
+
/* @__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: [
|
|
567
|
+
t("integration.browse.learnMore"),
|
|
568
|
+
" →"
|
|
569
|
+
] }) }),
|
|
570
|
+
/* @__PURE__ */ jsxRuntime.jsx(InstallModal, { item: installItem, onOpenChange: (open) => !open && setInstallItem(null) })
|
|
571
|
+
] });
|
|
572
|
+
};
|
|
573
|
+
const BrowseBreadcrumb = () => {
|
|
574
|
+
const { t } = reactI18next.useTranslation();
|
|
575
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("integration.browse.title") });
|
|
576
|
+
};
|
|
577
|
+
const handle$1 = {
|
|
578
|
+
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(BrowseBreadcrumb, {})
|
|
579
|
+
};
|
|
580
|
+
const Dash = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "text-ui-fg-subtle", children: "-" });
|
|
581
|
+
const IntegrationFieldValue = ({
|
|
582
|
+
field,
|
|
583
|
+
value,
|
|
584
|
+
secretConfigured = false
|
|
585
|
+
}) => {
|
|
586
|
+
const { t } = reactI18next.useTranslation();
|
|
587
|
+
if (field.secret || field.control === "secret") {
|
|
588
|
+
return secretConfigured ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: "••••••••" }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
589
|
+
}
|
|
590
|
+
if (field.control === "switch") {
|
|
591
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
592
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: value ? "green" : "grey", children: value ? t("integration.status.enabled") : t("integration.status.disabled") });
|
|
593
|
+
}
|
|
594
|
+
if (field.control === "json") {
|
|
595
|
+
if (Array.isArray(value)) {
|
|
596
|
+
return value.length ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.form.items", { count: value.length }) }) : /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
597
|
+
}
|
|
598
|
+
if (value == null) return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
599
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: t("integration.status.configured") });
|
|
600
|
+
}
|
|
601
|
+
if (value == null || value === "") return /* @__PURE__ */ jsxRuntime.jsx(Dash, {});
|
|
602
|
+
if (field.control === "select") {
|
|
603
|
+
const labelKey = field.optionLabels?.[String(value)];
|
|
604
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: labelKey ? t(labelKey) : String(value) });
|
|
605
|
+
}
|
|
606
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", children: String(value) });
|
|
607
|
+
};
|
|
608
|
+
var isCheckBoxInput = (element) => element.type === "checkbox";
|
|
609
|
+
var isFileInput = (element) => element.type === "file";
|
|
610
|
+
var isDateObject = (value) => value instanceof Date;
|
|
611
|
+
var isNullOrUndefined = (value) => value == null;
|
|
612
|
+
const isObjectType = (value) => typeof value === "object";
|
|
613
|
+
var isObject = (value) => !isNullOrUndefined(value) && !Array.isArray(value) && isObjectType(value) && !isDateObject(value);
|
|
614
|
+
var getEventValue = (event) => isObject(event) && event.target ? isCheckBoxInput(event.target) ? event.target.checked : isFileInput(event.target) ? event.target.files : event.target.value : event;
|
|
615
|
+
var isNameInFieldArray = (names, name) => name.split(".").some((part, index, arr) => !isNaN(Number(part)) && names.has(arr.slice(0, index).join(".")));
|
|
616
|
+
var isPlainObject = (tempObject) => {
|
|
617
|
+
const prototypeCopy = tempObject.constructor && tempObject.constructor.prototype;
|
|
618
|
+
return isObject(prototypeCopy) && prototypeCopy.hasOwnProperty("isPrototypeOf");
|
|
619
|
+
};
|
|
620
|
+
var isWeb = typeof window !== "undefined" && typeof window.HTMLElement !== "undefined" && typeof document !== "undefined";
|
|
621
|
+
function cloneObject(data) {
|
|
622
|
+
if (data instanceof Date) {
|
|
623
|
+
return new Date(data);
|
|
624
|
+
}
|
|
625
|
+
const isFileListInstance = typeof FileList !== "undefined" && data instanceof FileList;
|
|
626
|
+
if (isWeb && (data instanceof Blob || isFileListInstance)) {
|
|
627
|
+
return data;
|
|
628
|
+
}
|
|
629
|
+
const isArray = Array.isArray(data);
|
|
630
|
+
if (!isArray && !(isObject(data) && isPlainObject(data))) {
|
|
631
|
+
return data;
|
|
632
|
+
}
|
|
633
|
+
const copy = isArray ? [] : Object.create(Object.getPrototypeOf(data));
|
|
634
|
+
for (const key in data) {
|
|
635
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
636
|
+
copy[key] = cloneObject(data[key]);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return copy;
|
|
640
|
+
}
|
|
641
|
+
const EVENTS = {
|
|
642
|
+
BLUR: "blur",
|
|
643
|
+
FOCUS_OUT: "focusout",
|
|
644
|
+
CHANGE: "change",
|
|
645
|
+
SUBMIT: "submit",
|
|
646
|
+
TRIGGER: "trigger",
|
|
647
|
+
VALID: "valid"
|
|
648
|
+
};
|
|
649
|
+
const VALIDATION_MODE = {
|
|
650
|
+
onBlur: "onBlur",
|
|
651
|
+
onChange: "onChange",
|
|
652
|
+
onSubmit: "onSubmit",
|
|
653
|
+
onTouched: "onTouched",
|
|
654
|
+
all: "all"
|
|
655
|
+
};
|
|
656
|
+
const INPUT_VALIDATION_RULES = {
|
|
657
|
+
max: "max",
|
|
658
|
+
min: "min",
|
|
659
|
+
maxLength: "maxLength",
|
|
660
|
+
minLength: "minLength",
|
|
661
|
+
pattern: "pattern",
|
|
662
|
+
required: "required",
|
|
663
|
+
validate: "validate"
|
|
664
|
+
};
|
|
665
|
+
const ROOT_ERROR_TYPE = "root";
|
|
666
|
+
const PROTOTYPE_KEYWORDS = ["__proto__", "constructor", "prototype"];
|
|
667
|
+
const IS_KEY_RE = /^\w*$/;
|
|
668
|
+
var isKey = (value) => IS_KEY_RE.test(value);
|
|
669
|
+
var isUndefined = (val) => val === void 0;
|
|
670
|
+
const FIELD_PATH_RE = /[.[\]'"]/;
|
|
671
|
+
var stringToPath = (input) => input.split(FIELD_PATH_RE).filter(Boolean);
|
|
672
|
+
var get = (object, path, defaultValue) => {
|
|
673
|
+
if (!path || !isObject(object)) {
|
|
674
|
+
return defaultValue;
|
|
675
|
+
}
|
|
676
|
+
const paths = isKey(path) ? [path] : stringToPath(path);
|
|
677
|
+
if (paths.some((key) => PROTOTYPE_KEYWORDS.includes(key))) {
|
|
678
|
+
return defaultValue;
|
|
679
|
+
}
|
|
680
|
+
const result = paths.reduce((result2, key) => {
|
|
681
|
+
return isNullOrUndefined(result2) ? void 0 : result2[key];
|
|
682
|
+
}, object);
|
|
683
|
+
return isUndefined(result) || result === object ? isUndefined(object[path]) ? defaultValue : object[path] : result;
|
|
684
|
+
};
|
|
685
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
686
|
+
var isFunction = (value) => typeof value === "function";
|
|
401
687
|
var set = (object, path, value) => {
|
|
402
688
|
let index = -1;
|
|
403
689
|
const tempPath = isKey(path) ? [path] : stringToPath(path);
|
|
@@ -2399,857 +2685,571 @@ function createFormControl(props = {}) {
|
|
|
2399
2685
|
return _names;
|
|
2400
2686
|
},
|
|
2401
2687
|
set _names(value) {
|
|
2402
|
-
_names = value;
|
|
2403
|
-
},
|
|
2404
|
-
get _formState() {
|
|
2405
|
-
return _formState;
|
|
2406
|
-
},
|
|
2407
|
-
get _options() {
|
|
2408
|
-
return _options;
|
|
2409
|
-
},
|
|
2410
|
-
set _options(value) {
|
|
2411
|
-
_options = {
|
|
2412
|
-
..._options,
|
|
2413
|
-
...value
|
|
2414
|
-
};
|
|
2415
|
-
_validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
2416
|
-
_validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
2417
|
-
}
|
|
2418
|
-
},
|
|
2419
|
-
subscribe,
|
|
2420
|
-
trigger,
|
|
2421
|
-
register,
|
|
2422
|
-
handleSubmit,
|
|
2423
|
-
watch,
|
|
2424
|
-
setValue,
|
|
2425
|
-
setValues,
|
|
2426
|
-
getValues,
|
|
2427
|
-
reset,
|
|
2428
|
-
resetField,
|
|
2429
|
-
resetDefaultValues,
|
|
2430
|
-
clearErrors,
|
|
2431
|
-
unregister,
|
|
2432
|
-
setError,
|
|
2433
|
-
setFocus,
|
|
2434
|
-
getFieldState
|
|
2435
|
-
};
|
|
2436
|
-
return {
|
|
2437
|
-
...methods,
|
|
2438
|
-
formControl: methods
|
|
2439
|
-
};
|
|
2440
|
-
}
|
|
2441
|
-
function useForm(props = {}) {
|
|
2442
|
-
const _formControl = React__default.default.useRef(void 0);
|
|
2443
|
-
const _values = React__default.default.useRef(void 0);
|
|
2444
|
-
const _formControlProp = React__default.default.useRef(props.formControl);
|
|
2445
|
-
const [formState, updateFormState] = React__default.default.useState(() => ({
|
|
2446
|
-
...cloneObject(DEFAULT_FORM_STATE),
|
|
2447
|
-
isLoading: isFunction(props.defaultValues),
|
|
2448
|
-
errors: props.errors || {},
|
|
2449
|
-
disabled: props.disabled || false,
|
|
2450
|
-
defaultValues: isFunction(props.defaultValues) ? void 0 : props.defaultValues
|
|
2451
|
-
}));
|
|
2452
|
-
if (!_formControl.current || props.formControl && _formControlProp.current !== props.formControl) {
|
|
2453
|
-
_formControlProp.current = props.formControl;
|
|
2454
|
-
if (props.formControl) {
|
|
2455
|
-
_formControl.current = {
|
|
2456
|
-
...props.formControl,
|
|
2457
|
-
formState
|
|
2458
|
-
};
|
|
2459
|
-
if (props.defaultValues && !isFunction(props.defaultValues)) {
|
|
2460
|
-
props.formControl.reset(props.defaultValues, props.resetOptions);
|
|
2461
|
-
}
|
|
2462
|
-
} else {
|
|
2463
|
-
const { formControl, ...rest } = createFormControl(props);
|
|
2464
|
-
_formControl.current = {
|
|
2465
|
-
...rest,
|
|
2466
|
-
formState
|
|
2467
|
-
};
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
const control = _formControl.current.control;
|
|
2471
|
-
control._options = props;
|
|
2472
|
-
useIsomorphicLayoutEffect(() => {
|
|
2473
|
-
const sub = control._subscribe({
|
|
2474
|
-
formState: control._proxyFormState,
|
|
2475
|
-
callback: () => updateFormState({
|
|
2476
|
-
...control._formState,
|
|
2477
|
-
defaultValues: control._defaultValues
|
|
2478
|
-
}),
|
|
2479
|
-
reRenderRoot: true
|
|
2480
|
-
});
|
|
2481
|
-
updateFormState((data) => ({
|
|
2482
|
-
...data,
|
|
2483
|
-
isReady: true
|
|
2484
|
-
}));
|
|
2485
|
-
control._formState.isReady = true;
|
|
2486
|
-
return sub;
|
|
2487
|
-
}, [control]);
|
|
2488
|
-
React__default.default.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
|
|
2489
|
-
React__default.default.useEffect(() => {
|
|
2490
|
-
if (props.mode) {
|
|
2491
|
-
control._options.mode = props.mode;
|
|
2492
|
-
}
|
|
2493
|
-
if (props.reValidateMode) {
|
|
2494
|
-
control._options.reValidateMode = props.reValidateMode;
|
|
2495
|
-
}
|
|
2496
|
-
}, [control, props.mode, props.reValidateMode]);
|
|
2497
|
-
React__default.default.useEffect(() => {
|
|
2498
|
-
if (props.errors) {
|
|
2499
|
-
control._setErrors(props.errors);
|
|
2500
|
-
control._focusError();
|
|
2501
|
-
}
|
|
2502
|
-
}, [control, props.errors]);
|
|
2503
|
-
React__default.default.useEffect(() => {
|
|
2504
|
-
props.shouldUnregister && control._subjects.state.next({
|
|
2505
|
-
values: control._getWatch()
|
|
2506
|
-
});
|
|
2507
|
-
}, [control, props.shouldUnregister]);
|
|
2508
|
-
React__default.default.useEffect(() => {
|
|
2509
|
-
if (control._proxyFormState.isDirty) {
|
|
2510
|
-
const isDirty = control._getDirty();
|
|
2511
|
-
if (isDirty !== formState.isDirty) {
|
|
2512
|
-
control._subjects.state.next({
|
|
2513
|
-
isDirty
|
|
2514
|
-
});
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
}, [control, formState.isDirty]);
|
|
2518
|
-
React__default.default.useEffect(() => {
|
|
2519
|
-
var _a;
|
|
2520
|
-
if (props.values && !deepEqual(props.values, _values.current)) {
|
|
2521
|
-
control._reset(props.values, {
|
|
2522
|
-
keepFieldsRef: true,
|
|
2523
|
-
...control._options.resetOptions
|
|
2524
|
-
});
|
|
2525
|
-
if (!((_a = control._options.resetOptions) === null || _a === void 0 ? void 0 : _a.keepIsValid)) {
|
|
2526
|
-
control._setValid();
|
|
2527
|
-
}
|
|
2528
|
-
_values.current = props.values;
|
|
2529
|
-
updateFormState((state) => ({ ...state }));
|
|
2530
|
-
} else {
|
|
2531
|
-
control._resetDefaultValues();
|
|
2532
|
-
}
|
|
2533
|
-
}, [control, props.values]);
|
|
2534
|
-
React__default.default.useEffect(() => {
|
|
2535
|
-
if (!control._state.mount) {
|
|
2536
|
-
control._setValid();
|
|
2537
|
-
control._state.mount = true;
|
|
2538
|
-
}
|
|
2539
|
-
if (control._state.watch) {
|
|
2540
|
-
control._state.watch = false;
|
|
2541
|
-
control._subjects.state.next({ ...control._formState });
|
|
2542
|
-
}
|
|
2543
|
-
control._removeUnmounted();
|
|
2544
|
-
});
|
|
2545
|
-
_formControl.current.formState = React__default.default.useMemo(() => getProxyFormState(formState, control), [control, formState]);
|
|
2546
|
-
return _formControl.current;
|
|
2547
|
-
}
|
|
2548
|
-
const jsonToText = (v) => v == null || v === "" ? "" : JSON.stringify(v, null, 2);
|
|
2549
|
-
const JsonField = ({ field, f }) => {
|
|
2550
|
-
const { t } = reactI18next.useTranslation();
|
|
2551
|
-
const [raw, setRaw] = React.useState(() => jsonToText(f.value));
|
|
2552
|
-
const [error, setError] = React.useState(null);
|
|
2553
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2554
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2555
|
-
ui.Textarea,
|
|
2556
|
-
{
|
|
2557
|
-
rows: 6,
|
|
2558
|
-
className: "font-mono",
|
|
2559
|
-
placeholder: field.placeholder ? t(field.placeholder) : "[]",
|
|
2560
|
-
value: raw,
|
|
2561
|
-
disabled: field.readonly === true,
|
|
2562
|
-
onBlur: f.onBlur,
|
|
2563
|
-
onChange: (e) => {
|
|
2564
|
-
const text = e.target.value;
|
|
2565
|
-
setRaw(text);
|
|
2566
|
-
if (text.trim() === "") {
|
|
2567
|
-
setError(null);
|
|
2568
|
-
f.onChange(null);
|
|
2569
|
-
return;
|
|
2570
|
-
}
|
|
2571
|
-
try {
|
|
2572
|
-
f.onChange(JSON.parse(text));
|
|
2573
|
-
setError(null);
|
|
2574
|
-
} catch {
|
|
2575
|
-
setError(t("integration.form.invalidJson"));
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
),
|
|
2580
|
-
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: error })
|
|
2581
|
-
] });
|
|
2582
|
-
};
|
|
2583
|
-
const IntegrationField = ({
|
|
2584
|
-
field,
|
|
2585
|
-
control,
|
|
2586
|
-
secretConfigured = false
|
|
2587
|
-
}) => {
|
|
2588
|
-
const { t } = reactI18next.useTranslation();
|
|
2589
|
-
const enforceRequired = field.required && field.control !== "switch" && !(field.control === "secret" && secretConfigured);
|
|
2590
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2591
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { size: "small", weight: "plus", children: [
|
|
2592
|
-
t(field.label),
|
|
2593
|
-
field.required ? " *" : ""
|
|
2594
|
-
] }),
|
|
2595
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2596
|
-
Controller,
|
|
2597
|
-
{
|
|
2598
|
-
name: field.name,
|
|
2599
|
-
control,
|
|
2600
|
-
rules: {
|
|
2601
|
-
required: enforceRequired ? t("integration.form.required", { field: t(field.label) }) : false
|
|
2602
|
-
},
|
|
2603
|
-
render: ({ field: f, fieldState }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2604
|
-
renderControl(field, f, secretConfigured, t),
|
|
2605
|
-
fieldState.error?.message && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: fieldState.error.message })
|
|
2606
|
-
] })
|
|
2607
|
-
}
|
|
2608
|
-
),
|
|
2609
|
-
field.hint && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: t(field.hint) })
|
|
2610
|
-
] });
|
|
2611
|
-
};
|
|
2612
|
-
function renderControl(field, f, secretConfigured, t) {
|
|
2613
|
-
const placeholder = field.placeholder ? t(field.placeholder) : void 0;
|
|
2614
|
-
const disabled = field.readonly === true;
|
|
2615
|
-
switch (field.control) {
|
|
2616
|
-
case "switch":
|
|
2617
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: !!f.value, onCheckedChange: f.onChange, disabled });
|
|
2618
|
-
case "select": {
|
|
2619
|
-
const declared = field.options ?? [];
|
|
2620
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2621
|
-
ui.Select,
|
|
2622
|
-
{
|
|
2623
|
-
value: f.value == null ? "" : String(f.value),
|
|
2624
|
-
onValueChange: (next) => f.onChange(declared.find((o) => String(o) === next) ?? next),
|
|
2625
|
-
disabled,
|
|
2626
|
-
children: [
|
|
2627
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder }) }),
|
|
2628
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: declared.map((o) => {
|
|
2629
|
-
const labelKey = field.optionLabels?.[String(o)];
|
|
2630
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: String(o), children: labelKey ? t(labelKey) : String(o) }, String(o));
|
|
2631
|
-
}) })
|
|
2632
|
-
]
|
|
2633
|
-
}
|
|
2634
|
-
);
|
|
2635
|
-
}
|
|
2636
|
-
case "number":
|
|
2637
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2638
|
-
ui.Input,
|
|
2639
|
-
{
|
|
2640
|
-
type: "number",
|
|
2641
|
-
placeholder,
|
|
2642
|
-
value: f.value ?? "",
|
|
2643
|
-
disabled,
|
|
2644
|
-
onChange: (e) => f.onChange(e.target.value === "" ? null : e.target.valueAsNumber)
|
|
2645
|
-
}
|
|
2646
|
-
);
|
|
2647
|
-
case "url":
|
|
2648
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2649
|
-
ui.Input,
|
|
2650
|
-
{
|
|
2651
|
-
type: "url",
|
|
2652
|
-
placeholder,
|
|
2653
|
-
value: f.value ?? "",
|
|
2654
|
-
disabled,
|
|
2655
|
-
onChange: f.onChange
|
|
2656
|
-
}
|
|
2657
|
-
);
|
|
2658
|
-
case "secret":
|
|
2659
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2660
|
-
ui.Input,
|
|
2661
|
-
{
|
|
2662
|
-
type: "password",
|
|
2663
|
-
placeholder: secretConfigured ? t("integration.form.keepSecret") : placeholder ?? "",
|
|
2664
|
-
value: f.value ?? "",
|
|
2665
|
-
disabled,
|
|
2666
|
-
onChange: f.onChange,
|
|
2667
|
-
className: !f.value ? "[&~div]:hidden" : void 0
|
|
2668
|
-
}
|
|
2669
|
-
);
|
|
2670
|
-
case "json":
|
|
2671
|
-
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { field, f });
|
|
2672
|
-
default:
|
|
2673
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2674
|
-
ui.Input,
|
|
2675
|
-
{
|
|
2676
|
-
placeholder,
|
|
2677
|
-
value: f.value ?? "",
|
|
2678
|
-
disabled,
|
|
2679
|
-
onChange: f.onChange
|
|
2680
|
-
}
|
|
2681
|
-
);
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
function isFieldVisible(field, values) {
|
|
2685
|
-
const rule = field.visibleWhen;
|
|
2686
|
-
if (!rule) return true;
|
|
2687
|
-
return values[rule.field] === rule.equals;
|
|
2688
|
-
}
|
|
2689
|
-
function sectionDefaults(section, values) {
|
|
2690
|
-
const out = {};
|
|
2691
|
-
for (const f of section.fields) {
|
|
2692
|
-
if (f.secret || f.control === "secret") out[f.name] = "";
|
|
2693
|
-
else if (f.control === "switch") out[f.name] = values?.[f.name] ?? f.default ?? false;
|
|
2694
|
-
else if (f.control === "json") out[f.name] = values?.[f.name] ?? f.default;
|
|
2695
|
-
else out[f.name] = values?.[f.name] ?? f.default ?? "";
|
|
2696
|
-
}
|
|
2697
|
-
return out;
|
|
2698
|
-
}
|
|
2699
|
-
const EditSectionDrawer = ({
|
|
2700
|
-
open,
|
|
2701
|
-
onOpenChange,
|
|
2702
|
-
providerId,
|
|
2703
|
-
section,
|
|
2704
|
-
values,
|
|
2705
|
-
configuredSecrets = []
|
|
2706
|
-
}) => {
|
|
2707
|
-
const { t } = reactI18next.useTranslation();
|
|
2708
|
-
const qc = reactQuery.useQueryClient();
|
|
2709
|
-
const configured = new Set(configuredSecrets);
|
|
2710
|
-
const [defaults] = React.useState(() => sectionDefaults(section, values));
|
|
2711
|
-
const form = useForm({ values: defaults });
|
|
2712
|
-
const current = form.watch();
|
|
2713
|
-
const visibilityValues = { ...values, ...current };
|
|
2714
|
-
const save = reactQuery.useMutation({
|
|
2715
|
-
mutationFn: (sectionValues) => sdk.client.fetch(`/admin/integrations/${providerId}`, {
|
|
2716
|
-
method: "POST",
|
|
2717
|
-
// Only this section's values + its id — the server validates just this section and
|
|
2718
|
-
// merges over the rest of the stored config (keeping blank/untouched secrets).
|
|
2719
|
-
body: { section_id: section.id, values: sectionValues }
|
|
2720
|
-
}),
|
|
2721
|
-
onSuccess: () => {
|
|
2722
|
-
ui.toast.success(t("integration.toast.saved"));
|
|
2723
|
-
qc.invalidateQueries({ queryKey: ["integration", providerId] });
|
|
2724
|
-
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
2725
|
-
onOpenChange(false);
|
|
2726
|
-
},
|
|
2727
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.saveFailed"))
|
|
2728
|
-
});
|
|
2729
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
2730
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: t(section.title) }) }),
|
|
2731
|
-
/* @__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(
|
|
2732
|
-
IntegrationField,
|
|
2733
|
-
{
|
|
2734
|
-
field: f,
|
|
2735
|
-
control: form.control,
|
|
2736
|
-
secretConfigured: configured.has(f.name)
|
|
2737
|
-
},
|
|
2738
|
-
f.name
|
|
2739
|
-
)) }),
|
|
2740
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
2741
|
-
/* @__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") }) }),
|
|
2742
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2743
|
-
ui.Button,
|
|
2744
|
-
{
|
|
2745
|
-
size: "small",
|
|
2746
|
-
isLoading: save.isPending,
|
|
2747
|
-
onClick: form.handleSubmit((v) => save.mutate(v)),
|
|
2748
|
-
children: t("integration.actions.save")
|
|
2749
|
-
}
|
|
2750
|
-
)
|
|
2751
|
-
] })
|
|
2752
|
-
] }) });
|
|
2753
|
-
};
|
|
2754
|
-
const Row = ({ label, children }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle grid grid-cols-2 px-6 py-4", children: [
|
|
2755
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: label }),
|
|
2756
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-2", children })
|
|
2757
|
-
] });
|
|
2758
|
-
const EditPage = () => {
|
|
2759
|
-
const { t } = reactI18next.useTranslation();
|
|
2760
|
-
const { provider_id } = reactRouterDom.useParams();
|
|
2761
|
-
const qc = reactQuery.useQueryClient();
|
|
2762
|
-
const navigate = reactRouterDom.useNavigate();
|
|
2763
|
-
const prompt = ui.usePrompt();
|
|
2764
|
-
const [editingSection, setEditingSection] = React.useState(null);
|
|
2765
|
-
const { data, isLoading } = reactQuery.useQuery({
|
|
2766
|
-
queryKey: ["integration", provider_id],
|
|
2767
|
-
queryFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`)
|
|
2768
|
-
});
|
|
2769
|
-
const descriptor = data?.descriptor;
|
|
2770
|
-
const record = data?.integration;
|
|
2771
|
-
const isConfigured = !!record;
|
|
2772
|
-
const isComplete = data?.is_complete ?? false;
|
|
2773
|
-
const invalidate = () => {
|
|
2774
|
-
qc.invalidateQueries({ queryKey: ["integration", provider_id] });
|
|
2775
|
-
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
2776
|
-
};
|
|
2777
|
-
const test = reactQuery.useMutation({
|
|
2778
|
-
mutationFn: () => sdk.client.fetch(
|
|
2779
|
-
`/admin/integrations/${provider_id}/test-connection`,
|
|
2780
|
-
{ method: "POST" }
|
|
2781
|
-
),
|
|
2782
|
-
onSuccess: (r) => {
|
|
2783
|
-
r.status === "passed" ? ui.toast.success(t("integration.toast.connectionOk")) : ui.toast.warning(r.message ?? r.status);
|
|
2784
|
-
invalidate();
|
|
2785
|
-
},
|
|
2786
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.testFailed"))
|
|
2787
|
-
});
|
|
2788
|
-
const toggleEnabled = reactQuery.useMutation({
|
|
2789
|
-
mutationFn: (next) => sdk.client.fetch(`/admin/integrations/${provider_id}/enable`, {
|
|
2790
|
-
method: "POST",
|
|
2791
|
-
body: { is_enabled: next }
|
|
2792
|
-
}),
|
|
2793
|
-
onSuccess: (_d, next) => {
|
|
2794
|
-
ui.toast.success(next ? t("integration.toast.enabled") : t("integration.toast.disabled"));
|
|
2795
|
-
invalidate();
|
|
2796
|
-
},
|
|
2797
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.updateFailed"))
|
|
2798
|
-
});
|
|
2799
|
-
const remove = reactQuery.useMutation({
|
|
2800
|
-
mutationFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`, { method: "DELETE" }),
|
|
2801
|
-
onSuccess: () => {
|
|
2802
|
-
ui.toast.success(t("integration.toast.deleted"));
|
|
2803
|
-
invalidate();
|
|
2804
|
-
navigate("/settings/integrations");
|
|
2805
|
-
},
|
|
2806
|
-
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.deleteFailed"))
|
|
2807
|
-
});
|
|
2808
|
-
const onDelete = async () => {
|
|
2809
|
-
const confirmed = await prompt({
|
|
2810
|
-
title: t("integration.delete.title"),
|
|
2811
|
-
description: t("integration.delete.description", {
|
|
2812
|
-
name: descriptor ? t(descriptor.displayName) : ""
|
|
2813
|
-
}),
|
|
2814
|
-
confirmText: t("integration.actions.delete"),
|
|
2815
|
-
cancelText: t("integration.actions.cancel")
|
|
2816
|
-
});
|
|
2817
|
-
if (confirmed) remove.mutate();
|
|
2818
|
-
};
|
|
2819
|
-
if (isLoading) {
|
|
2820
|
-
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" }) });
|
|
2821
|
-
}
|
|
2822
|
-
if (!descriptor) {
|
|
2823
|
-
throw new Medusa.FetchError(
|
|
2824
|
-
`Integration with provider_id: ${provider_id} was not found`,
|
|
2825
|
-
"There is no page at this address",
|
|
2826
|
-
404
|
|
2827
|
-
);
|
|
2828
|
-
}
|
|
2829
|
-
const version = data?.version ?? null;
|
|
2830
|
-
const author = data?.author ?? null;
|
|
2831
|
-
const authorUrl = data?.author_url ?? null;
|
|
2832
|
-
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") };
|
|
2833
|
-
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;
|
|
2834
|
-
const editing = descriptor.sections.find((s) => s.id === editingSection) ?? null;
|
|
2835
|
-
const configuredSecrets = new Set(record?.configured_secrets ?? []);
|
|
2836
|
-
const sectionData = {
|
|
2837
|
-
providerId: provider_id,
|
|
2838
|
-
identifier: descriptor.identifier,
|
|
2839
|
-
values: record?.values ?? {},
|
|
2840
|
-
isComplete
|
|
2841
|
-
};
|
|
2842
|
-
const renderSection = (section) => /* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: section.id, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
2843
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
2844
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t(section.title) }),
|
|
2845
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
2846
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
2847
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: () => setEditingSection(section.id), children: [
|
|
2848
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "text-ui-fg-subtle" }),
|
|
2849
|
-
t("integration.actions.edit")
|
|
2850
|
-
] }) })
|
|
2851
|
-
] })
|
|
2852
|
-
] }),
|
|
2853
|
-
section.fields.filter((f) => isFieldVisible(f, record?.values ?? {})).map((f) => /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t(f.label), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2854
|
-
IntegrationFieldValue,
|
|
2855
|
-
{
|
|
2856
|
-
field: f,
|
|
2857
|
-
value: record?.values?.[f.name] ?? f.default,
|
|
2858
|
-
secretConfigured: configuredSecrets.has(f.name)
|
|
2859
|
-
}
|
|
2860
|
-
) }, f.name))
|
|
2861
|
-
] }, section.id) });
|
|
2862
|
-
const mainSections = descriptor.sections.filter((s) => s.column !== "side");
|
|
2863
|
-
const sideSections = descriptor.sections.filter((s) => s.column === "side");
|
|
2864
|
-
const main = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2865
|
-
/* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: "main", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
2866
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 px-6 py-4", children: [
|
|
2867
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2868
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
2869
|
-
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: descriptor.icon, alt: t(descriptor.displayName) }),
|
|
2870
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
2871
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2872
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: t(descriptor.displayName) }),
|
|
2873
|
-
provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: toModuleProviderId(provider_id, descriptor.category) })
|
|
2874
|
-
] }),
|
|
2875
|
-
version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
|
|
2876
|
-
t("integration.meta.version", { version }),
|
|
2877
|
-
author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2878
|
-
" • ",
|
|
2879
|
-
t("integration.meta.authorPrefix"),
|
|
2880
|
-
" ",
|
|
2881
|
-
authorUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2882
|
-
"a",
|
|
2883
|
-
{
|
|
2884
|
-
href: authorUrl,
|
|
2885
|
-
target: "_blank",
|
|
2886
|
-
rel: "noreferrer",
|
|
2887
|
-
className: "text-ui-fg-interactive",
|
|
2888
|
-
children: author
|
|
2889
|
-
}
|
|
2890
|
-
) : author
|
|
2891
|
-
] })
|
|
2892
|
-
] })
|
|
2893
|
-
] })
|
|
2894
|
-
] }),
|
|
2895
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
2896
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
|
|
2897
|
-
connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label }),
|
|
2898
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
2899
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
2900
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
|
|
2901
|
-
descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
|
|
2902
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2903
|
-
ui.DropdownMenu.Item,
|
|
2904
|
-
{
|
|
2905
|
-
disabled: toggleEnabled.isPending,
|
|
2906
|
-
onClick: () => toggleEnabled.mutate(!record.is_enabled),
|
|
2907
|
-
children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
|
|
2908
|
-
}
|
|
2909
|
-
),
|
|
2910
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
|
|
2911
|
-
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
|
|
2912
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
|
|
2913
|
-
t("integration.actions.delete")
|
|
2914
|
-
] })
|
|
2915
|
-
] })
|
|
2916
|
-
] })
|
|
2917
|
-
] })
|
|
2918
|
-
] }),
|
|
2919
|
-
descriptor.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(descriptor.description) })
|
|
2920
|
-
] }),
|
|
2921
|
-
/* @__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}`) }) })
|
|
2922
|
-
] }) }),
|
|
2923
|
-
mainSections.map(renderSection),
|
|
2924
|
-
editing && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2925
|
-
EditSectionDrawer,
|
|
2926
|
-
{
|
|
2927
|
-
open: true,
|
|
2928
|
-
onOpenChange: (open) => !open && setEditingSection(null),
|
|
2929
|
-
providerId: provider_id,
|
|
2930
|
-
section: editing,
|
|
2931
|
-
values: record?.values ?? {},
|
|
2932
|
-
configuredSecrets: record?.configured_secrets ?? []
|
|
2933
|
-
},
|
|
2934
|
-
editing.id
|
|
2935
|
-
)
|
|
2936
|
-
] });
|
|
2937
|
-
const side = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: sideSections.map(renderSection) });
|
|
2938
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2939
|
-
components.LayoutComposer,
|
|
2940
|
-
{
|
|
2941
|
-
widgetsZonePrefix: `gorgo.integration.${descriptor.identifier}`,
|
|
2942
|
-
preferredLayoutId: descriptor.preferredLayoutId,
|
|
2943
|
-
sections: { main, side },
|
|
2944
|
-
data: sectionData,
|
|
2945
|
-
hasOutlet: false
|
|
2946
|
-
}
|
|
2947
|
-
);
|
|
2948
|
-
};
|
|
2949
|
-
const IntegrationBreadcrumb = ({ params }) => {
|
|
2950
|
-
const { t } = reactI18next.useTranslation();
|
|
2951
|
-
const providerId = params.provider_id;
|
|
2952
|
-
const { data } = reactQuery.useQuery({
|
|
2953
|
-
queryKey: ["integration", providerId],
|
|
2954
|
-
queryFn: () => sdk.client.fetch(`/admin/integrations/${providerId}`),
|
|
2955
|
-
enabled: !!providerId
|
|
2956
|
-
});
|
|
2957
|
-
const descriptor = data?.descriptor;
|
|
2958
|
-
if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
2959
|
-
const name = t(descriptor.displayName);
|
|
2960
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2961
|
-
name + " ",
|
|
2962
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: providerId })
|
|
2963
|
-
] });
|
|
2964
|
-
};
|
|
2965
|
-
const handle$1 = {
|
|
2966
|
-
breadcrumb: ({ params }) => /* @__PURE__ */ jsxRuntime.jsx(IntegrationBreadcrumb, { params })
|
|
2967
|
-
};
|
|
2968
|
-
function isSafeHref(raw) {
|
|
2969
|
-
try {
|
|
2970
|
-
return new URL(raw).protocol === "https:";
|
|
2971
|
-
} catch {
|
|
2972
|
-
return false;
|
|
2973
|
-
}
|
|
2688
|
+
_names = value;
|
|
2689
|
+
},
|
|
2690
|
+
get _formState() {
|
|
2691
|
+
return _formState;
|
|
2692
|
+
},
|
|
2693
|
+
get _options() {
|
|
2694
|
+
return _options;
|
|
2695
|
+
},
|
|
2696
|
+
set _options(value) {
|
|
2697
|
+
_options = {
|
|
2698
|
+
..._options,
|
|
2699
|
+
...value
|
|
2700
|
+
};
|
|
2701
|
+
_validationModeBeforeSubmit = getValidationModes(_options.mode);
|
|
2702
|
+
_validationModeAfterSubmit = getValidationModes(_options.reValidateMode);
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
subscribe,
|
|
2706
|
+
trigger,
|
|
2707
|
+
register,
|
|
2708
|
+
handleSubmit,
|
|
2709
|
+
watch,
|
|
2710
|
+
setValue,
|
|
2711
|
+
setValues,
|
|
2712
|
+
getValues,
|
|
2713
|
+
reset,
|
|
2714
|
+
resetField,
|
|
2715
|
+
resetDefaultValues,
|
|
2716
|
+
clearErrors,
|
|
2717
|
+
unregister,
|
|
2718
|
+
setError,
|
|
2719
|
+
setFocus,
|
|
2720
|
+
getFieldState
|
|
2721
|
+
};
|
|
2722
|
+
return {
|
|
2723
|
+
...methods,
|
|
2724
|
+
formControl: methods
|
|
2725
|
+
};
|
|
2974
2726
|
}
|
|
2975
|
-
|
|
2976
|
-
const
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
if (
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
out.push({ type: "text", value: full });
|
|
2727
|
+
function useForm(props = {}) {
|
|
2728
|
+
const _formControl = React__default.default.useRef(void 0);
|
|
2729
|
+
const _values = React__default.default.useRef(void 0);
|
|
2730
|
+
const _formControlProp = React__default.default.useRef(props.formControl);
|
|
2731
|
+
const [formState, updateFormState] = React__default.default.useState(() => ({
|
|
2732
|
+
...cloneObject(DEFAULT_FORM_STATE),
|
|
2733
|
+
isLoading: isFunction(props.defaultValues),
|
|
2734
|
+
errors: props.errors || {},
|
|
2735
|
+
disabled: props.disabled || false,
|
|
2736
|
+
defaultValues: isFunction(props.defaultValues) ? void 0 : props.defaultValues
|
|
2737
|
+
}));
|
|
2738
|
+
if (!_formControl.current || props.formControl && _formControlProp.current !== props.formControl) {
|
|
2739
|
+
_formControlProp.current = props.formControl;
|
|
2740
|
+
if (props.formControl) {
|
|
2741
|
+
_formControl.current = {
|
|
2742
|
+
...props.formControl,
|
|
2743
|
+
formState
|
|
2744
|
+
};
|
|
2745
|
+
if (props.defaultValues && !isFunction(props.defaultValues)) {
|
|
2746
|
+
props.formControl.reset(props.defaultValues, props.resetOptions);
|
|
2996
2747
|
}
|
|
2997
|
-
} else if (strong !== void 0) {
|
|
2998
|
-
out.push({ type: "strong", children: parseInline(strong, depth + 1) });
|
|
2999
2748
|
} else {
|
|
3000
|
-
|
|
2749
|
+
const { formControl, ...rest } = createFormControl(props);
|
|
2750
|
+
_formControl.current = {
|
|
2751
|
+
...rest,
|
|
2752
|
+
formState
|
|
2753
|
+
};
|
|
3001
2754
|
}
|
|
3002
2755
|
}
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
const
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
blocks.push({ type: "paragraph", children: parseInline(para.join(" ")) });
|
|
3026
|
-
para = [];
|
|
3027
|
-
};
|
|
3028
|
-
for (let i = 0; i < lines.length; i++) {
|
|
3029
|
-
const line = lines[i];
|
|
3030
|
-
const fence = FENCE_RE.exec(line);
|
|
3031
|
-
if (fence) {
|
|
3032
|
-
flushPara();
|
|
3033
|
-
const body = [];
|
|
3034
|
-
i++;
|
|
3035
|
-
while (i < lines.length && !FENCE_RE.test(lines[i])) body.push(lines[i++]);
|
|
3036
|
-
blocks.push({ type: "codeBlock", label: fenceLabel(fence[1]), value: body.join("\n") });
|
|
3037
|
-
continue;
|
|
2756
|
+
const control = _formControl.current.control;
|
|
2757
|
+
control._options = props;
|
|
2758
|
+
useIsomorphicLayoutEffect(() => {
|
|
2759
|
+
const sub = control._subscribe({
|
|
2760
|
+
formState: control._proxyFormState,
|
|
2761
|
+
callback: () => updateFormState({
|
|
2762
|
+
...control._formState,
|
|
2763
|
+
defaultValues: control._defaultValues
|
|
2764
|
+
}),
|
|
2765
|
+
reRenderRoot: true
|
|
2766
|
+
});
|
|
2767
|
+
updateFormState((data) => ({
|
|
2768
|
+
...data,
|
|
2769
|
+
isReady: true
|
|
2770
|
+
}));
|
|
2771
|
+
control._formState.isReady = true;
|
|
2772
|
+
return sub;
|
|
2773
|
+
}, [control]);
|
|
2774
|
+
React__default.default.useEffect(() => control._disableForm(props.disabled), [control, props.disabled]);
|
|
2775
|
+
React__default.default.useEffect(() => {
|
|
2776
|
+
if (props.mode) {
|
|
2777
|
+
control._options.mode = props.mode;
|
|
3038
2778
|
}
|
|
3039
|
-
if (
|
|
3040
|
-
|
|
3041
|
-
continue;
|
|
2779
|
+
if (props.reValidateMode) {
|
|
2780
|
+
control._options.reValidateMode = props.reValidateMode;
|
|
3042
2781
|
}
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
2782
|
+
}, [control, props.mode, props.reValidateMode]);
|
|
2783
|
+
React__default.default.useEffect(() => {
|
|
2784
|
+
if (props.errors) {
|
|
2785
|
+
control._setErrors(props.errors);
|
|
2786
|
+
control._focusError();
|
|
3047
2787
|
}
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
2788
|
+
}, [control, props.errors]);
|
|
2789
|
+
React__default.default.useEffect(() => {
|
|
2790
|
+
props.shouldUnregister && control._subjects.state.next({
|
|
2791
|
+
values: control._getWatch()
|
|
2792
|
+
});
|
|
2793
|
+
}, [control, props.shouldUnregister]);
|
|
2794
|
+
React__default.default.useEffect(() => {
|
|
2795
|
+
if (control._proxyFormState.isDirty) {
|
|
2796
|
+
const isDirty = control._getDirty();
|
|
2797
|
+
if (isDirty !== formState.isDirty) {
|
|
2798
|
+
control._subjects.state.next({
|
|
2799
|
+
isDirty
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
}, [control, formState.isDirty]);
|
|
2804
|
+
React__default.default.useEffect(() => {
|
|
2805
|
+
var _a;
|
|
2806
|
+
if (props.values && !deepEqual(props.values, _values.current)) {
|
|
2807
|
+
control._reset(props.values, {
|
|
2808
|
+
keepFieldsRef: true,
|
|
2809
|
+
...control._options.resetOptions
|
|
3055
2810
|
});
|
|
3056
|
-
|
|
2811
|
+
if (!((_a = control._options.resetOptions) === null || _a === void 0 ? void 0 : _a.keepIsValid)) {
|
|
2812
|
+
control._setValid();
|
|
2813
|
+
}
|
|
2814
|
+
_values.current = props.values;
|
|
2815
|
+
updateFormState((state) => ({ ...state }));
|
|
2816
|
+
} else {
|
|
2817
|
+
control._resetDefaultValues();
|
|
3057
2818
|
}
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
if (
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
2819
|
+
}, [control, props.values]);
|
|
2820
|
+
React__default.default.useEffect(() => {
|
|
2821
|
+
if (!control._state.mount) {
|
|
2822
|
+
control._setValid();
|
|
2823
|
+
control._state.mount = true;
|
|
2824
|
+
}
|
|
2825
|
+
if (control._state.watch) {
|
|
2826
|
+
control._state.watch = false;
|
|
2827
|
+
control._subjects.state.next({ ...control._formState });
|
|
2828
|
+
}
|
|
2829
|
+
control._removeUnmounted();
|
|
2830
|
+
});
|
|
2831
|
+
_formControl.current.formState = React__default.default.useMemo(() => getProxyFormState(formState, control), [control, formState]);
|
|
2832
|
+
return _formControl.current;
|
|
2833
|
+
}
|
|
2834
|
+
const jsonToText = (v) => v == null || v === "" ? "" : JSON.stringify(v, null, 2);
|
|
2835
|
+
const JsonField = ({ field, f }) => {
|
|
2836
|
+
const { t } = reactI18next.useTranslation();
|
|
2837
|
+
const [raw, setRaw] = React.useState(() => jsonToText(f.value));
|
|
2838
|
+
const [error, setError] = React.useState(null);
|
|
2839
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2840
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2841
|
+
ui.Textarea,
|
|
2842
|
+
{
|
|
2843
|
+
rows: 6,
|
|
2844
|
+
className: "font-mono",
|
|
2845
|
+
placeholder: field.placeholder ? t(field.placeholder) : "[]",
|
|
2846
|
+
value: raw,
|
|
2847
|
+
disabled: field.readonly === true,
|
|
2848
|
+
onBlur: f.onBlur,
|
|
2849
|
+
onChange: (e) => {
|
|
2850
|
+
const text = e.target.value;
|
|
2851
|
+
setRaw(text);
|
|
2852
|
+
if (text.trim() === "") {
|
|
2853
|
+
setError(null);
|
|
2854
|
+
f.onChange(null);
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
try {
|
|
2858
|
+
f.onChange(JSON.parse(text));
|
|
2859
|
+
setError(null);
|
|
2860
|
+
} catch {
|
|
2861
|
+
setError(t("integration.form.invalidJson"));
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
),
|
|
2866
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: error })
|
|
2867
|
+
] });
|
|
2868
|
+
};
|
|
2869
|
+
const IntegrationField = ({
|
|
2870
|
+
field,
|
|
2871
|
+
control,
|
|
2872
|
+
secretConfigured = false
|
|
2873
|
+
}) => {
|
|
2874
|
+
const { t } = reactI18next.useTranslation();
|
|
2875
|
+
const enforceRequired = field.required && field.control !== "switch" && !(field.control === "secret" && secretConfigured);
|
|
2876
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2877
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Label, { size: "small", weight: "plus", children: [
|
|
2878
|
+
t(field.label),
|
|
2879
|
+
field.required ? " *" : ""
|
|
2880
|
+
] }),
|
|
2881
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2882
|
+
Controller,
|
|
2883
|
+
{
|
|
2884
|
+
name: field.name,
|
|
2885
|
+
control,
|
|
2886
|
+
rules: {
|
|
2887
|
+
required: enforceRequired ? t("integration.form.required", { field: t(field.label) }) : false
|
|
2888
|
+
},
|
|
2889
|
+
render: ({ field: f, fieldState }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2890
|
+
renderControl(field, f, secretConfigured, t),
|
|
2891
|
+
fieldState.error?.message && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-error", children: fieldState.error.message })
|
|
2892
|
+
] })
|
|
3070
2893
|
}
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
2894
|
+
),
|
|
2895
|
+
field.hint && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: t(field.hint) })
|
|
2896
|
+
] });
|
|
2897
|
+
};
|
|
2898
|
+
function renderControl(field, f, secretConfigured, t) {
|
|
2899
|
+
const placeholder = field.placeholder ? t(field.placeholder) : void 0;
|
|
2900
|
+
const disabled = field.readonly === true;
|
|
2901
|
+
switch (field.control) {
|
|
2902
|
+
case "switch":
|
|
2903
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Switch, { checked: !!f.value, onCheckedChange: f.onChange, disabled });
|
|
2904
|
+
case "select": {
|
|
2905
|
+
const declared = field.options ?? [];
|
|
2906
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2907
|
+
ui.Select,
|
|
2908
|
+
{
|
|
2909
|
+
value: f.value == null ? "" : String(f.value),
|
|
2910
|
+
onValueChange: (next) => f.onChange(declared.find((o) => String(o) === next) ?? next),
|
|
2911
|
+
disabled,
|
|
2912
|
+
children: [
|
|
2913
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder }) }),
|
|
2914
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Content, { children: declared.map((o) => {
|
|
2915
|
+
const labelKey = field.optionLabels?.[String(o)];
|
|
2916
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: String(o), children: labelKey ? t(labelKey) : String(o) }, String(o));
|
|
2917
|
+
}) })
|
|
2918
|
+
]
|
|
2919
|
+
}
|
|
2920
|
+
);
|
|
3074
2921
|
}
|
|
3075
|
-
|
|
3076
|
-
}
|
|
3077
|
-
flushPara();
|
|
3078
|
-
const weight = blocks.reduce((n, b) => n + (b.type === "list" ? b.items.length : 1), 0);
|
|
3079
|
-
return weight > MD_MAX_BLOCKS ? [{ type: "truncated" }] : blocks;
|
|
3080
|
-
}
|
|
3081
|
-
const Inline = ({ nodes }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: nodes.map((node, i) => {
|
|
3082
|
-
switch (node.type) {
|
|
3083
|
-
case "text":
|
|
3084
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.value }, i);
|
|
3085
|
-
case "code":
|
|
3086
|
-
return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "txt-compact-small bg-ui-bg-component rounded px-1 py-0.5", children: node.value }, i);
|
|
3087
|
-
case "strong":
|
|
3088
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "txt-compact-small-plus", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
3089
|
-
case "em":
|
|
3090
|
-
return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: node.children }) }, i);
|
|
3091
|
-
case "link":
|
|
2922
|
+
case "number":
|
|
3092
2923
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3093
|
-
|
|
2924
|
+
ui.Input,
|
|
3094
2925
|
{
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
}
|
|
3101
|
-
i
|
|
2926
|
+
type: "number",
|
|
2927
|
+
placeholder,
|
|
2928
|
+
value: f.value ?? "",
|
|
2929
|
+
disabled,
|
|
2930
|
+
onChange: (e) => f.onChange(e.target.value === "" ? null : e.target.valueAsNumber)
|
|
2931
|
+
}
|
|
3102
2932
|
);
|
|
3103
|
-
|
|
3104
|
-
}) });
|
|
3105
|
-
const Block = ({ block, truncatedLabel }) => {
|
|
3106
|
-
switch (block.type) {
|
|
3107
|
-
case "heading":
|
|
3108
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: block.level === 2 ? "h2" : "h3", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
3109
|
-
case "paragraph":
|
|
3110
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx(Inline, { nodes: block.children }) });
|
|
3111
|
-
case "list": {
|
|
3112
|
-
const List = block.ordered ? "ol" : "ul";
|
|
2933
|
+
case "url":
|
|
3113
2934
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3114
|
-
|
|
2935
|
+
ui.Input,
|
|
3115
2936
|
{
|
|
3116
|
-
|
|
3117
|
-
|
|
2937
|
+
type: "url",
|
|
2938
|
+
placeholder,
|
|
2939
|
+
value: f.value ?? "",
|
|
2940
|
+
disabled,
|
|
2941
|
+
onChange: f.onChange
|
|
2942
|
+
}
|
|
2943
|
+
);
|
|
2944
|
+
case "secret":
|
|
2945
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2946
|
+
ui.Input,
|
|
2947
|
+
{
|
|
2948
|
+
type: "password",
|
|
2949
|
+
placeholder: secretConfigured ? t("integration.form.keepSecret") : placeholder ?? "",
|
|
2950
|
+
value: f.value ?? "",
|
|
2951
|
+
disabled,
|
|
2952
|
+
onChange: f.onChange,
|
|
2953
|
+
className: !f.value ? "[&~div]:hidden" : void 0
|
|
2954
|
+
}
|
|
2955
|
+
);
|
|
2956
|
+
case "json":
|
|
2957
|
+
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { field, f });
|
|
2958
|
+
default:
|
|
2959
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2960
|
+
ui.Input,
|
|
2961
|
+
{
|
|
2962
|
+
placeholder,
|
|
2963
|
+
value: f.value ?? "",
|
|
2964
|
+
disabled,
|
|
2965
|
+
onChange: f.onChange
|
|
3118
2966
|
}
|
|
3119
2967
|
);
|
|
3120
|
-
}
|
|
3121
|
-
case "codeBlock":
|
|
3122
|
-
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: [
|
|
3123
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-2", children: [
|
|
3124
|
-
block.label ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-subtle", children: block.label }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
3125
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: block.value })
|
|
3126
|
-
] }),
|
|
3127
|
-
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "txt-compact-small text-ui-fg-subtle overflow-x-auto whitespace-pre pb-2", children: block.value })
|
|
3128
|
-
] });
|
|
3129
|
-
case "hr":
|
|
3130
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-ui-border-base border-t" });
|
|
3131
|
-
case "truncated":
|
|
3132
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: truncatedLabel });
|
|
3133
2968
|
}
|
|
3134
|
-
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
2969
|
+
}
|
|
2970
|
+
function isFieldVisible(field, values) {
|
|
2971
|
+
const rule = field.visibleWhen;
|
|
2972
|
+
if (!rule) return true;
|
|
2973
|
+
return values[rule.field] === rule.equals;
|
|
2974
|
+
}
|
|
2975
|
+
function sectionDefaults(section, values) {
|
|
2976
|
+
const out = {};
|
|
2977
|
+
for (const f of section.fields) {
|
|
2978
|
+
if (f.secret || f.control === "secret") out[f.name] = "";
|
|
2979
|
+
else if (f.control === "switch") out[f.name] = values?.[f.name] ?? f.default ?? false;
|
|
2980
|
+
else if (f.control === "json") out[f.name] = values?.[f.name] ?? f.default;
|
|
2981
|
+
else out[f.name] = values?.[f.name] ?? f.default ?? "";
|
|
2982
|
+
}
|
|
2983
|
+
return out;
|
|
2984
|
+
}
|
|
2985
|
+
const EditSectionDrawer = ({
|
|
2986
|
+
open,
|
|
2987
|
+
onOpenChange,
|
|
2988
|
+
providerId,
|
|
2989
|
+
section,
|
|
2990
|
+
values,
|
|
2991
|
+
configuredSecrets = []
|
|
3149
2992
|
}) => {
|
|
3150
|
-
const { t
|
|
3151
|
-
const
|
|
3152
|
-
const
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
2993
|
+
const { t } = reactI18next.useTranslation();
|
|
2994
|
+
const qc = reactQuery.useQueryClient();
|
|
2995
|
+
const configured = new Set(configuredSecrets);
|
|
2996
|
+
const [defaults] = React.useState(() => sectionDefaults(section, values));
|
|
2997
|
+
const form = useForm({ values: defaults });
|
|
2998
|
+
const current = form.watch();
|
|
2999
|
+
const visibilityValues = { ...values, ...current };
|
|
3000
|
+
const save = reactQuery.useMutation({
|
|
3001
|
+
mutationFn: (sectionValues) => sdk.client.fetch(`/admin/integrations/${providerId}`, {
|
|
3002
|
+
method: "POST",
|
|
3003
|
+
// Only this section's values + its id — the server validates just this section and
|
|
3004
|
+
// merges over the rest of the stored config (keeping blank/untouched secrets).
|
|
3005
|
+
body: { section_id: section.id, values: sectionValues }
|
|
3006
|
+
}),
|
|
3007
|
+
onSuccess: () => {
|
|
3008
|
+
ui.toast.success(t("integration.toast.saved"));
|
|
3009
|
+
qc.invalidateQueries({ queryKey: ["integration", providerId] });
|
|
3010
|
+
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
3011
|
+
onOpenChange(false);
|
|
3012
|
+
},
|
|
3013
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.saveFailed"))
|
|
3014
|
+
});
|
|
3015
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
3016
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: t(section.title) }) }),
|
|
3017
|
+
/* @__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(
|
|
3018
|
+
IntegrationField,
|
|
3019
|
+
{
|
|
3020
|
+
field: f,
|
|
3021
|
+
control: form.control,
|
|
3022
|
+
secretConfigured: configured.has(f.name)
|
|
3023
|
+
},
|
|
3024
|
+
f.name
|
|
3025
|
+
)) }),
|
|
3159
3026
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
3160
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: t("integration.actions.cancel") }) }),
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3027
|
+
/* @__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") }) }),
|
|
3028
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3029
|
+
ui.Button,
|
|
3030
|
+
{
|
|
3031
|
+
size: "small",
|
|
3032
|
+
isLoading: save.isPending,
|
|
3033
|
+
onClick: form.handleSubmit((v) => save.mutate(v)),
|
|
3034
|
+
children: t("integration.actions.save")
|
|
3035
|
+
}
|
|
3036
|
+
)
|
|
3165
3037
|
] })
|
|
3166
3038
|
] }) });
|
|
3167
3039
|
};
|
|
3168
|
-
const
|
|
3040
|
+
const Row = ({ label, children }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle grid grid-cols-2 px-6 py-4", children: [
|
|
3041
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: label }),
|
|
3042
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-2", children })
|
|
3043
|
+
] });
|
|
3044
|
+
const EditPage = () => {
|
|
3169
3045
|
const { t } = reactI18next.useTranslation();
|
|
3046
|
+
const { provider_id } = reactRouterDom.useParams();
|
|
3047
|
+
const qc = reactQuery.useQueryClient();
|
|
3170
3048
|
const navigate = reactRouterDom.useNavigate();
|
|
3171
|
-
const
|
|
3172
|
-
const [
|
|
3173
|
-
const
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
queryFn: () => sdk.client.fetch("/admin/integrations/catalog")
|
|
3049
|
+
const prompt = ui.usePrompt();
|
|
3050
|
+
const [editingSection, setEditingSection] = React.useState(null);
|
|
3051
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
3052
|
+
queryKey: ["integration", provider_id],
|
|
3053
|
+
queryFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`)
|
|
3177
3054
|
});
|
|
3178
|
-
const
|
|
3179
|
-
const
|
|
3180
|
-
const
|
|
3181
|
-
const
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3055
|
+
const descriptor = data?.descriptor;
|
|
3056
|
+
const record = data?.integration;
|
|
3057
|
+
const isConfigured = !!record;
|
|
3058
|
+
const isComplete = data?.is_complete ?? false;
|
|
3059
|
+
const invalidate = () => {
|
|
3060
|
+
qc.invalidateQueries({ queryKey: ["integration", provider_id] });
|
|
3061
|
+
qc.invalidateQueries({ queryKey: ["integration-overview"] });
|
|
3062
|
+
};
|
|
3063
|
+
const test = reactQuery.useMutation({
|
|
3064
|
+
mutationFn: () => sdk.client.fetch(
|
|
3065
|
+
`/admin/integrations/${provider_id}/test-connection`,
|
|
3066
|
+
{ method: "POST" }
|
|
3067
|
+
),
|
|
3068
|
+
onSuccess: (r) => {
|
|
3069
|
+
r.status === "passed" ? ui.toast.success(t("integration.toast.connectionOk")) : ui.toast.warning(r.message ?? r.status);
|
|
3070
|
+
invalidate();
|
|
3071
|
+
},
|
|
3072
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.testFailed"))
|
|
3185
3073
|
});
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3074
|
+
const toggleEnabled = reactQuery.useMutation({
|
|
3075
|
+
mutationFn: (next) => sdk.client.fetch(`/admin/integrations/${provider_id}/enable`, {
|
|
3076
|
+
method: "POST",
|
|
3077
|
+
body: { is_enabled: next }
|
|
3078
|
+
}),
|
|
3079
|
+
onSuccess: (_d, next) => {
|
|
3080
|
+
ui.toast.success(next ? t("integration.toast.enabled") : t("integration.toast.disabled"));
|
|
3081
|
+
invalidate();
|
|
3082
|
+
},
|
|
3083
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.updateFailed"))
|
|
3084
|
+
});
|
|
3085
|
+
const remove = reactQuery.useMutation({
|
|
3086
|
+
mutationFn: () => sdk.client.fetch(`/admin/integrations/${provider_id}`, { method: "DELETE" }),
|
|
3087
|
+
onSuccess: () => {
|
|
3088
|
+
ui.toast.success(t("integration.toast.deleted"));
|
|
3089
|
+
invalidate();
|
|
3090
|
+
navigate("/settings/integrations");
|
|
3091
|
+
},
|
|
3092
|
+
onError: (e) => ui.toast.error(e?.message ?? t("integration.toast.deleteFailed"))
|
|
3093
|
+
});
|
|
3094
|
+
const onDelete = async () => {
|
|
3095
|
+
const confirmed = await prompt({
|
|
3096
|
+
title: t("integration.delete.title"),
|
|
3097
|
+
description: t("integration.delete.description", {
|
|
3098
|
+
name: descriptor ? t(descriptor.displayName) : ""
|
|
3099
|
+
}),
|
|
3100
|
+
confirmText: t("integration.actions.delete"),
|
|
3101
|
+
cancelText: t("integration.actions.cancel")
|
|
3102
|
+
});
|
|
3103
|
+
if (confirmed) remove.mutate();
|
|
3104
|
+
};
|
|
3105
|
+
if (isLoading) {
|
|
3106
|
+
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" }) });
|
|
3107
|
+
}
|
|
3108
|
+
if (!descriptor) {
|
|
3109
|
+
throw new Medusa.FetchError(
|
|
3110
|
+
`Integration with provider_id: ${provider_id} was not found`,
|
|
3111
|
+
"There is no page at this address",
|
|
3112
|
+
404
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
const version = data?.version ?? null;
|
|
3116
|
+
const author = data?.author ?? null;
|
|
3117
|
+
const authorUrl = data?.author_url ?? null;
|
|
3118
|
+
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") };
|
|
3119
|
+
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;
|
|
3120
|
+
const editing = descriptor.sections.find((s) => s.id === editingSection) ?? null;
|
|
3121
|
+
const configuredSecrets = new Set(record?.configured_secrets ?? []);
|
|
3122
|
+
const sectionData = {
|
|
3123
|
+
providerId: provider_id,
|
|
3124
|
+
identifier: descriptor.identifier,
|
|
3125
|
+
values: record?.values ?? {},
|
|
3126
|
+
isComplete
|
|
3127
|
+
};
|
|
3128
|
+
const renderSection = (section) => /* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: section.id, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
3129
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
3130
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t(section.title) }),
|
|
3131
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
3132
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
3133
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: () => setEditingSection(section.id), children: [
|
|
3134
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "text-ui-fg-subtle" }),
|
|
3135
|
+
t("integration.actions.edit")
|
|
3136
|
+
] }) })
|
|
3208
3137
|
] })
|
|
3209
3138
|
] }),
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3139
|
+
section.fields.filter((f) => isFieldVisible(f, record?.values ?? {})).map((f) => /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t(f.label), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3140
|
+
IntegrationFieldValue,
|
|
3141
|
+
{
|
|
3142
|
+
field: f,
|
|
3143
|
+
value: record?.values?.[f.name] ?? f.default,
|
|
3144
|
+
secretConfigured: configuredSecrets.has(f.name)
|
|
3145
|
+
}
|
|
3146
|
+
) }, f.name))
|
|
3147
|
+
] }, section.id) });
|
|
3148
|
+
const mainSections = descriptor.sections.filter((s) => s.column !== "side");
|
|
3149
|
+
const sideSections = descriptor.sections.filter((s) => s.column === "side");
|
|
3150
|
+
const main = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3151
|
+
/* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: "main", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
3152
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 px-6 py-4", children: [
|
|
3153
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
3213
3154
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
3214
|
-
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src:
|
|
3215
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
3216
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3217
|
-
|
|
3155
|
+
/* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: descriptor.icon, alt: t(descriptor.displayName) }),
|
|
3156
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
3157
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
3158
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: t(descriptor.displayName) }),
|
|
3159
|
+
provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: toModuleProviderId(provider_id, descriptor.category) })
|
|
3160
|
+
] }),
|
|
3161
|
+
version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
|
|
3162
|
+
t("integration.meta.version", { version }),
|
|
3163
|
+
author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3164
|
+
" • ",
|
|
3165
|
+
t("integration.meta.authorPrefix"),
|
|
3166
|
+
" ",
|
|
3167
|
+
authorUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3168
|
+
"a",
|
|
3169
|
+
{
|
|
3170
|
+
href: authorUrl,
|
|
3171
|
+
target: "_blank",
|
|
3172
|
+
rel: "noreferrer",
|
|
3173
|
+
className: "text-ui-fg-interactive",
|
|
3174
|
+
children: author
|
|
3175
|
+
}
|
|
3176
|
+
) : author
|
|
3177
|
+
] })
|
|
3178
|
+
] })
|
|
3218
3179
|
] })
|
|
3219
3180
|
] }),
|
|
3220
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3181
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
3182
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
|
|
3183
|
+
connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label }),
|
|
3184
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
3185
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
3186
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
|
|
3187
|
+
descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
|
|
3188
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3189
|
+
ui.DropdownMenu.Item,
|
|
3190
|
+
{
|
|
3191
|
+
disabled: toggleEnabled.isPending,
|
|
3192
|
+
onClick: () => toggleEnabled.mutate(!record.is_enabled),
|
|
3193
|
+
children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
|
|
3194
|
+
}
|
|
3195
|
+
),
|
|
3196
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
|
|
3197
|
+
isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
|
|
3198
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
|
|
3199
|
+
t("integration.actions.delete")
|
|
3200
|
+
] })
|
|
3201
|
+
] })
|
|
3202
|
+
] })
|
|
3203
|
+
] })
|
|
3221
3204
|
] }),
|
|
3222
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle
|
|
3205
|
+
descriptor.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(descriptor.description) })
|
|
3223
3206
|
] }),
|
|
3224
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3225
|
-
item.installed && item.provider_id ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3226
|
-
ui.Button,
|
|
3227
|
-
{
|
|
3228
|
-
size: "small",
|
|
3229
|
-
variant: "primary",
|
|
3230
|
-
onClick: () => navigate(`/settings/integrations/${item.provider_id}`),
|
|
3231
|
-
children: t("integration.browse.settings")
|
|
3232
|
-
}
|
|
3233
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", variant: "secondary", onClick: () => setInstallItem(item), children: [
|
|
3234
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
|
|
3235
|
-
t("integration.browse.add")
|
|
3236
|
-
] }),
|
|
3237
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(`integration.categories.${item.category}`) })
|
|
3238
|
-
] })
|
|
3239
|
-
] }, item.integrationId)) }),
|
|
3240
|
-
/* @__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: [
|
|
3241
|
-
t("integration.browse.learnMore"),
|
|
3242
|
-
" →"
|
|
3207
|
+
/* @__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}`) }) })
|
|
3243
3208
|
] }) }),
|
|
3244
|
-
|
|
3209
|
+
mainSections.map(renderSection),
|
|
3210
|
+
editing && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3211
|
+
EditSectionDrawer,
|
|
3212
|
+
{
|
|
3213
|
+
open: true,
|
|
3214
|
+
onOpenChange: (open) => !open && setEditingSection(null),
|
|
3215
|
+
providerId: provider_id,
|
|
3216
|
+
section: editing,
|
|
3217
|
+
values: record?.values ?? {},
|
|
3218
|
+
configuredSecrets: record?.configured_secrets ?? []
|
|
3219
|
+
},
|
|
3220
|
+
editing.id
|
|
3221
|
+
)
|
|
3245
3222
|
] });
|
|
3223
|
+
const side = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: sideSections.map(renderSection) });
|
|
3224
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3225
|
+
components.LayoutComposer,
|
|
3226
|
+
{
|
|
3227
|
+
widgetsZonePrefix: `gorgo.integration.${descriptor.identifier}`,
|
|
3228
|
+
preferredLayoutId: descriptor.preferredLayoutId,
|
|
3229
|
+
sections: { main, side },
|
|
3230
|
+
data: sectionData,
|
|
3231
|
+
hasOutlet: false
|
|
3232
|
+
}
|
|
3233
|
+
);
|
|
3246
3234
|
};
|
|
3247
|
-
const
|
|
3235
|
+
const IntegrationBreadcrumb = ({ params }) => {
|
|
3248
3236
|
const { t } = reactI18next.useTranslation();
|
|
3249
|
-
|
|
3237
|
+
const providerId = params.provider_id;
|
|
3238
|
+
const { data } = reactQuery.useQuery({
|
|
3239
|
+
queryKey: ["integration", providerId],
|
|
3240
|
+
queryFn: () => sdk.client.fetch(`/admin/integrations/${providerId}`),
|
|
3241
|
+
enabled: !!providerId
|
|
3242
|
+
});
|
|
3243
|
+
const descriptor = data?.descriptor;
|
|
3244
|
+
if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
|
|
3245
|
+
const name = t(descriptor.displayName);
|
|
3246
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3247
|
+
name + " ",
|
|
3248
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: providerId })
|
|
3249
|
+
] });
|
|
3250
3250
|
};
|
|
3251
3251
|
const handle = {
|
|
3252
|
-
breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3252
|
+
breadcrumb: ({ params }) => /* @__PURE__ */ jsxRuntime.jsx(IntegrationBreadcrumb, { params })
|
|
3253
3253
|
};
|
|
3254
3254
|
const integration$w = { "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": "العودة إلى التكاملات" };
|
|
3255
3255
|
const ar = {
|
|
@@ -3427,13 +3427,13 @@ const routeModule = {
|
|
|
3427
3427
|
handle: { label: config.label, translationNs: config.translationNs, ...handle$2 }
|
|
3428
3428
|
},
|
|
3429
3429
|
{
|
|
3430
|
-
Component:
|
|
3431
|
-
path: "/settings/integrations
|
|
3430
|
+
Component: BrowsePage,
|
|
3431
|
+
path: "/settings/integrations/browse",
|
|
3432
3432
|
handle: handle$1
|
|
3433
3433
|
},
|
|
3434
3434
|
{
|
|
3435
|
-
Component:
|
|
3436
|
-
path: "/settings/integrations
|
|
3435
|
+
Component: EditPage,
|
|
3436
|
+
path: "/settings/integrations/:provider_id",
|
|
3437
3437
|
handle
|
|
3438
3438
|
}
|
|
3439
3439
|
]
|