@gorgo/medusa-integration 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.medusa/server/src/admin/index.js +519 -106
  2. package/.medusa/server/src/admin/index.mjs +522 -109
  3. package/.medusa/server/src/api/admin/integrations/[provider_id]/route.js +7 -2
  4. package/.medusa/server/src/api/admin/integrations/catalog/route.d.ts +3 -0
  5. package/.medusa/server/src/api/admin/integrations/catalog/route.js +10 -0
  6. package/.medusa/server/src/api/admin/integrations/middlewares.js +16 -1
  7. package/.medusa/server/src/api/admin/integrations/route.js +17 -3
  8. package/.medusa/server/src/api/admin/integrations/validators.d.ts +7 -0
  9. package/.medusa/server/src/api/admin/integrations/validators.js +8 -2
  10. package/.medusa/server/src/jobs/test-integration-connections.js +3 -3
  11. package/.medusa/server/src/lib/__tests__/catalog-fallback.unit.spec.d.ts +1 -0
  12. package/.medusa/server/src/lib/__tests__/catalog-fallback.unit.spec.js +37 -0
  13. package/.medusa/server/src/lib/__tests__/catalog.unit.spec.d.ts +1 -0
  14. package/.medusa/server/src/lib/__tests__/catalog.unit.spec.js +71 -0
  15. package/.medusa/server/src/lib/catalog.d.ts +9 -0
  16. package/.medusa/server/src/lib/catalog.generated.d.ts +2 -0
  17. package/.medusa/server/src/lib/catalog.generated.js +88 -0
  18. package/.medusa/server/src/lib/catalog.js +55 -0
  19. package/.medusa/server/src/modules/integration/loaders/__tests__/package-meta.unit.spec.d.ts +1 -0
  20. package/.medusa/server/src/modules/integration/loaders/__tests__/package-meta.unit.spec.js +63 -0
  21. package/.medusa/server/src/modules/integration/loaders/load-providers.js +9 -37
  22. package/.medusa/server/src/modules/integration/loaders/package-meta.d.ts +22 -0
  23. package/.medusa/server/src/modules/integration/loaders/package-meta.js +45 -0
  24. package/.medusa/server/src/modules/integration/loaders/resolve-package-meta.d.ts +8 -0
  25. package/.medusa/server/src/modules/integration/loaders/resolve-package-meta.js +59 -0
  26. package/.medusa/server/src/modules/integration/migrations/Migration20260724101525.d.ts +5 -0
  27. package/.medusa/server/src/modules/integration/migrations/Migration20260724101525.js +16 -0
  28. package/.medusa/server/src/modules/integration/models/integration.d.ts +8 -4
  29. package/.medusa/server/src/modules/integration/services/__tests__/integration-module.unit.spec.js +107 -3
  30. package/.medusa/server/src/modules/integration/services/integration-module.d.ts +46 -9
  31. package/.medusa/server/src/modules/integration/services/integration-module.js +54 -4
  32. package/.medusa/server/src/modules/integration/types/index.d.ts +11 -0
  33. package/.medusa/server/src/modules/integration/types/index.js +4 -2
  34. package/.medusa/server/src/modules/integration/utils/integration.d.ts +8 -4
  35. package/.medusa/server/src/modules/integration/utils/integration.js +9 -5
  36. package/.medusa/server/src/types/http/integration/admin/entities.d.ts +34 -0
  37. package/.medusa/server/src/types/http/integration/admin/responses.d.ts +17 -2
  38. package/.medusa/server/src/workflows/integration/steps/set-integration-enabled.d.ts +1 -1
  39. package/.medusa/server/src/workflows/integration/workflows/set-integration-enabled.d.ts +1 -1
  40. package/README.md +5 -5
  41. package/README.ru.md +5 -5
  42. package/package.json +7 -3
@@ -4,14 +4,14 @@ const adminSdk = require("@medusajs/admin-sdk");
4
4
  const icons = require("@medusajs/icons");
5
5
  const ui = require("@medusajs/ui");
6
6
  const reactQuery = require("@tanstack/react-query");
7
+ const React = require("react");
7
8
  const reactI18next = require("react-i18next");
8
9
  const reactRouterDom = require("react-router-dom");
9
10
  const Medusa = require("@medusajs/js-sdk");
10
- const React = require("react");
11
11
  const components = require("@medusajs/dashboard/components");
12
12
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
13
- const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
14
13
  const React__default = /* @__PURE__ */ _interopDefault(React);
