@open-mercato/core 0.4.6-develop-c2b70de148 → 0.4.6-develop-03a1e82e85

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 (30) hide show
  1. package/dist/modules/auth/frontend/login.js +13 -2
  2. package/dist/modules/auth/frontend/login.js.map +2 -2
  3. package/dist/modules/catalog/components/products/ProductCategorizeSection.js +1 -0
  4. package/dist/modules/catalog/components/products/ProductCategorizeSection.js.map +2 -2
  5. package/dist/modules/data_sync/lib/sync-engine.js +8 -2
  6. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  7. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +1 -1
  8. package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +1 -1
  9. package/dist/modules/feature_toggles/components/formConfig.js +1 -0
  10. package/dist/modules/feature_toggles/components/formConfig.js.map +2 -2
  11. package/dist/modules/feature_toggles/components/overrideFormConfig.js +1 -0
  12. package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +2 -2
  13. package/dist/modules/integrations/backend/integrations/filters.js +40 -0
  14. package/dist/modules/integrations/backend/integrations/filters.js.map +7 -0
  15. package/dist/modules/integrations/backend/integrations/page.js +34 -22
  16. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  17. package/package.json +2 -2
  18. package/src/modules/auth/frontend/login-injection.ts +2 -0
  19. package/src/modules/auth/frontend/login.tsx +15 -2
  20. package/src/modules/catalog/components/products/ProductCategorizeSection.tsx +1 -0
  21. package/src/modules/data_sync/lib/sync-engine.ts +8 -2
  22. package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +1 -1
  23. package/src/modules/feature_toggles/components/formConfig.tsx +1 -0
  24. package/src/modules/feature_toggles/components/overrideFormConfig.tsx +1 -0
  25. package/src/modules/integrations/backend/integrations/filters.ts +39 -0
  26. package/src/modules/integrations/backend/integrations/page.tsx +51 -37
  27. package/src/modules/integrations/i18n/de.json +88 -11
  28. package/src/modules/integrations/i18n/en.json +79 -2
  29. package/src/modules/integrations/i18n/es.json +96 -19
  30. package/src/modules/integrations/i18n/pl.json +105 -28
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/feature_toggles/components/overrideFormConfig.tsx"],
4
- "sourcesContent": ["import { CrudFormGroup, CrudCustomFieldRenderProps, CrudField } from \"@open-mercato/ui/backend/CrudForm\";\nimport { JsonBuilder } from \"@open-mercato/ui/backend/JsonBuilder\";\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport function renderOverrideValueComponent(props: CrudCustomFieldRenderProps) {\n const t = useT()\n const toggleType = props.values?.toggleType as string;\n const isOverride = props.values?.isOverride as boolean;\n\n if (!isOverride) {\n return (\n <div className=\"text-sm text-muted-foreground p-4 text-center bg-muted/20 rounded border border-dashed\">\n {t('feature_toggles.override.disabled', 'Override is disabled. Values will be inherited from the default configuration.')}\n </div>\n );\n }\n\n switch (toggleType) {\n case 'boolean':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.boolean.label', 'Override Value (Boolean)')}</label>\n <select\n value={props.value as string || 'false'}\n onChange={(e) => props.setValue(e.target.value === 'true')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n disabled={props.disabled}\n >\n <option value=\"true\">{t('feature_toggles.values.true', 'True')}</option>\n <option value=\"false\">{t('feature_toggles.values.false', 'False')}</option>\n </select>\n </div>\n );\n\n case 'string':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.string.label', 'Override Value (String)')}</label>\n <input\n type=\"text\"\n value={props.value as string || ''}\n onChange={(e) => props.setValue(e.target.value)}\n placeholder={t('feature_toggles.override.fields.value.string.placeholder', 'Enter override string value')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n disabled={props.disabled}\n autoFocus={props.autoFocus}\n />\n </div>\n );\n\n case 'number':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.number.label', 'Override Value (Number)')}</label>\n <input\n type=\"number\"\n value={props.value as number || 0}\n onChange={(e) => props.setValue(Number(e.target.value) || 0)}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n disabled={props.disabled}\n autoFocus={props.autoFocus}\n />\n </div>\n );\n\n case 'json':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.json.label', 'Override Value (JSON)')}</label>\n <JsonBuilder\n value={props.value}\n onChange={props.setValue}\n disabled={props.disabled}\n />\n </div>\n );\n\n default:\n return (\n <div className=\"text-sm text-muted-foreground p-4 text-center bg-muted/20 rounded border border-dashed\">\n {t('feature_toggles.override.unknownType', 'Unknown toggle type. Cannot configure override value.')}\n </div>\n );\n }\n}\n\nexport function createOverrideFieldDefinitions(\n t: (key: string) => string,\n): CrudField[] {\n return [\n {\n id: 'isOverride',\n label: t('feature_toggles.override.fields.isOverride.label'),\n type: 'checkbox',\n required: false,\n description: t('feature_toggles.override.fields.isOverride.description'),\n },\n {\n id: 'overrideValue',\n label: '',\n type: 'custom',\n component: renderOverrideValueComponent,\n description: t('feature_toggles.override.fields.overrideValue.description'),\n },\n ]\n}\n\nexport function createOverrideFormGroups(\n t: (key: string) => string,\n): CrudFormGroup[] {\n return [\n {\n id: 'overrideMode',\n title: t('feature_toggles.override.groups.mode'),\n column: 1,\n fields: ['isOverride'],\n },\n {\n id: 'overrideValue',\n title: t('feature_toggles.override.groups.value'),\n column: 1,\n fields: ['overrideValue'],\n },\n ]\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport { CrudFormGroup, CrudCustomFieldRenderProps, CrudField } from \"@open-mercato/ui/backend/CrudForm\";\nimport { JsonBuilder } from \"@open-mercato/ui/backend/JsonBuilder\";\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\nexport function renderOverrideValueComponent(props: CrudCustomFieldRenderProps) {\n const t = useT()\n const toggleType = props.values?.toggleType as string;\n const isOverride = props.values?.isOverride as boolean;\n\n if (!isOverride) {\n return (\n <div className=\"text-sm text-muted-foreground p-4 text-center bg-muted/20 rounded border border-dashed\">\n {t('feature_toggles.override.disabled', 'Override is disabled. Values will be inherited from the default configuration.')}\n </div>\n );\n }\n\n switch (toggleType) {\n case 'boolean':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.boolean.label', 'Override Value (Boolean)')}</label>\n <select\n value={props.value as string || 'false'}\n onChange={(e) => props.setValue(e.target.value === 'true')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n disabled={props.disabled}\n >\n <option value=\"true\">{t('feature_toggles.values.true', 'True')}</option>\n <option value=\"false\">{t('feature_toggles.values.false', 'False')}</option>\n </select>\n </div>\n );\n\n case 'string':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.string.label', 'Override Value (String)')}</label>\n <input\n type=\"text\"\n value={props.value as string || ''}\n onChange={(e) => props.setValue(e.target.value)}\n placeholder={t('feature_toggles.override.fields.value.string.placeholder', 'Enter override string value')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n disabled={props.disabled}\n autoFocus={props.autoFocus}\n />\n </div>\n );\n\n case 'number':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.number.label', 'Override Value (Number)')}</label>\n <input\n type=\"number\"\n value={props.value as number || 0}\n onChange={(e) => props.setValue(Number(e.target.value) || 0)}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n disabled={props.disabled}\n autoFocus={props.autoFocus}\n />\n </div>\n );\n\n case 'json':\n return (\n <div>\n <label className=\"block text-sm font-medium mb-2\">{t('feature_toggles.override.fields.value.json.label', 'Override Value (JSON)')}</label>\n <JsonBuilder\n value={props.value}\n onChange={props.setValue}\n disabled={props.disabled}\n />\n </div>\n );\n\n default:\n return (\n <div className=\"text-sm text-muted-foreground p-4 text-center bg-muted/20 rounded border border-dashed\">\n {t('feature_toggles.override.unknownType', 'Unknown toggle type. Cannot configure override value.')}\n </div>\n );\n }\n}\n\nexport function createOverrideFieldDefinitions(\n t: (key: string) => string,\n): CrudField[] {\n return [\n {\n id: 'isOverride',\n label: t('feature_toggles.override.fields.isOverride.label'),\n type: 'checkbox',\n required: false,\n description: t('feature_toggles.override.fields.isOverride.description'),\n },\n {\n id: 'overrideValue',\n label: '',\n type: 'custom',\n component: renderOverrideValueComponent,\n description: t('feature_toggles.override.fields.overrideValue.description'),\n },\n ]\n}\n\nexport function createOverrideFormGroups(\n t: (key: string) => string,\n): CrudFormGroup[] {\n return [\n {\n id: 'overrideMode',\n title: t('feature_toggles.override.groups.mode'),\n column: 1,\n fields: ['isOverride'],\n },\n {\n id: 'overrideValue',\n title: t('feature_toggles.override.groups.value'),\n column: 1,\n fields: ['overrideValue'],\n },\n ]\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,40 @@
1
+ const INTEGRATION_MARKETPLACE_CATEGORIES = [
2
+ "all",
3
+ "payment",
4
+ "shipping",
5
+ "data_sync",
6
+ "communication",
7
+ "notification",
8
+ "storage",
9
+ "webhook"
10
+ ];
11
+ function buildIntegrationMarketplaceFilterDefs(t) {
12
+ return [
13
+ {
14
+ id: "category",
15
+ label: t("integrations.marketplace.filters.category", "Category"),
16
+ type: "select",
17
+ options: INTEGRATION_MARKETPLACE_CATEGORIES.map((category) => ({
18
+ value: category,
19
+ label: t(`integrations.marketplace.categories.${category}`)
20
+ })),
21
+ formatValue: (value) => t(`integrations.marketplace.categories.${value}`, value)
22
+ }
23
+ ];
24
+ }
25
+ function normalizeIntegrationMarketplaceFilterValues(values) {
26
+ const category = typeof values.category === "string" ? values.category : "";
27
+ if (!category || category === "all") return {};
28
+ return { category };
29
+ }
30
+ function getIntegrationMarketplaceCategory(values) {
31
+ const category = typeof values.category === "string" ? values.category : "";
32
+ return category || "all";
33
+ }
34
+ export {
35
+ INTEGRATION_MARKETPLACE_CATEGORIES,
36
+ buildIntegrationMarketplaceFilterDefs,
37
+ getIntegrationMarketplaceCategory,
38
+ normalizeIntegrationMarketplaceFilterValues
39
+ };
40
+ //# sourceMappingURL=filters.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/integrations/backend/integrations/filters.ts"],
4
+ "sourcesContent": ["import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\n\nexport const INTEGRATION_MARKETPLACE_CATEGORIES = [\n 'all',\n 'payment',\n 'shipping',\n 'data_sync',\n 'communication',\n 'notification',\n 'storage',\n 'webhook',\n] as const\n\nexport function buildIntegrationMarketplaceFilterDefs(t: TranslateFn): FilterDef[] {\n return [\n {\n id: 'category',\n label: t('integrations.marketplace.filters.category', 'Category'),\n type: 'select',\n options: INTEGRATION_MARKETPLACE_CATEGORIES.map((category) => ({\n value: category,\n label: t(`integrations.marketplace.categories.${category}`),\n })),\n formatValue: (value) => t(`integrations.marketplace.categories.${value}`, value),\n },\n ]\n}\n\nexport function normalizeIntegrationMarketplaceFilterValues(values: FilterValues): FilterValues {\n const category = typeof values.category === 'string' ? values.category : ''\n if (!category || category === 'all') return {}\n return { category }\n}\n\nexport function getIntegrationMarketplaceCategory(values: FilterValues): string {\n const category = typeof values.category === 'string' ? values.category : ''\n return category || 'all'\n}\n"],
5
+ "mappings": "AAGO,MAAM,qCAAqC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,sCAAsC,GAA6B;AACjF,SAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,EAAE,6CAA6C,UAAU;AAAA,MAChE,MAAM;AAAA,MACN,SAAS,mCAAmC,IAAI,CAAC,cAAc;AAAA,QAC7D,OAAO;AAAA,QACP,OAAO,EAAE,uCAAuC,QAAQ,EAAE;AAAA,MAC5D,EAAE;AAAA,MACF,aAAa,CAAC,UAAU,EAAE,uCAAuC,KAAK,IAAI,KAAK;AAAA,IACjF;AAAA,EACF;AACF;AAEO,SAAS,4CAA4C,QAAoC;AAC9F,QAAM,WAAW,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;AACzE,MAAI,CAAC,YAAY,aAAa,MAAO,QAAO,CAAC;AAC7C,SAAO,EAAE,SAAS;AACpB;AAEO,SAAS,kCAAkC,QAA8B;AAC9E,QAAM,WAAW,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;AACzE,SAAO,YAAY;AACrB;",
6
+ "names": []
7
+ }
@@ -13,16 +13,14 @@ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
13
13
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
14
14
  import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