14
+ const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
15
15
  const sdk = new Medusa__default.default({
16
16
  baseUrl: "/",
17
17
  debug: false,
@@ -22,9 +22,23 @@ const sdk = new Medusa__default.default({
22
22
  const IntegrationIcon = ({
23
23
  src,
24
24
  alt,
25
- size = "base"
25
+ size = "base",
26
+ framed = true
26
27
  }) => {
27
28
  const dim = size === "small" ? "h-6 w-6" : "h-10 w-10";
29
+ const [errored, setErrored] = React.useState(false);
30
+ const showImage = !!src && !errored;
31
+ if (showImage && !framed) {
32
+ return /* @__PURE__ */ jsxRuntime.jsx(
33
+ "img",
34
+ {
35
+ src,
36
+ alt,
37
+ className: ui.clx("shrink-0 object-contain", dim),
38
+ onError: () => setErrored(true)
39
+ }
40
+ );
41
+ }
28
42
  return /* @__PURE__ */ jsxRuntime.jsx(
29
43
  "div",
30
44
  {
@@ -32,50 +46,237 @@ const IntegrationIcon = ({
32
46
  "bg-ui-bg-component shadow-borders-base flex shrink-0 items-center justify-center overflow-hidden rounded-md",
33
47
  dim
34
48
  ),
35
- children: src ? /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(icons.PuzzleSolid, { className: "text-ui-fg-subtle" })
49
+ children: showImage ? /* @__PURE__ */ jsxRuntime.jsx(
50
+ "img",
51
+ {
52
+ src,
53
+ alt,
54
+ className: "h-full w-full object-cover",
55
+ onError: () => setErrored(true)
56
+ }
57
+ ) : /* @__PURE__ */ jsxRuntime.jsx(icons.PuzzleSolid, { className: "text-ui-fg-subtle" })
36
58
  }
37
59
  );
38
60
  };
61
+ const PAGE_SIZE = 20;
62
+ const DOCS_URL = "https://docs.gorgojs.com/ru/medusa-plugins/integration-module";
63
+ const IntegrationCell = ({ item }) => {
64
+ const { t } = reactI18next.useTranslation();
65
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
66
+ /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: item.icon, alt: t(item.display_name), size: "base" }),
67
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
68
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
69
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", weight: "plus", children: t(item.display_name) }),
70
+ item.provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: item.provider_id })
71
+ ] }),
72
+ item.version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
73
+ t("integration.meta.version", { version: item.version }),
74
+ item.author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
75
+ " • ",
76
+ t("integration.meta.authorPrefix"),
77
+ " ",
78
+ item.author_url ? /* @__PURE__ */ jsxRuntime.jsx(
79
+ "a",
80
+ {
81
+ href: item.author_url,
82
+ target: "_blank",
83
+ rel: "noreferrer",
84
+ className: "text-ui-fg-interactive",
85
+ onClick: (e) => e.stopPropagation(),
86
+ children: item.author
87
+ }
88
+ ) : item.author
89
+ ] })
90
+ ] })
91
+ ] })
92
+ ] });
93
+ };
94
+ const StatusCell = ({ item }) => {
95
+ const { t } = reactI18next.useTranslation();
96
+ const lifecycle = !item.is_configured ? { color: "grey", label: t("integration.status.notConfigured") } : !item.is_complete ? { color: "orange", label: t("integration.status.incomplete") } : item.is_enabled ? { color: "green", label: t("integration.status.enabled") } : { color: "grey", label: t("integration.status.disabled") };
97
+ const connection = item.has_test_connection ? item.last_test_status === "passed" ? { color: "green", label: t("integration.status.connected") } : item.last_test_status === "failed" ? { color: "red", label: t("integration.status.connectionFailed") } : null : null;
98
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-1", children: [
99
+ /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
100
+ connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label })
101
+ ] });
102
+ };
103
+ const columnHelper = ui.createDataTableColumnHelper();
39
104
  const IntegrationsPage = () => {
40
105
  const { t } = reactI18next.useTranslation();
106
+ const navigate = reactRouterDom.useNavigate();
107
+ const qc = reactQuery.useQueryClient();
108
+ const prompt = ui.usePrompt();
109
+ const [activeTab, setActiveTab] = React.useState("all");
110
+ const [search, setSearch] = React.useState("");
111
+ const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: PAGE_SIZE });
112
+ const category = activeTab === "all" ? void 0 : activeTab;
113
+ const queryParams = {
114
+ q: search || void 0,
115
+ category,
116
+ limit: pagination.pageSize,
117
+ offset: pagination.pageIndex * pagination.pageSize
118
+ };
41
119
  const { data, isLoading } = reactQuery.useQuery({
42
- queryKey: ["integration-overview"],
43
- queryFn: () => sdk.client.fetch("/admin/integrations")
120
+ queryKey: ["integration-overview", queryParams],
121
+ queryFn: () => sdk.client.fetch("/admin/integrations", { query: queryParams }),
122
+ placeholderData: reactQuery.keepPreviousData
44
123
  });