15
15
  import { useT } from "@open-mercato/shared/lib/i18n/context";
16
+ import { FilterBar } from "@open-mercato/ui/backend/FilterBar";
17
+ import { Bell, CreditCard, HardDrive, LayoutGrid, MessageSquare, RefreshCw, Truck, Webhook } from "lucide-react";
16
18
  import {
17
- LayoutGrid,
18
- CreditCard,
19
- Truck,
20
- RefreshCw,
21
- MessageSquare,
22
- Bell,
23
- HardDrive,
24
- Webhook
25
- } from "lucide-react";
19
+ buildIntegrationMarketplaceFilterDefs,
20
+ getIntegrationMarketplaceCategory,
21
+ INTEGRATION_MARKETPLACE_CATEGORIES,
22
+ normalizeIntegrationMarketplaceFilterValues
23
+ } from "./filters.js";
26
24
  const CATEGORY_ICONS = {
27
25
  all: LayoutGrid,
28
26
  payment: CreditCard,
@@ -33,7 +31,6 @@ const CATEGORY_ICONS = {
33
31
  storage: HardDrive,
34
32
  webhook: Webhook
35
33
  };
36
- const CATEGORIES = ["all", "payment", "shipping", "data_sync", "communication", "notification", "storage", "webhook"];
37
34
  function categoryBadgeVariant(category) {
38
35
  if (!category) return "outline";
39
36
  return "secondary";
@@ -42,10 +39,12 @@ function IntegrationsMarketplacePage() {
42
39
  const [data, setData] = React.useState(null);
43
40
  const [isLoading, setIsLoading] = React.useState(true);
44
41
  const [search, setSearch] = React.useState("");
45
- const [category, setCategory] = React.useState("all");
42
+ const [filterValues, setFilterValues] = React.useState({});
46
43
  const [togglingIds, setTogglingIds] = React.useState(/* @__PURE__ */ new Set());
47
44
  const scopeVersion = useOrganizationScopeVersion();
48
45
  const t = useT();
46
+ const categoryFilters = React.useMemo(() => buildIntegrationMarketplaceFilterDefs(t), [t]);
47
+ const selectedCategory = React.useMemo(() => getIntegrationMarketplaceCategory(filterValues), [filterValues]);
49
48
  const load = React.useCallback(async () => {
50
49
  setIsLoading(true);
51
50
  const fallback = { items: [], bundles: [] };
@@ -94,8 +93,8 @@ function IntegrationsMarketplacePage() {
94
93
  const q = search.toLowerCase();
95
94
  items = items.filter((item) => item.title.toLowerCase().includes(q) || item.description?.toLowerCase().includes(q));
96
95
  }
97
- if (category !== "all") {
98
- items = items.filter((item) => item.category === category);
96
+ if (selectedCategory !== "all") {
97
+ items = items.filter((item) => item.category === selectedCategory);
99
98
  }
100
99
  const bundled = /* @__PURE__ */ new Map();
101
100
  const standalone = [];
@@ -110,7 +109,7 @@ function IntegrationsMarketplacePage() {
110
109
  }
111
110
  const bundles = (data.bundles ?? []).filter((b) => bundled.has(b.id)).map((b) => ({ ...b, integrations: bundled.get(b.id) ?? [] }));
112
111
  return { bundles, standalone };
113
- }, [data, search, category]);
112
+ }, [data, search, selectedCategory]);
114
113
  if (isLoading) {
115
114
  return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-16", children: /* @__PURE__ */ jsx(Spinner, {}) }) }) });
116
115
  }
@@ -119,7 +118,20 @@ function IntegrationsMarketplacePage() {
119
118
  /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: t("integrations.marketplace.title") }),
120
119
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("integrations.marketplace.description") })
121
120
  ] }) }),