45
- const items = (data == null ? void 0 : data.integrations) ?? [];
46
- const byCategory = items.reduce((acc, i) => {
47
- var _a;
48
- (acc[_a = i.category] ?? (acc[_a] = [])).push(i);
49
- return acc;
50
- }, {});
124
+ const integrations = (data == null ? void 0 : data.integrations) ?? [];
125
+ const count = (data == null ? void 0 : data.count) ?? 0;
126
+ const categories = (data == null ? void 0 : data.categories) ?? [];
127
+ const invalidate = () => qc.invalidateQueries({ queryKey: ["integration-overview"] });
128
+ const toggleEnabled = reactQuery.useMutation({
129
+ mutationFn: ({ providerId, next }) => sdk.client.fetch(`/admin/integrations/${providerId}/enable`, {
130
+ method: "POST",
131
+ body: { is_enabled: next }
132
+ }),
133
+ onSuccess: (_d, { next }) => {
134
+ ui.toast.success(next ? t("integration.toast.enabled") : t("integration.toast.disabled"));
135
+ invalidate();
136
+ },
137
+ onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.updateFailed"))
138
+ });
139
+ const test = reactQuery.useMutation({
140
+ mutationFn: (providerId) => sdk.client.fetch(
141
+ `/admin/integrations/${providerId}/test-connection`,
142
+ { method: "POST" }
143
+ ),
144
+ onSuccess: (r) => {
145
+ r.status === "passed" ? ui.toast.success(t("integration.toast.connectionOk")) : ui.toast.warning(r.message ?? r.status);
146
+ invalidate();
147
+ },
148
+ onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.testFailed"))
149
+ });
150
+ const remove = reactQuery.useMutation({
151
+ mutationFn: (providerId) => sdk.client.fetch(`/admin/integrations/${providerId}`, { method: "DELETE" }),
152
+ onSuccess: () => {
153
+ ui.toast.success(t("integration.toast.deleted"));
154
+ invalidate();
155
+ },
156
+ onError: (e) => ui.toast.error((e == null ? void 0 : e.message) ?? t("integration.toast.deleteFailed"))
157
+ });
158
+ const onDelete = async (item) => {
159
+ const confirmed = await prompt({
160
+ title: t("integration.delete.title"),
161
+ description: t("integration.delete.description", { name: t(item.display_name) }),
162
+ confirmText: t("integration.actions.delete"),
163
+ cancelText: t("integration.actions.cancel")
164
+ });
165
+ if (confirmed) remove.mutate(item.provider_id);
166
+ };
167
+ const buildActions = (item) => {
168
+ const groups = [];
169
+ const primary = [];
170
+ if (item.is_configured) {
171
+ primary.push({
172
+ label: item.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable"),
173
+ onClick: () => toggleEnabled.mutate({ providerId: item.provider_id, next: !item.is_enabled })
174
+ });
175
+ }
176
+ if (item.is_configured && item.has_test_connection && item.is_complete) {
177
+ primary.push({
178
+ label: t("integration.actions.test"),
179
+ onClick: () => test.mutate(item.provider_id)
180
+ });
181
+ }
182
+ if (primary.length) groups.push(primary);
183
+ if (item.is_configured) {
184
+ groups.push([
185
+ {
186
+ label: t("integration.actions.delete"),
187
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}),
188
+ onClick: () => onDelete(item)
189
+ }
190
+ ]);
191
+ }
192
+ return groups.length > 0 ? groups : null;
193
+ };
194
+ const columns = React.useMemo(
195
+ () => [
196
+ columnHelper.display({
197
+ id: "integration",
198
+ header: t("integration.columns.integration"),
199
+ cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx(IntegrationCell, { item: row.original })
200
+ }),
201
+ columnHelper.display({
202
+ id: "description",
203
+ header: t("integration.columns.description"),
204
+ cell: ({ row }) => row.original.description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "text-ui-fg-subtle line-clamp-2", children: t(row.original.description) }) : null
205
+ }),
206
+ columnHelper.display({
207
+ id: "category",
208
+ header: t("integration.columns.category"),
209
+ cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: t(`integration.categories.${row.original.category}`) })
210
+ }),
211
+ columnHelper.display({
212
+ id: "status",
213
+ header: t("integration.columns.status"),
214
+ cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx(StatusCell, { item: row.original })
215
+ }),
216
+ columnHelper.action({
217
+ actions: (ctx) => buildActions(ctx.row.original)
218
+ })
219
+ ],
220
+ // Safe to memo on [t] only: react-query keeps each mutation's `mutate` ref stable across
221
+ // renders (options updated internally), so the closed-over buildActions/mutations stay current.
222
+ // eslint-disable-next-line react-hooks/exhaustive-deps
223
+ [t]
224
+ );
225
+ const table = ui.useDataTable({
226
+ data: integrations,
227
+ columns,
228
+ rowCount: count,
229
+ isLoading,
230
+ getRowId: (row) => row.provider_id,
231
+ onRowClick: (_e, row) => {
232
+ const item = row.original;
233
+ navigate(`/settings/integrations/${item.provider_id}`);
234
+ },
235
+ search: { state: search, onSearchChange: setSearch },
236
+ pagination: { state: pagination, onPaginationChange: setPagination }
237
+ });
238
+ const isFiltered = activeTab !== "all" || search.trim().length > 0;
51
239
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
52
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("integration.list.title") }) }),
53
- isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center px-6 py-8", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Spinner, { className: "text-ui-fg-subtle animate-spin" }) }) : items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.list.empty") }) }) : Object.entries(byCategory).map(([category, group]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4", children: [
54
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: t(`integration.categories.${category}`) }),
55
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex flex-col gap-2", children: group.map((i) => {
56
- const configured = i.is_configured && i.is_enabled;
57
- return /* @__PURE__ */ jsxRuntime.jsxs(
58
- reactRouterDom.Link,
240
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
241
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("integration.list.title") }),
243
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.list.subtitle") })
244
+ ] }),
245
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "primary", onClick: () => navigate("/settings/integrations/browse"), children: t("integration.list.browseIntegrations") })
246
+ ] }),
247
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, className: "[&_td]:py-3", children: [
248
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Toolbar, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center justify-between gap-3", children: [
249
+ /* @__PURE__ */ jsxRuntime.jsx(
250
+ ui.Tabs,
59
251
  {
60
- to: `/settings/integrations/${i.provider_id}`,
61
- className: "flex items-center justify-between rounded-md border px-3 py-2 hover:bg-ui-bg-base-hover",
62
- children: [
63
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
64
- /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: i.icon, alt: t(i.display_name), size: "small" }),
65
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: t(i.display_name) }),
66
- i.instance_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: i.instance_id })
67
- ] }),
68
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
69
- i.last_test_status === "passed" && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "green", size: "2xsmall", children: t("integration.list.tested") }),
70
- i.last_test_status === "failed" && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "red", size: "2xsmall", children: t("integration.list.testFailed") }),
71
- i.is_configured && !i.is_complete ? /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: "orange", children: t("integration.status.incomplete") }) : /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: configured ? "green" : "grey", children: !i.is_configured ? t("integration.status.notConfigured") : i.is_enabled ? t("integration.status.enabled") : t("integration.status.disabled") })
72
- ] })
73
- ]
74
- },
75
- i.provider_id
76
- );
77
- }) })
78
- ] }, category))
252
+ value: activeTab,
253
+ onValueChange: (v) => {
254
+ setActiveTab(v);
255
+ setPagination((p) => ({ ...p, pageIndex: 0 }));
256
+ },
257
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
258
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "all", children: t("integration.list.tabs.all") }),
259
+ categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: c, children: t(`integration.categories.${c}`) }, c))
260
+ ] })
261
+ }
262
+ ),
263
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Search, { placeholder: t("integration.list.searchPlaceholder") })
264
+ ] }) }),
265
+ /* @__PURE__ */ jsxRuntime.jsx(
266
+ ui.DataTable.Table,
267
+ {
268
+ emptyState: {
269
+ empty: { heading: isFiltered ? t("integration.list.emptyFiltered") : t("integration.list.empty") },
270
+ filtered: { heading: t("integration.list.emptyFiltered") }
271
+ }
272
+ }
273
+ ),
274
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
275
+ ] }),
276
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("a", { href: DOCS_URL, target: "_blank", rel: "noreferrer", className: "text-ui-fg-base txt-small", children: [
277
+ t("integration.list.learnMore"),
278
+ " →"
279
+ ] }) })
79
280
  ] });