122
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center", children: [
121
+ /* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx(
122
+ FilterBar,
123
+ {
124
+ searchValue: search,
125
+ onSearchChange: setSearch,
126
+ searchPlaceholder: t("integrations.marketplace.search"),
127
+ searchAlign: "left",
128
+ filters: categoryFilters,
129
+ values: filterValues,
130
+ onApply: (values) => setFilterValues(normalizeIntegrationMarketplaceFilterValues(values)),
131
+ onClear: () => setFilterValues({})
132
+ }
133
+ ) }),
134
+ /* @__PURE__ */ jsx("div", { className: "hidden lg:flex lg:flex-col gap-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center", children: [
123
135
  /* @__PURE__ */ jsx(
124
136
  Input,
125
137
  {
@@ -129,24 +141,24 @@ function IntegrationsMarketplacePage() {
129
141
  className: "max-w-sm"
130
142
  }
131
143
  ),
132
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: CATEGORIES.map((cat) => {
133
- const Icon = CATEGORY_ICONS[cat];
144
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: INTEGRATION_MARKETPLACE_CATEGORIES.map((category) => {
145
+ const Icon = CATEGORY_ICONS[category];
134
146
  return /* @__PURE__ */ jsxs(
135
147
  Button,
136
148
  {
137
149
  type: "button",
138
- variant: category === cat ? "default" : "outline",
150
+ variant: selectedCategory === category ? "default" : "outline",
139
151
  size: "sm",
140
- onClick: () => setCategory(cat),
152
+ onClick: () => setFilterValues(normalizeIntegrationMarketplaceFilterValues({ category })),
141
153
  children: [
142
154
  Icon ? /* @__PURE__ */ jsx(Icon, { className: "mr-1.5 h-3.5 w-3.5" }) : null,
143
- t(`integrations.marketplace.categories.${cat}`)
155
+ t(`integrations.marketplace.categories.${category}`)
144
156
  ]
145
157
  },
146
- cat
158
+ category
147
159
  );
148
160
  }) })
149
- ] }),
161
+ ] }) }),
150
162
  filteredItems.bundles.map((bundle) => /* @__PURE__ */ jsxs(Card, { children: [
151
163
  /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
152
164
  /* @__PURE__ */ jsxs("div", { children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/integrations/backend/integrations/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Card, CardHeader, CardTitle, CardContent } from '@open-mercato/ui/primitives/card'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n LayoutGrid,\n CreditCard,\n Truck,\n RefreshCw,\n MessageSquare,\n Bell,\n HardDrive,\n Webhook,\n} from 'lucide-react'\n\ntype IntegrationItem = {\n id: string\n title: string\n description?: string\n category?: string\n icon?: string\n bundleId?: string\n isEnabled: boolean\n hasCredentials: boolean\n}\n\ntype BundleItem = {\n id: string\n title: string\n description?: string\n icon?: string\n integrationCount: number\n enabledCount: number\n}\n\ntype ListResponse = {\n items: IntegrationItem[]\n bundles: BundleItem[]\n}\n\nconst CATEGORY_ICONS: Record<string, React.ElementType> = {\n all: LayoutGrid,\n payment: CreditCard,\n shipping: Truck,\n data_sync: RefreshCw,\n communication: MessageSquare,\n notification: Bell,\n storage: HardDrive,\n webhook: Webhook,\n}\n\nconst CATEGORIES = ['all', 'payment', 'shipping', 'data_sync', 'communication', 'notification', 'storage', 'webhook'] as const\n\nfunction categoryBadgeVariant(category: string | undefined): 'default' | 'secondary' | 'outline' {\n if (!category) return 'outline'\n return 'secondary'\n}\n\nexport default function IntegrationsMarketplacePage() {\n const [data, setData] = React.useState<ListResponse | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [search, setSearch] = React.useState('')\n const [category, setCategory] = React.useState<string>('all')\n const [togglingIds, setTogglingIds] = React.useState<Set<string>>(new Set())\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n\n const load = React.useCallback(async () => {\n setIsLoading(true)\n const fallback: ListResponse = { items: [], bundles: [] }\n const call = await apiCall<ListResponse>('/api/integrations', undefined, { fallback })\n if (!call.ok) {\n flash(t('integrations.marketplace.loadError'), 'error')\n setIsLoading(false)\n return\n }\n setData(call.result ?? fallback)\n setIsLoading(false)\n }, [t])\n\n React.useEffect(() => { void load() }, [load, scopeVersion])\n\n const handleToggle = React.useCallback(async (integrationId: string, enabled: boolean) => {\n setTogglingIds((prev) => new Set(prev).add(integrationId))\n const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/state`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ isEnabled: enabled }),\n }, { fallback: null })\n\n if (!call.ok) {\n flash(t('integrations.detail.stateError'), 'error')\n } else {\n setData((prev) => {\n if (!prev) return prev\n return {\n ...prev,\n items: prev.items.map((item) =>\n item.id === integrationId ? { ...item, isEnabled: enabled } : item,\n ),\n }\n })\n }\n setTogglingIds((prev) => { const next = new Set(prev); next.delete(integrationId); return next })\n }, [t])\n\n const filteredItems = React.useMemo(() => {\n if (!data) return { bundles: [], standalone: [] }\n\n let items = data.items\n if (search) {\n const q = search.toLowerCase()\n items = items.filter((item) => item.title.toLowerCase().includes(q) || item.description?.toLowerCase().includes(q))\n }\n if (category !== 'all') {\n items = items.filter((item) => item.category === category)\n }\n\n const bundled = new Map<string, IntegrationItem[]>()\n const standalone: IntegrationItem[] = []\n for (const item of items) {\n if (item.bundleId) {\n const list = bundled.get(item.bundleId) ?? []\n list.push(item)\n bundled.set(item.bundleId, list)\n } else {\n standalone.push(item)\n }\n }\n\n const bundles = (data.bundles ?? [])\n .filter((b) => bundled.has(b.id))\n .map((b) => ({ ...b, integrations: bundled.get(b.id) ?? [] }))\n\n return { bundles, standalone }\n }, [data, search, category])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex items-center justify-center py-16\">\n <Spinner />\n </div>\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <section className=\"space-y-6 rounded-lg border bg-background p-6\">\n <header className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('integrations.marketplace.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('integrations.marketplace.description')}\n </p>\n </div>\n </header>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <Input\n placeholder={t('integrations.marketplace.search')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"max-w-sm\"\n />\n <div className=\"flex flex-wrap gap-1.5\">\n {CATEGORIES.map((cat) => {\n const Icon = CATEGORY_ICONS[cat]\n return (\n <Button\n key={cat}\n type=\"button\"\n variant={category === cat ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setCategory(cat)}\n >\n {Icon ? <Icon className=\"mr-1.5 h-3.5 w-3.5\" /> : null}\n {t(`integrations.marketplace.categories.${cat}`)}\n </Button>\n )\n })}\n </div>\n </div>\n\n {filteredItems.bundles.map((bundle) => (\n <Card key={bundle.id}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <div>\n <CardTitle>{bundle.title}</CardTitle>\n {bundle.description && (\n <p className=\"text-muted-foreground text-sm mt-1\">{bundle.description}</p>\n )}\n <p className=\"text-muted-foreground text-xs mt-1\">\n {t('integrations.marketplace.integrations', { count: bundle.integrations.length })}\n </p>\n </div>\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href={`/backend/integrations/bundle/${encodeURIComponent(bundle.id)}`}>\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {bundle.integrations.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"min-w-0\">\n <Link\n href={`/backend/integrations/${encodeURIComponent(item.id)}`}\n className=\"text-sm font-medium hover:underline\"\n >\n {item.title}\n </Link>\n {item.category && (\n <Badge variant={categoryBadgeVariant(item.category)} className=\"ml-2 text-xs\">\n {item.category}\n </Badge>\n )}\n </div>\n <Switch\n checked={item.isEnabled}\n disabled={togglingIds.has(item.id)}\n onCheckedChange={(checked) => void handleToggle(item.id, checked)}\n />\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n ))}\n\n {filteredItems.standalone.length > 0 && (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredItems.standalone.map((item) => (\n <Card key={item.id} className=\"flex flex-col\">\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{item.title}</CardTitle>\n <Switch\n checked={item.isEnabled}\n disabled={togglingIds.has(item.id)}\n onCheckedChange={(checked) => void handleToggle(item.id, checked)}\n />\n </div>\n {item.category && (\n <Badge variant={categoryBadgeVariant(item.category)} className=\"w-fit text-xs\">\n {item.category}\n </Badge>\n )}\n </CardHeader>\n <CardContent className=\"flex-1\">\n {item.description && (\n <p className=\"text-muted-foreground text-sm\">{item.description}</p>\n )}\n </CardContent>\n <div className=\"px-6 pb-4\">\n <Button asChild variant=\"outline\" size=\"sm\" className=\"w-full\">\n <Link href={`/backend/integrations/${encodeURIComponent(item.id)}`}>\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n </div>\n </Card>\n ))}\n </div>\n )}\n\n {filteredItems.bundles.length === 0 && filteredItems.standalone.length === 0 && (\n <div className=\"text-center py-12 text-muted-foreground\">\n {t('integrations.marketplace.noResults')}\n </div>\n )}\n </section>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Card, CardHeader, CardTitle, CardContent } from '@open-mercato/ui/primitives/card'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Switch } from '@open-mercato/ui/primitives/switch'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { FilterBar, type FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Bell, CreditCard, HardDrive, LayoutGrid, MessageSquare, RefreshCw, Truck, Webhook } from 'lucide-react'\nimport {\n buildIntegrationMarketplaceFilterDefs,\n getIntegrationMarketplaceCategory,\n INTEGRATION_MARKETPLACE_CATEGORIES,\n normalizeIntegrationMarketplaceFilterValues,\n} from './filters'\n\ntype IntegrationItem = {\n id: string\n title: string\n description?: string\n category?: string\n icon?: string\n bundleId?: string\n isEnabled: boolean\n hasCredentials: boolean\n}\n\ntype BundleItem = {\n id: string\n title: string\n description?: string\n icon?: string\n integrationCount: number\n enabledCount: number\n}\n\ntype ListResponse = {\n items: IntegrationItem[]\n bundles: BundleItem[]\n}\n\nconst CATEGORY_ICONS: Record<string, React.ElementType> = {\n all: LayoutGrid,\n payment: CreditCard,\n shipping: Truck,\n data_sync: RefreshCw,\n communication: MessageSquare,\n notification: Bell,\n storage: HardDrive,\n webhook: Webhook,\n}\n\nfunction categoryBadgeVariant(category: string | undefined): 'default' | 'secondary' | 'outline' {\n if (!category) return 'outline'\n return 'secondary'\n}\n\nexport default function IntegrationsMarketplacePage() {\n const [data, setData] = React.useState<ListResponse | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [togglingIds, setTogglingIds] = React.useState<Set<string>>(new Set())\n const scopeVersion = useOrganizationScopeVersion()\n const t = useT()\n\n const categoryFilters = React.useMemo(() => buildIntegrationMarketplaceFilterDefs(t), [t])\n const selectedCategory = React.useMemo(() => getIntegrationMarketplaceCategory(filterValues), [filterValues])\n\n const load = React.useCallback(async () => {\n setIsLoading(true)\n const fallback: ListResponse = { items: [], bundles: [] }\n const call = await apiCall<ListResponse>('/api/integrations', undefined, { fallback })\n if (!call.ok) {\n flash(t('integrations.marketplace.loadError'), 'error')\n setIsLoading(false)\n return\n }\n setData(call.result ?? fallback)\n setIsLoading(false)\n }, [t])\n\n React.useEffect(() => { void load() }, [load, scopeVersion])\n\n const handleToggle = React.useCallback(async (integrationId: string, enabled: boolean) => {\n setTogglingIds((prev) => new Set(prev).add(integrationId))\n const call = await apiCall(`/api/integrations/${encodeURIComponent(integrationId)}/state`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ isEnabled: enabled }),\n }, { fallback: null })\n\n if (!call.ok) {\n flash(t('integrations.detail.stateError'), 'error')\n } else {\n setData((prev) => {\n if (!prev) return prev\n return {\n ...prev,\n items: prev.items.map((item) =>\n item.id === integrationId ? { ...item, isEnabled: enabled } : item,\n ),\n }\n })\n }\n setTogglingIds((prev) => { const next = new Set(prev); next.delete(integrationId); return next })\n }, [t])\n\n const filteredItems = React.useMemo(() => {\n if (!data) return { bundles: [], standalone: [] }\n\n let items = data.items\n if (search) {\n const q = search.toLowerCase()\n items = items.filter((item) => item.title.toLowerCase().includes(q) || item.description?.toLowerCase().includes(q))\n }\n if (selectedCategory !== 'all') {\n items = items.filter((item) => item.category === selectedCategory)\n }\n\n const bundled = new Map<string, IntegrationItem[]>()\n const standalone: IntegrationItem[] = []\n for (const item of items) {\n if (item.bundleId) {\n const list = bundled.get(item.bundleId) ?? []\n list.push(item)\n bundled.set(item.bundleId, list)\n } else {\n standalone.push(item)\n }\n }\n\n const bundles = (data.bundles ?? [])\n .filter((b) => bundled.has(b.id))\n .map((b) => ({ ...b, integrations: bundled.get(b.id) ?? [] }))\n\n return { bundles, standalone }\n }, [data, search, selectedCategory])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex items-center justify-center py-16\">\n <Spinner />\n </div>\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <section className=\"space-y-6 rounded-lg border bg-background p-6\">\n <header className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('integrations.marketplace.title')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('integrations.marketplace.description')}\n </p>\n </div>\n </header>\n\n <div className=\"lg:hidden\">\n <FilterBar\n searchValue={search}\n onSearchChange={setSearch}\n searchPlaceholder={t('integrations.marketplace.search')}\n searchAlign=\"left\"\n filters={categoryFilters}\n values={filterValues}\n onApply={(values) => setFilterValues(normalizeIntegrationMarketplaceFilterValues(values))}\n onClear={() => setFilterValues({})}\n />\n </div>\n\n <div className=\"hidden lg:flex lg:flex-col gap-4\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <Input\n placeholder={t('integrations.marketplace.search')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"max-w-sm\"\n />\n <div className=\"flex flex-wrap gap-1.5\">\n {INTEGRATION_MARKETPLACE_CATEGORIES.map((category) => {\n const Icon = CATEGORY_ICONS[category]\n return (\n <Button\n key={category}\n type=\"button\"\n variant={selectedCategory === category ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setFilterValues(normalizeIntegrationMarketplaceFilterValues({ category }))}\n >\n {Icon ? <Icon className=\"mr-1.5 h-3.5 w-3.5\" /> : null}\n {t(`integrations.marketplace.categories.${category}`)}\n </Button>\n )\n })}\n </div>\n </div>\n </div>\n\n {filteredItems.bundles.map((bundle) => (\n <Card key={bundle.id}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <div>\n <CardTitle>{bundle.title}</CardTitle>\n {bundle.description && (\n <p className=\"text-muted-foreground text-sm mt-1\">{bundle.description}</p>\n )}\n <p className=\"text-muted-foreground text-xs mt-1\">\n {t('integrations.marketplace.integrations', { count: bundle.integrations.length })}\n </p>\n </div>\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href={`/backend/integrations/bundle/${encodeURIComponent(bundle.id)}`}>\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n {bundle.integrations.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"min-w-0\">\n <Link\n href={`/backend/integrations/${encodeURIComponent(item.id)}`}\n className=\"text-sm font-medium hover:underline\"\n >\n {item.title}\n </Link>\n {item.category && (\n <Badge variant={categoryBadgeVariant(item.category)} className=\"ml-2 text-xs\">\n {item.category}\n </Badge>\n )}\n </div>\n <Switch\n checked={item.isEnabled}\n disabled={togglingIds.has(item.id)}\n onCheckedChange={(checked) => void handleToggle(item.id, checked)}\n />\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n ))}\n\n {filteredItems.standalone.length > 0 && (\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {filteredItems.standalone.map((item) => (\n <Card key={item.id} className=\"flex flex-col\">\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{item.title}</CardTitle>\n <Switch\n checked={item.isEnabled}\n disabled={togglingIds.has(item.id)}\n onCheckedChange={(checked) => void handleToggle(item.id, checked)}\n />\n </div>\n {item.category && (\n <Badge variant={categoryBadgeVariant(item.category)} className=\"w-fit text-xs\">\n {item.category}\n </Badge>\n )}\n </CardHeader>\n <CardContent className=\"flex-1\">\n {item.description && (\n <p className=\"text-muted-foreground text-sm\">{item.description}</p>\n )}\n </CardContent>\n <div className=\"px-6 pb-4\">\n <Button asChild variant=\"outline\" size=\"sm\" className=\"w-full\">\n <Link href={`/backend/integrations/${encodeURIComponent(item.id)}`}>\n {t('integrations.marketplace.configure')}\n </Link>\n </Button>\n </div>\n </Card>\n ))}\n </div>\n )}\n\n {filteredItems.bundles.length === 0 && filteredItems.standalone.length === 0 && (\n <div className=\"text-center py-12 text-muted-foreground\">\n {t('integrations.marketplace.noResults')}\n </div>\n )}\n </section>\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.4.6-develop-c2b70de148",
3
+ "version": "0.4.6-develop-03a1e82e85",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -207,7 +207,7 @@
207
207
  }
208
208
  },
209
209
  "dependencies": {
210
- "@open-mercato/shared": "0.4.6-develop-c2b70de148",
210
+ "@open-mercato/shared": "0.4.6-develop-03a1e82e85",
211
211
  "@types/html-to-text": "^9.0.4",
212
212
  "@types/semver": "^7.5.8",
213
213
  "@xyflow/react": "^12.6.0",
@@ -40,6 +40,8 @@ export type LoginFormWidgetContext = {
40
40
  searchParams: URLSearchParams
41
41
  /** Set or clear an alternative auth provider for the current email */
42
42
  setAuthOverride: (override: AuthOverride | null) => void
43
+ /** Mark whether auth override detection is still resolving */
44
+ setAuthOverridePending?: (pending: boolean) => void
43
45
  /** Display an error message in the login form's error area */
44
46
  setError: (error: string | null) => void
45
47
  }
@@ -89,6 +89,8 @@ export default function LoginPage() {
89
89
  const [error, setError] = useState<string | null>(null)
90
90
  const [submitting, setSubmitting] = useState(false)
91
91
  const [authOverride, setAuthOverride] = useState<AuthOverride | null>(null)
92
+ const [authOverridePending, setAuthOverridePending] = useState(false)
93
+ const [clientReady, setClientReady] = useState(false)
92
94
  const [email, setEmail] = useState('')
93
95
  const [tenantId, setTenantId] = useState<string | null>(null)
94
96
  const [tenantName, setTenantName] = useState<string | null>(null)
@@ -96,6 +98,10 @@ export default function LoginPage() {
96
98
  const [tenantInvalid, setTenantInvalid] = useState<string | null>(null)
97
99
  const showTenantInvalid = tenantId != null && tenantInvalid === tenantId
98
100
 
101
+ useEffect(() => {
102
+ setClientReady(true)
103
+ }, [])
104
+
99
105
  useEffect(() => {
100
106
  const tenantParam = (searchParams.get('tenant') || '').trim()
101
107
  if (tenantParam) {
@@ -167,6 +173,9 @@ export default function LoginPage() {
167
173
 
168
174
  async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
169
175
  e.preventDefault()
176
+ if (!clientReady || authOverridePending) {
177
+ return
178
+ }
170
179
  setError(null)
171
180
  if (authOverride) {
172
181
  authOverride.onSubmit()
@@ -248,9 +257,12 @@ export default function LoginPage() {
248
257
  tenantId,
249
258
  searchParams,
250
259
  setAuthOverride,
260
+ setAuthOverridePending,
251
261
  setError,
252
262
  }), [email, tenantId, searchParams])
253
263
 
264
+ const formReady = clientReady && !authOverridePending
265
+
254
266
  return (
255
267
  <div className="min-h-svh flex items-center justify-center p-4">
256
268
  <Card className="w-full max-w-sm">
@@ -260,7 +272,7 @@ export default function LoginPage() {
260
272
  <CardDescription>{translate('auth.login.subtitle', 'Access your workspace')}</CardDescription>
261
273
  </CardHeader>
262
274
  <CardContent>
263
- <form className="grid gap-3" onSubmit={onSubmit} noValidate>
275
+ <form className="grid gap-3" onSubmit={onSubmit} noValidate data-auth-ready={formReady ? '1' : '0'}>
264
276
  {tenantId ? (
265
277
  <input type="hidden" name="tenantId" value={tenantId} />
266
278
  ) : null}
@@ -318,6 +330,7 @@ export default function LoginPage() {
318
330
  type="email"
319
331
  required
320
332
  aria-invalid={!!error}
333
+ onChange={(e) => setEmail(e.target.value)}
321
334
  onBlur={(e) => setEmail(e.target.value)}
322
335
  />
323
336
  </div>
@@ -337,7 +350,7 @@ export default function LoginPage() {
337
350
  <span>{translate('auth.login.rememberMe', 'Remember me')}</span>
338
351
  </label>
339
352
  )}
340
- <Button type="submit" disabled={submitting} className="h-10 mt-2">
353
+ <Button type="submit" disabled={submitting || !formReady} className="h-10 mt-2">
341
354
  {submitting
342
355
  ? translate('auth.login.loading', 'Loading...')
343
356
  : authOverride
@@ -1,3 +1,4 @@
1
+ "use client"
1
2
  import * as React from 'react'
2
3
  import { TagsInput } from '@open-mercato/ui/backend/inputs/TagsInput'
3
4
  import { Label } from '@open-mercato/ui/primitives/label'
@@ -171,7 +171,10 @@ export function createSyncEngine(deps: EngineDeps) {
171
171
  return {
172
172
  async runImport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {
173
173
  const run = await syncRunService.getRun(runId, scope)
174
- if (!run) throw new Error(`Sync run ${runId} not found`)
174
+ if (!run) {
175
+ console.warn(`[data-sync] Skipping stale import job for missing run ${runId}`)
176
+ return
177
+ }
175
178
 
176
179
  const providerKey = resolveProviderKey(run.integrationId)
177
180
  const adapter = getDataSyncAdapter(providerKey)
@@ -271,7 +274,10 @@ export function createSyncEngine(deps: EngineDeps) {
271
274
 
272
275
  async runExport(runId: string, batchSize: number, scope: SyncScope): Promise<void> {
273
276
  const run = await syncRunService.getRun(runId, scope)
274
- if (!run) throw new Error(`Sync run ${runId} not found`)
277
+ if (!run) {
278
+ console.warn(`[data-sync] Skipping stale export job for missing run ${runId}`)
279
+ return
280
+ }
275
281
 
276
282
  const providerKey = resolveProviderKey(run.integrationId)
277
283
  const adapter = getDataSyncAdapter(providerKey)
@@ -222,7 +222,7 @@ export function DictionaryEntrySelect({
222
222
  <div className="flex items-center gap-2">
223
223
  <select
224
224
  className={[
225
- 'h-9 w-full rounded border px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-70',
225
+ 'h-9 w-full rounded border pl-3 pr-8 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-70',
226
226
  selectClassName,
227
227
  ]
228
228
  .filter(Boolean)
@@ -1,3 +1,4 @@
1
+ "use client"
1
2
  import { CrudFormGroup, CrudCustomFieldRenderProps, CrudField } from "@open-mercato/ui/backend/CrudForm";
2
3
  import { JsonBuilder } from "@open-mercato/ui/backend/JsonBuilder";
3
4
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -1,3 +1,4 @@
1
+ "use client"
1
2
  import { CrudFormGroup, CrudCustomFieldRenderProps, CrudField } from "@open-mercato/ui/backend/CrudForm";
2
3
  import { JsonBuilder } from "@open-mercato/ui/backend/JsonBuilder";
3
4
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -0,0 +1,39 @@
1
+ import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
2
+ import type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'
3
+
4
+ export const INTEGRATION_MARKETPLACE_CATEGORIES = [
5
+ 'all',
6
+ 'payment',
7
+ 'shipping',
8
+ 'data_sync',
9
+ 'communication',
10
+ 'notification',
11
+ 'storage',
12
+ 'webhook',
13
+ ] as const
14
+
15
+ export function buildIntegrationMarketplaceFilterDefs(t: TranslateFn): FilterDef[] {
16
+ return [
17
+ {
18
+ id: 'category',
19
+ label: t('integrations.marketplace.filters.category', 'Category'),
20
+ type: 'select',
21
+ options: INTEGRATION_MARKETPLACE_CATEGORIES.map((category) => ({
22
+ value: category,
23
+ label: t(`integrations.marketplace.categories.${category}`),
24
+ })),
25
+ formatValue: (value) => t(`integrations.marketplace.categories.${value}`, value),
26
+ },
27
+ ]
28
+ }
29
+
30
+ export function normalizeIntegrationMarketplaceFilterValues(values: FilterValues): FilterValues {
31
+ const category = typeof values.category === 'string' ? values.category : ''
32
+ if (!category || category === 'all') return {}
33
+ return { category }
34
+ }
35
+
36
+ export function getIntegrationMarketplaceCategory(values: FilterValues): string {
37
+ const category = typeof values.category === 'string' ? values.category : ''
38
+ return category || 'all'
39
+ }