80
281
  };
81
282
  const config = adminSdk.defineRouteConfig({
@@ -87,7 +288,7 @@ const IntegrationsBreadcrumb = () => {
87
288
  const { t } = reactI18next.useTranslation();
88
289
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("integration.list.title") });
89
290
  };
90
- const handle$1 = {
291
+ const handle$2 = {
91
292
  breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(IntegrationsBreadcrumb, {})
92
293
  };
93
294
  const Dash = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "text-ui-fg-subtle", children: "-" });
@@ -1883,7 +2084,6 @@ const EditSectionDrawer = ({
1883
2084
  ] })
1884
2085
  ] }) });
1885
2086
  };
1886
- const testColor = (s) => s === "passed" ? "green" : s === "failed" ? "red" : "grey";
1887
2087
  const Row = ({ label, children }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-ui-fg-subtle grid grid-cols-2 px-6 py-4", children: [
1888
2088
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: label }),
1889
2089
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-x-2", children })
@@ -1955,9 +2155,14 @@ const EditPage = () => {
1955
2155
  if (!descriptor) {
1956
2156
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6", children: [
1957
2157
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.unknown", { id: provider_id }) }),
1958
- /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: "/settings/integrations", className: "text-ui-fg-interactive", children: t("integration.back") })
2158
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: "/settings/integrations", className: "text-ui-fg-base", children: t("integration.back") })
1959
2159
  ] });
1960
2160
  }
2161
+ const version = (data == null ? void 0 : data.version) ?? null;
2162
+ const author = (data == null ? void 0 : data.author) ?? null;
2163
+ const authorUrl = (data == null ? void 0 : data.author_url) ?? null;
2164
+ 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") };
2165
+ const connection = descriptor.hasTestConnection ? (record == null ? void 0 : record.last_test_status) === "passed" ? { color: "green", label: t("integration.status.connected") } : (record == null ? void 0 : record.last_test_status) === "failed" ? { color: "red", label: t("integration.status.connectionFailed") } : null : null;
1961
2166
  const editing = descriptor.sections.find((s) => s.id === editingSection) ?? null;
1962
2167
  const configuredSecrets = new Set((record == null ? void 0 : record.configured_secrets) ?? []);
1963
2168
  const sectionData = {
@@ -1993,40 +2198,62 @@ const EditPage = () => {
1993
2198
  const sideSections = descriptor.sections.filter((s) => s.column === "side");
1994
2199
  const main = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1995
2200
  /* @__PURE__ */ jsxRuntime.jsx(components.LayoutComposer.Entry, { id: "main", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
1996
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
1997
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
1998
- /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: descriptor.icon, alt: t(descriptor.displayName) }),
1999
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
2000
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
2001
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: t(descriptor.displayName) }),
2002
- descriptor.instanceId && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: descriptor.instanceId })
2003
- ] }),
2004
- descriptor.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(descriptor.description) })
2005
- ] })
2006
- ] }),
2007
- isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
2008
- /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
2009
- /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
2010
- descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
2011
- isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
2012
- ui.DropdownMenu.Item,
2013
- {
2014
- disabled: toggleEnabled.isPending,
2015
- onClick: () => toggleEnabled.mutate(!record.is_enabled),
2016
- children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
2017
- }
2018
- ),
2019
- isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
2020
- isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
2021
- /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
2022
- t("integration.actions.delete")
2201
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 px-6 py-4", children: [
2202
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2203
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
2204
+ /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: descriptor.icon, alt: t(descriptor.displayName) }),
2205
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
2206
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
2207
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: t(descriptor.displayName) }),
2208
+ provider_id && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: provider_id })
2209
+ ] }),
2210
+ version && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: [
2211
+ t("integration.meta.version", { version }),
2212
+ author && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2213
+ " • ",
2214
+ t("integration.meta.authorPrefix"),
2215
+ " ",
2216
+ authorUrl ? /* @__PURE__ */ jsxRuntime.jsx(
2217
+ "a",
2218
+ {
2219
+ href: authorUrl,
2220
+ target: "_blank",
2221
+ rel: "noreferrer",
2222
+ className: "text-ui-fg-interactive",
2223
+ children: author
2224
+ }
2225
+ ) : author
2226
+ ] })
2227
+ ] })
2228
+ ] })
2229
+ ] }),
2230
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
2231
+ /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: lifecycle.color, children: lifecycle.label }),
2232
+ connection && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: connection.color, children: connection.label }),
2233
+ isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
2234
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
2235
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { children: [
2236
+ descriptor.hasTestConnection && record.is_enabled && isComplete && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Item, { disabled: test.isPending, onClick: () => test.mutate(), children: t("integration.actions.test") }),
2237
+ isConfigured && /* @__PURE__ */ jsxRuntime.jsx(
2238
+ ui.DropdownMenu.Item,
2239
+ {
2240
+ disabled: toggleEnabled.isPending,
2241
+ onClick: () => toggleEnabled.mutate(!record.is_enabled),
2242
+ children: record.is_enabled ? t("integration.actions.disable") : t("integration.actions.enable")
2243
+ }
2244
+ ),
2245
+ isConfigured && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
2246
+ isConfigured && /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { className: "gap-x-2", onClick: onDelete, children: [
2247
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
2248
+ t("integration.actions.delete")
2249
+ ] })
2250
+ ] })
2023
2251
  ] })
2024
2252
  ] })
2025
- ] })
2253
+ ] }),
2254
+ descriptor.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(descriptor.description) })
2026
2255
  ] }),
2027
- /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("integration.labels.status"), children: !isConfigured ? /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: "grey", children: t("integration.status.notConfigured") }) : !isComplete ? /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: "orange", children: t("integration.status.incomplete") }) : /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: record.is_enabled ? "green" : "grey", children: record.is_enabled ? t("integration.status.enabled") : t("integration.status.disabled") }) }),
2028
- /* @__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}`) }) }),
2029
- /* @__PURE__ */ jsxRuntime.jsx(Row, { label: t("integration.labels.lastTest"), children: (record == null ? void 0 : record.last_test_status) ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: testColor(record.last_test_status), children: t(`integration.testStatus.${record.last_test_status}`) }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "text-ui-fg-subtle", children: t("integration.labels.neverTested") }) })
2256
+ /* @__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}`) }) })
2030
2257
  ] }) }),
2031
2258
  mainSections.map(renderSection),
2032
2259
  editing && /* @__PURE__ */ jsxRuntime.jsx(
@@ -2063,50 +2290,199 @@ const IntegrationBreadcrumb = ({ params }) => {
2063
2290
  enabled: !!providerId
2064
2291
  });
2065
2292
  const descriptor = data == null ? void 0 : data.descriptor;
2066
- if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: providerId });
2293
+ if (!descriptor) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
2067
2294
  const name = t(descriptor.displayName);
2068
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: descriptor.instanceId ? `${name} · ${descriptor.instanceId}` : name });
2295
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2296
+ name + " ",
2297
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: providerId })
2298
+ ] });
2069
2299
  };
2070
- const handle = {
2300
+ const handle$1 = {
2071
2301
  breadcrumb: ({ params }) => /* @__PURE__ */ jsxRuntime.jsx(IntegrationBreadcrumb, { params })
2072
2302
  };
2303
+ const PUBLISH_URL = "https://docs.gorgojs.com/integrations";
2304
+ const API_URL = "https://gorgojs.com";
2305
+ const InstallModal = ({ item, onOpenChange }) => {
2306
+ const { t } = reactI18next.useTranslation();
2307
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: !!item, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
2308
+ /* @__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: [
2309
+ item && /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: API_URL + item.icon, alt: item.label, size: "small" }),
2310
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: item == null ? void 0 : item.label })
2311
+ ] }) }) }),
2312
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { className: "flex flex-1 flex-col gap-y-4 overflow-auto", children: item && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2313
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.install_addToConfig") }),
2314
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle flex items-center justify-between gap-x-2 rounded-md px-3 py-2", children: [
2315
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "txt-compact-small text-ui-fg-subtle", children: `npm i ${item.npm}` }),
2316
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: `npm i ${item.npm}` })
2317
+ ] }),
2318
+ item.configSnippet && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-ui-bg-subtle flex flex-col gap-y-2 rounded-md px-3 py-2", children: [
2319
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-2", children: [
2320
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", weight: "plus", className: "text-ui-fg-subtle", children: "medusa-config.ts" }),
2321
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Copy, { content: item.configSnippet })
2322
+ ] }),
2323
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "txt-compact-small text-ui-fg-subtle overflow-x-auto whitespace-pre", children: item.configSnippet })
2324
+ ] }),
2325
+ /* @__PURE__ */ jsxRuntime.jsxs("a", { href: item.docsUrl, target: "_blank", rel: "noreferrer", className: "text-ui-fg-base txt-small", children: [
2326
+ t("integration.browse.install_docs"),
2327
+ " →"
2328
+ ] })
2329
+ ] }) }),
2330
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", children: t("integration.actions.cancel") }) }) })
2331
+ ] }) });
2332
+ };
2333
+ const BrowsePage = () => {
2334
+ const { t } = reactI18next.useTranslation();
2335
+ const navigate = reactRouterDom.useNavigate();
2336
+ const [activeTab, setActiveTab] = React.useState("all");
2337
+ const [search, setSearch] = React.useState("");
2338
+ const [installItem, setInstallItem] = React.useState(null);
2339
+ const { data, isLoading } = reactQuery.useQuery({
2340
+ queryKey: ["integration-catalog"],
2341
+ queryFn: () => sdk.client.fetch("/admin/integrations/catalog")
2342
+ });
2343
+ const items = (data == null ? void 0 : data.integrations) ?? [];
2344
+ const categories = React.useMemo(() => [...new Set(items.map((i) => i.category))], [items]);
2345
+ const q = search.trim().toLowerCase();
2346
+ const filtered = items.filter((i) => {
2347
+ if (activeTab !== "all" && i.category !== activeTab) return false;
2348
+ if (q && ![i.label, i.shortDescription, i.npm].some((s) => s.toLowerCase().includes(q))) return false;
2349
+ return true;
2350
+ });
2351
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3", children: [
2352
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
2353
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between px-6 py-4", children: [
2354
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
2355
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: t("integration.browse.title") }),
2356
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t("integration.browse.subtitle") })
2357
+ ] }),
2358
+ false
2359
+ ] }),
2360
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 px-6 py-4", children: [
2361
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs, { value: activeTab, onValueChange: setActiveTab, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
2362
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "all", children: t("integration.list.tabs.all") }),
2363
+ categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: c, children: t(`integration.categories.${c}`) }, c))
2364
+ ] }) }),
2365
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-64", children: /* @__PURE__ */ jsxRuntime.jsx(
2366
+ ui.Input,
2367
+ {
2368
+ placeholder: t("integration.browse.searchPlaceholder"),
2369
+ value: search,
2370
+ onChange: (e) => setSearch(e.target.value)
2371
+ }
2372
+ ) })
2373
+ ] })
2374
+ ] }),
2375
+ 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: [
2376
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-y-3 p-4", children: [
2377
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-x-2", children: [
2378
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
2379
+ /* @__PURE__ */ jsxRuntime.jsx(IntegrationIcon, { src: API_URL + item.icon, alt: item.label }),
2380
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
2381
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: item.label }),
2382
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", leading: "compact", className: "text-ui-fg-subtle", children: t("integration.browse.by", { author: item.authorLocalized }) })
2383
+ ] })
2384
+ ] }),
2385
+ /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: item.installed ? "green" : "grey", children: item.installed ? t("integration.browse.installed") : t("integration.browse.notInstalled") })
2386
+ ] }),
2387
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle line-clamp-2", children: item.shortDescription })
2388
+ ] }),
2389
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
2390
+ item.installed && item.provider_id ? /* @__PURE__ */ jsxRuntime.jsx(
2391
+ ui.Button,
2392
+ {
2393
+ size: "small",
2394
+ variant: "primary",
2395
+ onClick: () => navigate(`/settings/integrations/${item.provider_id}`),
2396
+ children: t("integration.browse.settings")
2397
+ }
2398
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "small", variant: "secondary", onClick: () => setInstallItem(item), children: [
2399
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
2400
+ t("integration.browse.add")
2401
+ ] }),
2402
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: t(`integration.categories.${item.category}`) })
2403
+ ] })
2404
+ ] }, item.integrationId)) }),
2405
+ /* @__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: [
2406
+ t("integration.browse.learnMore"),
2407
+ " →"
2408
+ ] }) }),
2409
+ /* @__PURE__ */ jsxRuntime.jsx(InstallModal, { item: installItem, onOpenChange: (open) => !open && setInstallItem(null) })
2410
+ ] });
2411
+ };
2412
+ const BrowseBreadcrumb = () => {
2413
+ const { t } = reactI18next.useTranslation();
2414
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("integration.browse.title") });
2415
+ };
2416
+ const handle = {
2417
+ breadcrumb: () => /* @__PURE__ */ jsxRuntime.jsx(BrowseBreadcrumb, {})
2418
+ };
2073
2419
  const integration$1 = {
2074
2420
  list: {
2075
2421
  title: "Integrations",
2422
+ subtitle: "Manage integrations settings",
2423
+ browseIntegrations: "Browse Integrations",
2424
+ learnMore: "Learn how to build integrations",
2425
+ searchPlaceholder: "Search integrations",
2076
2426
  empty: "No integrations registered. Declare providers under the integration plugin's options.providers in medusa-config.",
2427
+ emptyFiltered: "No integrations match your filters.",
2077
2428
  tested: "tested",
2078
- testFailed: "test failed"
2429
+ testFailed: "test failed",
2430
+ tabs: {
2431
+ all: "All"
2432
+ }
2433
+ },
2434
+ browse: {
2435
+ title: "Browse",
2436
+ subtitle: "Find available integrations",
2437
+ publish: "Add Integration",
2438
+ learnMore: "Learn how to build and publish your own integration",
2439
+ searchPlaceholder: "Search integrations",
2440
+ by: "by {{author}}",
2441
+ installed: "Installed",
2442
+ notInstalled: "Not installed",
2443
+ add: "Add",
2444
+ settings: "Settings",
2445
+ install_addToConfig: "Add this to your medusa-config.ts and restart the server.",
2446
+ install_docs: "Documentation"
2447
+ },
2448
+ columns: {
2449
+ integration: "Integration",
2450
+ description: "Description",
2451
+ category: "Category",
2452
+ status: "Status"
2079
2453
  },
2080
2454
  status: {
2081
2455
  notConfigured: "Not configured",
2082
2456
  incomplete: "Incomplete",
2083
2457
  enabled: "Enabled",
2084
2458
  disabled: "Disabled",
2085
- configured: "Configured"
2459
+ configured: "Configured",
2460
+ connected: "Connected",
2461
+ connectionFailed: "Connection failed"
2462
+ },
2463
+ meta: {
2464
+ version: "v{{version}}",
2465
+ authorPrefix: "by"
2086
2466
  },
2087
2467
  labels: {
2088
- status: "Status",
2089
- category: "Category",
2090
- lastTest: "Last test",
2091
- neverTested: "Never tested"
2468
+ category: "Category"
2092
2469
  },
2093
2470
  categories: {
2094
2471
  payment: "Payment",
2472
+ notification: "Notification",
2095
2473
  fulfillment: "Fulfillment",
2096
- marketplace: "Marketplace",
2097
- crm: "CRM",
2474
+ search: "Search",
2475
+ content: "Content",
2476
+ analytics: "Analytics",
2477
+ authentication: "Authentication",
2098
2478
  erp: "ERP",
2479
+ crm: "CRM",
2099
2480
  pim: "PIM",
2100
- notification: "Notification",
2481
+ marketplace: "Marketplace",
2101
2482
  feed: "Feed",
2102
- tax: "Tax",
2483
+ file_providers: "File providers",
2103
2484
  other: "Other"
2104
2485
  },
2105
- testStatus: {
2106
- passed: "Passed",
2107
- failed: "Failed",
2108
- skipped: "Skipped"
2109
- },
2110
2486
  actions: {
2111
2487
  test: "Test connection",
2112
2488
  enable: "Enable",
@@ -2146,40 +2522,70 @@ const en = {
2146
2522
  const integration = {
2147
2523
  list: {
2148
2524
  title: "Интеграции",
2525
+ subtitle: "Управление настройками интеграций",
2526
+ browseIntegrations: "Найти интеграции",
2527
+ learnMore: "Как создавать интеграции",
2528
+ searchPlaceholder: "Поиск интеграций",
2149
2529
  empty: "Нет зарегистрированных интеграций. Объявите провайдеров в options.providers плагина интеграций в medusa-config.",
2530
+ emptyFiltered: "Нет интеграций по заданным фильтрам",
2150
2531
  tested: "проверено",
2151
- testFailed: "ошибка проверки"
2532
+ testFailed: "ошибка проверки",
2533
+ tabs: {
2534
+ all: "Все"
2535
+ }
2536
+ },
2537
+ browse: {
2538
+ title: "Каталог",
2539
+ subtitle: "Поиск доступных интеграций",
2540
+ publish: "Добавить интеграцию",
2541
+ learnMore: "Как создать и опубликовать свою интеграцию",
2542
+ searchPlaceholder: "Поиск интеграций",
2543
+ by: "от {{author}}",
2544
+ installed: "Установлено",
2545
+ notInstalled: "Не установлено",
2546
+ add: "Добавить",
2547
+ settings: "Настройки",
2548
+ install_addToConfig: "Добавьте это в medusa-config.ts и перезапустите сервер.",
2549
+ install_docs: "Документация"
2550
+ },
2551
+ columns: {
2552
+ integration: "Интеграция",
2553
+ description: "Описание",
2554
+ category: "Категория",
2555
+ status: "Статус"
2152
2556
  },
2153
2557
  status: {
2154
2558
  notConfigured: "Не настроено",
2155
2559
  incomplete: "Не завершено",
2156
2560
  enabled: "Включено",
2157
2561
  disabled: "Выключено",
2158
- configured: "Настроено"
2562
+ configured: "Настроено",
2563
+ connected: "Подключено",
2564
+ connectionFailed: "Ошибка подключения"
2565
+ },
2566
+ meta: {
2567
+ version: "v{{version}}",
2568
+ authorPrefix: "от"
2159
2569
  },
2160
2570
  labels: {
2161
- status: "Статус",
2162
- category: "Категория",
2163
- lastTest: "Последняя проверка",
2164
- neverTested: "Не проверялось"
2571
+ category: "Категория"
2165
2572
  },
2166
2573
  categories: {
2167
2574
  payment: "Платежи",
2575
+ notification: "Уведомления",
2168
2576
  fulfillment: "Доставка",
2169
- marketplace: "Маркетплейс",
2170
- crm: "CRM",
2577
+ search: "Поиск",
2578
+ content: "Контент",
2579
+ analytics: "Аналитика",
2580
+ authentication: "Аутентификация",
2171
2581
  erp: "ERP",
2582
+ crm: "CRM",
2172
2583
  pim: "PIM",
2173
- notification: "Уведомления",
2584
+ marketplace: "Маркетплейс",
2174
2585
  feed: "Фиды",
2175
- tax: "Налоги",
2586
+ file_providers: "Файловые провайдеры",
2176
2587
  other: "Прочее"
2177
2588
  },
2178
- testStatus: {
2179
- passed: "Пройдено",
2180
- failed: "Ошибка",
2181
- skipped: "Пропущено"
2182
- },
2183
2589
  actions: {
2184
2590
  test: "Проверить соединение",
2185
2591
  enable: "Включить",
@@ -2226,11 +2632,16 @@ const routeModule = {
2226
2632
  {
2227
2633
  Component: IntegrationsPage,
2228
2634
  path: "/settings/integrations",
2229
- handle: handle$1
2635
+ handle: { label: config.label, translationNs: config.translationNs, ...handle$2 }
2230
2636
  },
2231
2637
  {
2232
2638
  Component: EditPage,
2233
2639
  path: "/settings/integrations/:provider_id",
2640
+ handle: handle$1
2641
+ },
2642
+ {
2643
+ Component: BrowsePage,
2644
+ path: "/settings/integrations/browse",
2234
2645
  handle
2235
2646
  }
2236
2647
  ]
@@ -2252,6 +2663,7 @@ const displayModule = {
2252
2663
  displays: {}
2253
2664
  };
2254
2665
  const i18nModule = { resources: i18nTranslations0 };
2666
+ const cellRendererModule = {};
2255
2667
  const layoutModule = { layouts: [] };
2256
2668
  const plugin = {
2257
2669
  widgetModule,
@@ -2260,6 +2672,7 @@ const plugin = {
2260
2672
  formModule,
2261
2673
  displayModule,
2262
2674
  i18nModule,
2675
+ cellRendererModule,
2263
2676
  layoutModule
2264
2677
  };
2265
2678
  module.exports = plugin;