@open-mercato/core 0.4.7-develop-78d7541539 → 0.4.7-develop-74069040de
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/dist/modules/catalog/api/bulk-delete/route.js +86 -0
- package/dist/modules/catalog/api/bulk-delete/route.js.map +7 -0
- package/dist/modules/catalog/api/prices/route.js +39 -6
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/products/route.js +6 -11
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +2 -0
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +9 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/lib/bulkDelete.js +70 -0
- package/dist/modules/catalog/lib/bulkDelete.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js +185 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js.map +7 -0
- package/dist/modules/catalog/widgets/injection-table.js +9 -1
- package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js +40 -0
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js.map +7 -0
- package/dist/modules/data_sync/api/options.js +52 -0
- package/dist/modules/data_sync/api/options.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +30 -35
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +15 -30
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +109 -0
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/route.js +72 -0
- package/dist/modules/data_sync/api/schedules/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/serialize.js +21 -0
- package/dist/modules/data_sync/api/schedules/serialize.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +656 -47
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +116 -34
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +394 -0
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/di.js +2 -0
- package/dist/modules/data_sync/di.js.map +2 -2
- package/dist/modules/data_sync/lib/id-mapping.js +24 -2
- package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
- package/dist/modules/data_sync/lib/start-run.js +57 -0
- package/dist/modules/data_sync/lib/start-run.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +93 -4
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +5 -1
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-schedule-service.js +138 -0
- package/dist/modules/data_sync/lib/sync-schedule-service.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +28 -2
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +28 -2
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-scheduled.js +5 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +5 -2
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/lib/field-definitions.js +3 -1
- package/dist/modules/entities/lib/field-definitions.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +14 -15
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +3 -3
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +148 -33
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/lib/state-service.js +15 -1
- package/dist/modules/integrations/lib/state-service.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +24 -22
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/progress/api/active/route.js +3 -1
- package/dist/modules/progress/api/active/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +1 -1
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +1 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/lib/events.js.map +1 -1
- package/dist/modules/progress/lib/progressService.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +42 -1
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/lib/document.js +35 -1
- package/dist/modules/query_index/lib/document.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +91 -4
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +2 -0
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/sales/api/adjustment-kinds/route.js +3 -9
- package/dist/modules/sales/api/adjustment-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/channels/route.js +3 -10
- package/dist/modules/sales/api/channels/route.js.map +2 -2
- package/dist/modules/sales/api/delivery-windows/route.js +3 -10
- package/dist/modules/sales/api/delivery-windows/route.js.map +2 -2
- package/dist/modules/sales/api/payment-methods/route.js +3 -11
- package/dist/modules/sales/api/payment-methods/route.js.map +2 -2
- package/dist/modules/sales/api/price-kinds/route.js +3 -5
- package/dist/modules/sales/api/price-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/shipping-methods/route.js +3 -11
- package/dist/modules/sales/api/shipping-methods/route.js.map +2 -2
- package/dist/modules/sales/api/tags/route.js +3 -9
- package/dist/modules/sales/api/tags/route.js.map +2 -2
- package/dist/modules/sales/api/tax-rates/route.js +3 -13
- package/dist/modules/sales/api/tax-rates/route.js.map +2 -2
- package/dist/modules/sales/api/utils.js +9 -0
- package/dist/modules/sales/api/utils.js.map +2 -2
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +3 -9
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +3 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +4 -3
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/serialize.js +25 -0
- package/dist/modules/workflows/api/definitions/serialize.js.map +7 -0
- package/package.json +3 -3
- package/src/modules/catalog/api/bulk-delete/route.ts +93 -0
- package/src/modules/catalog/api/prices/route.ts +53 -6
- package/src/modules/catalog/api/products/route.ts +6 -11
- package/src/modules/catalog/commands/products.ts +2 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -0
- package/src/modules/catalog/i18n/de.json +10 -0
- package/src/modules/catalog/i18n/en.json +10 -0
- package/src/modules/catalog/i18n/es.json +10 -0
- package/src/modules/catalog/i18n/pl.json +10 -0
- package/src/modules/catalog/lib/bulkDelete.ts +106 -0
- package/src/modules/catalog/widgets/injection/product-bulk-delete/widget.ts +242 -0
- package/src/modules/catalog/widgets/injection-table.ts +8 -0
- package/src/modules/catalog/workers/catalog-product-bulk-delete.ts +48 -0
- package/src/modules/data_sync/AGENTS.md +11 -3
- package/src/modules/data_sync/api/options.ts +58 -0
- package/src/modules/data_sync/api/run.ts +34 -36
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +2 -2
- package/src/modules/data_sync/api/runs/[id]/retry.ts +14 -31
- package/src/modules/data_sync/api/schedules/[id]/route.ts +130 -0
- package/src/modules/data_sync/api/schedules/route.ts +77 -0
- package/src/modules/data_sync/api/schedules/serialize.ts +31 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +756 -2
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +179 -53
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +512 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +6 -0
- package/src/modules/data_sync/i18n/de.json +72 -0
- package/src/modules/data_sync/i18n/en.json +72 -0
- package/src/modules/data_sync/i18n/es.json +72 -0
- package/src/modules/data_sync/i18n/pl.json +72 -0
- package/src/modules/data_sync/lib/adapter.ts +4 -1
- package/src/modules/data_sync/lib/id-mapping.ts +32 -2
- package/src/modules/data_sync/lib/start-run.ts +90 -0
- package/src/modules/data_sync/lib/sync-engine.ts +111 -4
- package/src/modules/data_sync/lib/sync-run-service.ts +5 -1
- package/src/modules/data_sync/lib/sync-schedule-service.ts +207 -0
- package/src/modules/data_sync/workers/sync-export.ts +33 -2
- package/src/modules/data_sync/workers/sync-import.ts +33 -2
- package/src/modules/data_sync/workers/sync-scheduled.ts +7 -0
- package/src/modules/entities/api/definitions.ts +12 -2
- package/src/modules/entities/lib/field-definitions.ts +2 -0
- package/src/modules/integrations/AGENTS.md +16 -3
- package/src/modules/integrations/api/[id]/route.ts +14 -15
- package/src/modules/integrations/api/route.ts +3 -3
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +176 -54
- package/src/modules/integrations/lib/state-service.ts +25 -1
- package/src/modules/messages/api/[id]/route.ts +25 -22
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +3 -3
- package/src/modules/progress/api/active/route.ts +4 -1
- package/src/modules/progress/api/jobs/[id]/route.ts +1 -1
- package/src/modules/progress/api/jobs/route.ts +1 -1
- package/src/modules/progress/lib/events.ts +6 -0
- package/src/modules/progress/lib/progressService.ts +1 -0
- package/src/modules/progress/lib/progressServiceImpl.ts +47 -1
- package/src/modules/query_index/lib/document.ts +52 -1
- package/src/modules/query_index/lib/engine.ts +104 -4
- package/src/modules/query_index/lib/indexer.ts +2 -0
- package/src/modules/sales/api/adjustment-kinds/route.ts +3 -9
- package/src/modules/sales/api/channels/route.ts +3 -10
- package/src/modules/sales/api/delivery-windows/route.ts +3 -10
- package/src/modules/sales/api/payment-methods/route.ts +3 -11
- package/src/modules/sales/api/price-kinds/route.ts +3 -5
- package/src/modules/sales/api/shipping-methods/route.ts +3 -11
- package/src/modules/sales/api/tags/route.ts +3 -9
- package/src/modules/sales/api/tax-rates/route.ts +3 -13
- package/src/modules/sales/api/utils.ts +9 -0
- package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +3 -9
- package/src/modules/workflows/api/definitions/[id]/route.ts +3 -2
- package/src/modules/workflows/api/definitions/route.ts +4 -3
- package/src/modules/workflows/api/definitions/serialize.ts +23 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
5
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
6
|
+
import { Badge } from "@open-mercato/ui/primitives/badge";
|
|
7
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
8
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
9
|
+
import { Label } from "@open-mercato/ui/primitives/label";
|
|
10
|
+
import { Notice } from "@open-mercato/ui/primitives/Notice";
|
|
11
|
+
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
12
|
+
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
13
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
14
|
+
import {
|
|
15
|
+
CalendarClock,
|
|
16
|
+
Play,
|
|
17
|
+
RefreshCw,
|
|
18
|
+
Save,
|
|
19
|
+
ShieldAlert,
|
|
20
|
+
ShieldCheck,
|
|
21
|
+
Trash2
|
|
22
|
+
} from "lucide-react";
|
|
23
|
+
const DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
24
|
+
function formatEntityTypeLabel(entityType) {
|
|
25
|
+
return entityType.replace(/[_-]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
26
|
+
}
|
|
27
|
+
function buildDefaultScheduleState(entityType) {
|
|
28
|
+
const normalized = entityType.trim().toLowerCase();
|
|
29
|
+
const longerInterval = normalized === "categories" || normalized === "attributes";
|
|
30
|
+
return {
|
|
31
|
+
scheduleType: "interval",
|
|
32
|
+
scheduleValue: longerInterval ? "6h" : "1h",
|
|
33
|
+
timezone: DEFAULT_TIMEZONE,
|
|
34
|
+
fullSync: normalized !== "products",
|
|
35
|
+
isEnabled: true,
|
|
36
|
+
lastRunAt: null
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function getSupportedDirections(direction) {
|
|
40
|
+
if (direction === "export") return ["export"];
|
|
41
|
+
if (direction === "bidirectional") return ["import", "export"];
|
|
42
|
+
return ["import"];
|
|
43
|
+
}
|
|
44
|
+
function buildScheduleKey(entityType, direction) {
|
|
45
|
+
return `${entityType}:${direction}`;
|
|
46
|
+
}
|
|
47
|
+
function buildScheduleEditors(entityTypes, directions, records) {
|
|
48
|
+
const nextEntries = [];
|
|
49
|
+
for (const entityType of entityTypes) {
|
|
50
|
+
for (const direction of directions) {
|
|
51
|
+
const record = records.find((item) => item.entityType === entityType && item.direction === direction);
|
|
52
|
+
nextEntries.push([
|
|
53
|
+
buildScheduleKey(entityType, direction),
|
|
54
|
+
record ? {
|
|
55
|
+
id: record.id,
|
|
56
|
+
scheduleType: record.scheduleType,
|
|
57
|
+
scheduleValue: record.scheduleValue,
|
|
58
|
+
timezone: record.timezone,
|
|
59
|
+
fullSync: record.fullSync,
|
|
60
|
+
isEnabled: record.isEnabled,
|
|
61
|
+
lastRunAt: record.lastRunAt
|
|
62
|
+
} : buildDefaultScheduleState(entityType)
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return Object.fromEntries(nextEntries);
|
|
67
|
+
}
|
|
68
|
+
function IntegrationScheduleTab(props) {
|
|
69
|
+
const t = useT();
|
|
70
|
+
const scopeVersion = useOrganizationScopeVersion();
|
|
71
|
+
const [option, setOption] = React.useState(null);
|
|
72
|
+
const [schedules, setSchedules] = React.useState({});
|
|
73
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
74
|
+
const [runningKey, setRunningKey] = React.useState(null);
|
|
75
|
+
const [savingKey, setSavingKey] = React.useState(null);
|
|
76
|
+
const [deletingKey, setDeletingKey] = React.useState(null);
|
|
77
|
+
const load = React.useCallback(async () => {
|
|
78
|
+
setIsLoading(true);
|
|
79
|
+
try {
|
|
80
|
+
const [optionsCall, schedulesCall] = await Promise.all([
|
|
81
|
+
apiCall("/api/data_sync/options", void 0, { fallback: { items: [] } }),
|
|
82
|
+
apiCall(`/api/data_sync/schedules?integrationId=${encodeURIComponent(props.integrationId)}&page=1&pageSize=100`, void 0, { fallback: { items: [] } })
|
|
83
|
+
]);
|
|
84
|
+
const resolvedOption = (optionsCall.result?.items ?? []).find((item) => item.integrationId === props.integrationId) ?? null;
|
|
85
|
+
setOption(resolvedOption);
|
|
86
|
+
const supportedEntities = resolvedOption?.supportedEntities ?? [];
|
|
87
|
+
const supportedDirections2 = getSupportedDirections(resolvedOption?.direction);
|
|
88
|
+
setSchedules(buildScheduleEditors(supportedEntities, supportedDirections2, schedulesCall.result?.items ?? []));
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : t("data_sync.integrationTab.loadError", "Failed to load sync schedules.");
|
|
91
|
+
flash(message, "error");
|
|
92
|
+
} finally {
|
|
93
|
+
setIsLoading(false);
|
|
94
|
+
}
|
|
95
|
+
}, [props.integrationId, t]);
|
|
96
|
+
React.useEffect(() => {
|
|
97
|
+
void load();
|
|
98
|
+
}, [load, scopeVersion]);
|
|
99
|
+
const supportedDirections = React.useMemo(
|
|
100
|
+
() => getSupportedDirections(option?.direction),
|
|
101
|
+
[option?.direction]
|
|
102
|
+
);
|
|
103
|
+
const rows = React.useMemo(
|
|
104
|
+
() => (option?.supportedEntities ?? []).flatMap((entityType) => supportedDirections.map((direction) => ({
|
|
105
|
+
entityType,
|
|
106
|
+
direction,
|
|
107
|
+
key: buildScheduleKey(entityType, direction)
|
|
108
|
+
}))),
|
|
109
|
+
[option?.supportedEntities, supportedDirections]
|
|
110
|
+
);
|
|
111
|
+
const updateScheduleEditor = React.useCallback((key, patch, entityType) => {
|
|
112
|
+
setSchedules((current) => ({
|
|
113
|
+
...current,
|
|
114
|
+
[key]: {
|
|
115
|
+
...current[key] ?? buildDefaultScheduleState(entityType),
|
|
116
|
+
...patch
|
|
117
|
+
}
|
|
118
|
+
}));
|
|
119
|
+
}, []);
|
|
120
|
+
const handleStartSync = React.useCallback(async (entityType, direction, scheduleKey) => {
|
|
121
|
+
if (!props.isEnabled) {
|
|
122
|
+
flash(t("data_sync.integrationTab.integrationDisabled", "Enable the integration before starting a sync."), "error");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!props.hasCredentials) {
|
|
126
|
+
flash(t("data_sync.integrationTab.credentialsMissing", "Configure credentials before starting a sync."), "error");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
setRunningKey(scheduleKey);
|
|
130
|
+
try {
|
|
131
|
+
const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType);
|
|
132
|
+
const call = await apiCall("/api/data_sync/run", {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: { "content-type": "application/json" },
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
integrationId: props.integrationId,
|
|
137
|
+
entityType,
|
|
138
|
+
direction,
|
|
139
|
+
fullSync: scheduleState.fullSync,
|
|
140
|
+
batchSize: 100
|
|
141
|
+
})
|
|
142
|
+
}, { fallback: null });
|
|
143
|
+
if (!call.ok) {
|
|
144
|
+
throw new Error(call.result?.error ?? "Failed to start sync");
|
|
145
|
+
}
|
|
146
|
+
flash(t("data_sync.integrationTab.runStarted", "Sync run started."), "success");
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const message = error instanceof Error ? error.message : t("data_sync.integrationTab.runError", "Failed to start sync.");
|
|
149
|
+
flash(message, "error");
|
|
150
|
+
} finally {
|
|
151
|
+
setRunningKey(null);
|
|
152
|
+
}
|
|
153
|
+
}, [props.hasCredentials, props.integrationId, props.isEnabled, schedules, t]);
|
|
154
|
+
const handleSaveSchedule = React.useCallback(async (entityType, direction, scheduleKey) => {
|
|
155
|
+
const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType);
|
|
156
|
+
setSavingKey(scheduleKey);
|
|
157
|
+
try {
|
|
158
|
+
const call = await apiCall("/api/data_sync/schedules", {
|
|
159
|
+
method: "POST",
|
|
160
|
+
headers: { "content-type": "application/json" },
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
integrationId: props.integrationId,
|
|
163
|
+
entityType,
|
|
164
|
+
direction,
|
|
165
|
+
scheduleType: scheduleState.scheduleType,
|
|
166
|
+
scheduleValue: scheduleState.scheduleValue,
|
|
167
|
+
timezone: scheduleState.timezone,
|
|
168
|
+
fullSync: scheduleState.fullSync,
|
|
169
|
+
isEnabled: scheduleState.isEnabled
|
|
170
|
+
})
|
|
171
|
+
}, { fallback: null });
|
|
172
|
+
if (!call.ok || !call.result) {
|
|
173
|
+
throw new Error(call.result?.error ?? "Failed to save schedule");
|
|
174
|
+
}
|
|
175
|
+
updateScheduleEditor(scheduleKey, {
|
|
176
|
+
id: call.result.id,
|
|
177
|
+
scheduleType: call.result.scheduleType,
|
|
178
|
+
scheduleValue: call.result.scheduleValue,
|
|
179
|
+
timezone: call.result.timezone,
|
|
180
|
+
fullSync: call.result.fullSync,
|
|
181
|
+
isEnabled: call.result.isEnabled,
|
|
182
|
+
lastRunAt: call.result.lastRunAt
|
|
183
|
+
}, entityType);
|
|
184
|
+
flash(t("data_sync.dashboard.schedule.success", "Recurring schedule saved"), "success");
|
|
185
|
+
} catch (error) {
|
|
186
|
+
const message = error instanceof Error ? error.message : t("data_sync.dashboard.schedule.error", "Failed to save recurring schedule");
|
|
187
|
+
flash(message, "error");
|
|
188
|
+
} finally {
|
|
189
|
+
setSavingKey(null);
|
|
190
|
+
}
|
|
191
|
+
}, [props.integrationId, schedules, t, updateScheduleEditor]);
|
|
192
|
+
const handleDeleteSchedule = React.useCallback(async (entityType, scheduleKey) => {
|
|
193
|
+
const scheduleState = schedules[scheduleKey];
|
|
194
|
+
if (!scheduleState?.id) {
|
|
195
|
+
updateScheduleEditor(scheduleKey, buildDefaultScheduleState(entityType), entityType);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
setDeletingKey(scheduleKey);
|
|
199
|
+
try {
|
|
200
|
+
const call = await apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleState.id)}`, {
|
|
201
|
+
method: "DELETE"
|
|
202
|
+
}, { fallback: null });
|
|
203
|
+
if (!call.ok) {
|
|
204
|
+
throw new Error(call.result?.error ?? "Failed to delete schedule");
|
|
205
|
+
}
|
|
206
|
+
setSchedules((current) => ({
|
|
207
|
+
...current,
|
|
208
|
+
[scheduleKey]: buildDefaultScheduleState(entityType)
|
|
209
|
+
}));
|
|
210
|
+
flash(t("data_sync.dashboard.schedule.deleteSuccess", "Recurring schedule removed"), "success");
|
|
211
|
+
} catch (error) {
|
|
212
|
+
const message = error instanceof Error ? error.message : t("data_sync.dashboard.schedule.deleteError", "Failed to remove recurring schedule");
|
|
213
|
+
flash(message, "error");
|
|
214
|
+
} finally {
|
|
215
|
+
setDeletingKey(null);
|
|
216
|
+
}
|
|
217
|
+
}, [schedules, t, updateScheduleEditor]);
|
|
218
|
+
if (isLoading) {
|
|
219
|
+
return /* @__PURE__ */ jsx("div", { className: "flex min-h-40 items-center justify-center rounded-lg border bg-card", children: /* @__PURE__ */ jsx(Spinner, {}) });
|
|
220
|
+
}
|
|
221
|
+
if (!option) {
|
|
222
|
+
return /* @__PURE__ */ jsx(Notice, { compact: true, variant: "warning", children: t("data_sync.integrationTab.notAvailable", "This integration is not registered as a data sync provider.") });
|
|
223
|
+
}
|
|
224
|
+
return /* @__PURE__ */ jsxs("section", { className: "space-y-4 rounded-lg border bg-card p-6", children: [
|
|
225
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
|
|
226
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
227
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
228
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "outline", className: props.isEnabled ? "border-emerald-300 text-emerald-700" : "border-amber-300 text-amber-700", children: [
|
|
229
|
+
props.isEnabled ? /* @__PURE__ */ jsx(ShieldCheck, { className: "mr-2 h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ShieldAlert, { className: "mr-2 h-3.5 w-3.5" }),
|
|
230
|
+
props.isEnabled ? t("data_sync.dashboard.start.status.enabled", "Integration enabled") : t("data_sync.dashboard.start.status.disabled", "Integration disabled")
|
|
231
|
+
] }),
|
|
232
|
+
/* @__PURE__ */ jsxs(Badge, { variant: "outline", className: props.hasCredentials ? "border-sky-300 text-sky-700" : "border-amber-300 text-amber-700", children: [
|
|
233
|
+
/* @__PURE__ */ jsx(CalendarClock, { className: "mr-2 h-3.5 w-3.5" }),
|
|
234
|
+
props.hasCredentials ? t("data_sync.dashboard.start.status.credentialsReady", "Credentials ready") : t("data_sync.dashboard.start.status.credentialsMissing", "Credentials missing")
|
|
235
|
+
] })
|
|
236
|
+
] }),
|
|
237
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
238
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: t("data_sync.integrationTab.title", "Sync schedules") }),
|
|
239
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("data_sync.integrationTab.description", "Run one-off syncs or save recurring schedules for every supported entity directly from the integration detail page.") })
|
|
240
|
+
] })
|
|
241
|
+
] }),
|
|
242
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", onClick: () => void load(), disabled: isLoading, children: [
|
|
243
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "mr-2 h-4 w-4" }),
|
|
244
|
+
t("data_sync.integrationTab.refresh", "Refresh")
|
|
245
|
+
] })
|
|
246
|
+
] }),
|
|
247
|
+
!props.isEnabled ? /* @__PURE__ */ jsx(Notice, { compact: true, variant: "warning", children: t("data_sync.integrationTab.integrationDisabledNotice", "The integration is disabled. You can save schedules now, but runs will stay blocked until the integration is enabled.") }) : null,
|
|
248
|
+
!props.hasCredentials ? /* @__PURE__ */ jsx(Notice, { compact: true, variant: "warning", children: t("data_sync.integrationTab.credentialsMissingNotice", "Credentials are still missing. Save schedules first if you want, but manual and scheduled runs will fail until credentials are configured.") }) : null,
|
|
249
|
+
rows.length === 0 ? /* @__PURE__ */ jsx(Notice, { compact: true, children: t("data_sync.integrationTab.empty", "This provider does not expose any schedulable sync entities yet.") }) : /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-[1080px] text-sm", children: [
|
|
250
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-muted/50 text-left", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
251
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.dashboard.columns.entityType", "Entity Type") }),
|
|
252
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.dashboard.columns.direction", "Direction") }),
|
|
253
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.dashboard.schedule.type", "Schedule type") }),
|
|
254
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.integrationTab.columns.value", "Value") }),
|
|
255
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.dashboard.schedule.timezone", "Timezone") }),
|
|
256
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.dashboard.start.fullSync", "Run as full sync") }),
|
|
257
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.dashboard.schedule.enabled", "Schedule enabled") }),
|
|
258
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.integrationTab.columns.lastRun", "Last run") }),
|
|
259
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: t("data_sync.integrationTab.columns.actions", "Actions") })
|
|
260
|
+
] }) }),
|
|
261
|
+
/* @__PURE__ */ jsx("tbody", { children: rows.map((row) => {
|
|
262
|
+
const scheduleState = schedules[row.key] ?? buildDefaultScheduleState(row.entityType);
|
|
263
|
+
const isRunning = runningKey === row.key;
|
|
264
|
+
const isSaving = savingKey === row.key;
|
|
265
|
+
const isDeleting = deletingKey === row.key;
|
|
266
|
+
const controlsDisabled = isRunning || isSaving || isDeleting;
|
|
267
|
+
return /* @__PURE__ */ jsxs("tr", { className: "border-t align-top", children: [
|
|
268
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3 font-medium", children: formatEntityTypeLabel(row.entityType) }),
|
|
269
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: t(`data_sync.dashboard.direction.${row.direction}`, row.direction === "import" ? "Import" : "Export") }),
|
|
270
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxs(
|
|
271
|
+
"select",
|
|
272
|
+
{
|
|
273
|
+
className: "flex h-10 w-full min-w-32 rounded-md border border-input bg-background px-3 py-2 text-sm",
|
|
274
|
+
value: scheduleState.scheduleType,
|
|
275
|
+
onChange: (event) => updateScheduleEditor(row.key, {
|
|
276
|
+
scheduleType: event.target.value === "cron" ? "cron" : "interval"
|
|
277
|
+
}, row.entityType),
|
|
278
|
+
disabled: controlsDisabled,
|
|
279
|
+
children: [
|
|
280
|
+
/* @__PURE__ */ jsx("option", { value: "interval", children: t("data_sync.dashboard.schedule.interval", "Interval") }),
|
|
281
|
+
/* @__PURE__ */ jsx("option", { value: "cron", children: t("data_sync.dashboard.schedule.cron", "Cron") })
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
) }),
|
|
285
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsx(
|
|
286
|
+
Input,
|
|
287
|
+
{
|
|
288
|
+
value: scheduleState.scheduleValue,
|
|
289
|
+
onChange: (event) => updateScheduleEditor(row.key, { scheduleValue: event.target.value }, row.entityType),
|
|
290
|
+
disabled: controlsDisabled,
|
|
291
|
+
placeholder: scheduleState.scheduleType === "cron" ? "0 * * * *" : "1h"
|
|
292
|
+
}
|
|
293
|
+
) }),
|
|
294
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsx(
|
|
295
|
+
Input,
|
|
296
|
+
{
|
|
297
|
+
value: scheduleState.timezone,
|
|
298
|
+
onChange: (event) => updateScheduleEditor(row.key, { timezone: event.target.value }, row.entityType),
|
|
299
|
+
disabled: controlsDisabled
|
|
300
|
+
}
|
|
301
|
+
) }),
|
|
302
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxs("label", { className: "flex min-h-10 items-center gap-2 text-sm", children: [
|
|
303
|
+
/* @__PURE__ */ jsx(
|
|
304
|
+
"input",
|
|
305
|
+
{
|
|
306
|
+
type: "checkbox",
|
|
307
|
+
checked: scheduleState.fullSync,
|
|
308
|
+
onChange: (event) => updateScheduleEditor(row.key, { fullSync: event.target.checked }, row.entityType),
|
|
309
|
+
disabled: controlsDisabled
|
|
310
|
+
}
|
|
311
|
+
),
|
|
312
|
+
/* @__PURE__ */ jsx("span", { children: t("data_sync.integrationTab.fullSyncShort", "Full") })
|
|
313
|
+
] }) }),
|
|
314
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxs("label", { className: "flex min-h-10 items-center gap-2 text-sm", children: [
|
|
315
|
+
/* @__PURE__ */ jsx(
|
|
316
|
+
"input",
|
|
317
|
+
{
|
|
318
|
+
type: "checkbox",
|
|
319
|
+
checked: scheduleState.isEnabled,
|
|
320
|
+
onChange: (event) => updateScheduleEditor(row.key, { isEnabled: event.target.checked }, row.entityType),
|
|
321
|
+
disabled: controlsDisabled
|
|
322
|
+
}
|
|
323
|
+
),
|
|
324
|
+
/* @__PURE__ */ jsx("span", { children: scheduleState.isEnabled ? t("data_sync.dashboard.schedule.status.shortEnabled", "Scheduled") : t("data_sync.dashboard.schedule.status.shortDisabled", "Paused") })
|
|
325
|
+
] }) }),
|
|
326
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3 text-muted-foreground", children: scheduleState.lastRunAt ? new Date(scheduleState.lastRunAt).toLocaleString() : scheduleState.id ? t("data_sync.dashboard.schedule.neverRun", "Saved, but no scheduled execution has completed yet.") : t("data_sync.dashboard.schedule.none", "No recurring schedule saved for this target yet.") }),
|
|
327
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
328
|
+
/* @__PURE__ */ jsxs(
|
|
329
|
+
Button,
|
|
330
|
+
{
|
|
331
|
+
type: "button",
|
|
332
|
+
size: "sm",
|
|
333
|
+
onClick: () => void handleStartSync(row.entityType, row.direction, row.key),
|
|
334
|
+
disabled: controlsDisabled || !props.isEnabled || !props.hasCredentials,
|
|
335
|
+
children: [
|
|
336
|
+
isRunning ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }) : /* @__PURE__ */ jsx(Play, { className: "mr-2 h-4 w-4" }),
|
|
337
|
+
isRunning ? t("data_sync.integrationTab.starting", "Starting...") : t("data_sync.integrationTab.start", "Run now")
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
/* @__PURE__ */ jsxs(
|
|
342
|
+
Button,
|
|
343
|
+
{
|
|
344
|
+
type: "button",
|
|
345
|
+
size: "sm",
|
|
346
|
+
variant: "outline",
|
|
347
|
+
onClick: () => void handleSaveSchedule(row.entityType, row.direction, row.key),
|
|
348
|
+
disabled: controlsDisabled,
|
|
349
|
+
children: [
|
|
350
|
+
isSaving ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }) : /* @__PURE__ */ jsx(Save, { className: "mr-2 h-4 w-4" }),
|
|
351
|
+
isSaving ? t("data_sync.dashboard.schedule.saving", "Saving...") : t("data_sync.dashboard.schedule.save", "Save recurring schedule")
|
|
352
|
+
]
|
|
353
|
+
}
|
|
354
|
+
),
|
|
355
|
+
/* @__PURE__ */ jsxs(
|
|
356
|
+
Button,
|
|
357
|
+
{
|
|
358
|
+
type: "button",
|
|
359
|
+
size: "sm",
|
|
360
|
+
variant: "outline",
|
|
361
|
+
onClick: () => void handleDeleteSchedule(row.entityType, row.key),
|
|
362
|
+
disabled: controlsDisabled || !scheduleState.id,
|
|
363
|
+
children: [
|
|
364
|
+
isDeleting ? /* @__PURE__ */ jsx(Spinner, { className: "mr-2 h-4 w-4" }) : /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
|
|
365
|
+
isDeleting ? t("data_sync.dashboard.schedule.deleting", "Removing...") : t("data_sync.dashboard.schedule.delete", "Remove schedule")
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
] }) })
|
|
370
|
+
] }, row.key);
|
|
371
|
+
}) })
|
|
372
|
+
] }) }),
|
|
373
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 rounded-lg border border-dashed p-4 text-sm text-muted-foreground md:grid-cols-3", children: [
|
|
374
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
375
|
+
/* @__PURE__ */ jsx(Label, { children: t("data_sync.dashboard.schedule.interval", "Interval") }),
|
|
376
|
+
/* @__PURE__ */ jsx("p", { children: t("data_sync.dashboard.schedule.intervalHelp", "Example: `1h`, `6h`, or `24h` for repeating intervals.") })
|
|
377
|
+
] }),
|
|
378
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
379
|
+
/* @__PURE__ */ jsx(Label, { children: t("data_sync.dashboard.schedule.cron", "Cron") }),
|
|
380
|
+
/* @__PURE__ */ jsx("p", { children: t("data_sync.dashboard.schedule.cronHelp", "Example: `0 * * * *` runs at the start of every hour.") })
|
|
381
|
+
] }),
|
|
382
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
383
|
+
/* @__PURE__ */ jsx(Label, { children: t("data_sync.integrationTab.runNowTitle", "Manual runs") }),
|
|
384
|
+
/* @__PURE__ */ jsx("p", { children: t("data_sync.integrationTab.runNowHelp", "Run now uses the full-sync checkbox from the same row and starts progress tracking immediately in Data Sync.") })
|
|
385
|
+
] })
|
|
386
|
+
] })
|
|
387
|
+
] });
|
|
388
|
+
}
|
|
389
|
+
var IntegrationScheduleTab_default = IntegrationScheduleTab;
|
|
390
|
+
export {
|
|
391
|
+
IntegrationScheduleTab,
|
|
392
|
+
IntegrationScheduleTab_default as default
|
|
393
|
+
};
|
|
394
|
+
//# sourceMappingURL=IntegrationScheduleTab.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/data_sync/components/IntegrationScheduleTab.tsx"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Notice } from '@open-mercato/ui/primitives/Notice'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n CalendarClock,\n Play,\n RefreshCw,\n Save,\n ShieldAlert,\n ShieldCheck,\n Trash2,\n} from 'lucide-react'\n\ntype SyncOption = {\n integrationId: string\n title: string\n direction: 'import' | 'export' | 'bidirectional'\n supportedEntities: string[]\n hasCredentials: boolean\n isEnabled: boolean\n}\n\ntype SyncOptionsResponse = {\n items?: SyncOption[]\n}\n\ntype SyncScheduleRecord = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n}\n\ntype SyncSchedulesResponse = {\n items?: SyncScheduleRecord[]\n}\n\ntype SyncScheduleEditorState = {\n id?: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n}\n\ntype IntegrationScheduleTabProps = {\n integrationId: string\n hasCredentials: boolean\n isEnabled: boolean\n}\n\nconst DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\nfunction formatEntityTypeLabel(entityType: string): string {\n return entityType\n .replace(/[_-]+/g, ' ')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nfunction buildDefaultScheduleState(entityType: string): SyncScheduleEditorState {\n const normalized = entityType.trim().toLowerCase()\n const longerInterval = normalized === 'categories' || normalized === 'attributes'\n return {\n scheduleType: 'interval',\n scheduleValue: longerInterval ? '6h' : '1h',\n timezone: DEFAULT_TIMEZONE,\n fullSync: normalized !== 'products',\n isEnabled: true,\n lastRunAt: null,\n }\n}\n\nfunction getSupportedDirections(direction: SyncOption['direction'] | null | undefined): Array<'import' | 'export'> {\n if (direction === 'export') return ['export']\n if (direction === 'bidirectional') return ['import', 'export']\n return ['import']\n}\n\nfunction buildScheduleKey(entityType: string, direction: 'import' | 'export'): string {\n return `${entityType}:${direction}`\n}\n\nfunction buildScheduleEditors(\n entityTypes: string[],\n directions: Array<'import' | 'export'>,\n records: SyncScheduleRecord[],\n): Record<string, SyncScheduleEditorState> {\n const nextEntries: Array<[string, SyncScheduleEditorState]> = []\n for (const entityType of entityTypes) {\n for (const direction of directions) {\n const record = records.find((item) => item.entityType === entityType && item.direction === direction)\n nextEntries.push([\n buildScheduleKey(entityType, direction),\n record\n ? {\n id: record.id,\n scheduleType: record.scheduleType,\n scheduleValue: record.scheduleValue,\n timezone: record.timezone,\n fullSync: record.fullSync,\n isEnabled: record.isEnabled,\n lastRunAt: record.lastRunAt,\n }\n : buildDefaultScheduleState(entityType),\n ])\n }\n }\n return Object.fromEntries(nextEntries)\n}\n\nexport function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [option, setOption] = React.useState<SyncOption | null>(null)\n const [schedules, setSchedules] = React.useState<Record<string, SyncScheduleEditorState>>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [runningKey, setRunningKey] = React.useState<string | null>(null)\n const [savingKey, setSavingKey] = React.useState<string | null>(null)\n const [deletingKey, setDeletingKey] = React.useState<string | null>(null)\n\n const load = React.useCallback(async () => {\n setIsLoading(true)\n try {\n const [optionsCall, schedulesCall] = await Promise.all([\n apiCall<SyncOptionsResponse>('/api/data_sync/options', undefined, { fallback: { items: [] } }),\n apiCall<SyncSchedulesResponse>(`/api/data_sync/schedules?integrationId=${encodeURIComponent(props.integrationId)}&page=1&pageSize=100`, undefined, { fallback: { items: [] } }),\n ])\n\n const resolvedOption = (optionsCall.result?.items ?? []).find((item) => item.integrationId === props.integrationId) ?? null\n setOption(resolvedOption)\n\n const supportedEntities = resolvedOption?.supportedEntities ?? []\n const supportedDirections = getSupportedDirections(resolvedOption?.direction)\n setSchedules(buildScheduleEditors(supportedEntities, supportedDirections, schedulesCall.result?.items ?? []))\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.integrationTab.loadError', 'Failed to load sync schedules.')\n flash(message, 'error')\n } finally {\n setIsLoading(false)\n }\n }, [props.integrationId, t])\n\n React.useEffect(() => {\n void load()\n }, [load, scopeVersion])\n\n const supportedDirections = React.useMemo(\n () => getSupportedDirections(option?.direction),\n [option?.direction],\n )\n\n const rows = React.useMemo(\n () => (option?.supportedEntities ?? []).flatMap((entityType) => (\n supportedDirections.map((direction) => ({\n entityType,\n direction,\n key: buildScheduleKey(entityType, direction),\n }))\n )),\n [option?.supportedEntities, supportedDirections],\n )\n\n const updateScheduleEditor = React.useCallback((key: string, patch: Partial<SyncScheduleEditorState>, entityType: string) => {\n setSchedules((current) => ({\n ...current,\n [key]: {\n ...(current[key] ?? buildDefaultScheduleState(entityType)),\n ...patch,\n },\n }))\n }, [])\n\n const handleStartSync = React.useCallback(async (entityType: string, direction: 'import' | 'export', scheduleKey: string) => {\n if (!props.isEnabled) {\n flash(t('data_sync.integrationTab.integrationDisabled', 'Enable the integration before starting a sync.'), 'error')\n return\n }\n if (!props.hasCredentials) {\n flash(t('data_sync.integrationTab.credentialsMissing', 'Configure credentials before starting a sync.'), 'error')\n return\n }\n\n setRunningKey(scheduleKey)\n try {\n const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)\n const call = await apiCall<{ id: string }>('/api/data_sync/run', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n integrationId: props.integrationId,\n entityType,\n direction,\n fullSync: scheduleState.fullSync,\n batchSize: 100,\n }),\n }, { fallback: null })\n\n if (!call.ok) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to start sync')\n }\n\n flash(t('data_sync.integrationTab.runStarted', 'Sync run started.'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.integrationTab.runError', 'Failed to start sync.')\n flash(message, 'error')\n } finally {\n setRunningKey(null)\n }\n }, [props.hasCredentials, props.integrationId, props.isEnabled, schedules, t])\n\n const handleSaveSchedule = React.useCallback(async (entityType: string, direction: 'import' | 'export', scheduleKey: string) => {\n const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)\n setSavingKey(scheduleKey)\n try {\n const call = await apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n integrationId: props.integrationId,\n entityType,\n direction,\n scheduleType: scheduleState.scheduleType,\n scheduleValue: scheduleState.scheduleValue,\n timezone: scheduleState.timezone,\n fullSync: scheduleState.fullSync,\n isEnabled: scheduleState.isEnabled,\n }),\n }, { fallback: null })\n\n if (!call.ok || !call.result) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to save schedule')\n }\n\n updateScheduleEditor(scheduleKey, {\n id: call.result.id,\n scheduleType: call.result.scheduleType,\n scheduleValue: call.result.scheduleValue,\n timezone: call.result.timezone,\n fullSync: call.result.fullSync,\n isEnabled: call.result.isEnabled,\n lastRunAt: call.result.lastRunAt,\n }, entityType)\n flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')\n flash(message, 'error')\n } finally {\n setSavingKey(null)\n }\n }, [props.integrationId, schedules, t, updateScheduleEditor])\n\n const handleDeleteSchedule = React.useCallback(async (entityType: string, scheduleKey: string) => {\n const scheduleState = schedules[scheduleKey]\n if (!scheduleState?.id) {\n updateScheduleEditor(scheduleKey, buildDefaultScheduleState(entityType), entityType)\n return\n }\n\n setDeletingKey(scheduleKey)\n try {\n const call = await apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleState.id)}`, {\n method: 'DELETE',\n }, { fallback: null })\n\n if (!call.ok) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to delete schedule')\n }\n\n setSchedules((current) => ({\n ...current,\n [scheduleKey]: buildDefaultScheduleState(entityType),\n }))\n flash(t('data_sync.dashboard.schedule.deleteSuccess', 'Recurring schedule removed'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule')\n flash(message, 'error')\n } finally {\n setDeletingKey(null)\n }\n }, [schedules, t, updateScheduleEditor])\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-40 items-center justify-center rounded-lg border bg-card\">\n <Spinner />\n </div>\n )\n }\n\n if (!option) {\n return (\n <Notice compact variant=\"warning\">\n {t('data_sync.integrationTab.notAvailable', 'This integration is not registered as a data sync provider.')}\n </Notice>\n )\n }\n\n return (\n <section className=\"space-y-4 rounded-lg border bg-card p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={props.isEnabled ? 'border-emerald-300 text-emerald-700' : 'border-amber-300 text-amber-700'}>\n {props.isEnabled ? <ShieldCheck className=\"mr-2 h-3.5 w-3.5\" /> : <ShieldAlert className=\"mr-2 h-3.5 w-3.5\" />}\n {props.isEnabled\n ? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')\n : t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}\n </Badge>\n <Badge variant=\"outline\" className={props.hasCredentials ? 'border-sky-300 text-sky-700' : 'border-amber-300 text-amber-700'}>\n <CalendarClock className=\"mr-2 h-3.5 w-3.5\" />\n {props.hasCredentials\n ? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')\n : t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}\n </Badge>\n </div>\n <div>\n <h3 className=\"text-base font-semibold\">{t('data_sync.integrationTab.title', 'Sync schedules')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.integrationTab.description', 'Run one-off syncs or save recurring schedules for every supported entity directly from the integration detail page.')}\n </p>\n </div>\n </div>\n <Button type=\"button\" variant=\"outline\" onClick={() => void load()} disabled={isLoading}>\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n {t('data_sync.integrationTab.refresh', 'Refresh')}\n </Button>\n </div>\n\n {!props.isEnabled ? (\n <Notice compact variant=\"warning\">\n {t('data_sync.integrationTab.integrationDisabledNotice', 'The integration is disabled. You can save schedules now, but runs will stay blocked until the integration is enabled.')}\n </Notice>\n ) : null}\n\n {!props.hasCredentials ? (\n <Notice compact variant=\"warning\">\n {t('data_sync.integrationTab.credentialsMissingNotice', 'Credentials are still missing. Save schedules first if you want, but manual and scheduled runs will fail until credentials are configured.')}\n </Notice>\n ) : null}\n\n {rows.length === 0 ? (\n <Notice compact>\n {t('data_sync.integrationTab.empty', 'This provider does not expose any schedulable sync entities yet.')}\n </Notice>\n ) : (\n <div className=\"overflow-x-auto rounded-lg border\">\n <table className=\"w-full min-w-[1080px] text-sm\">\n <thead className=\"bg-muted/50 text-left\">\n <tr>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.columns.entityType', 'Entity Type')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.columns.direction', 'Direction')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.type', 'Schedule type')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.value', 'Value')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.timezone', 'Timezone')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.start.fullSync', 'Run as full sync')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.enabled', 'Schedule enabled')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.lastRun', 'Last run')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.actions', 'Actions')}</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n const scheduleState = schedules[row.key] ?? buildDefaultScheduleState(row.entityType)\n const isRunning = runningKey === row.key\n const isSaving = savingKey === row.key\n const isDeleting = deletingKey === row.key\n const controlsDisabled = isRunning || isSaving || isDeleting\n return (\n <tr key={row.key} className=\"border-t align-top\">\n <td className=\"px-3 py-3 font-medium\">{formatEntityTypeLabel(row.entityType)}</td>\n <td className=\"px-3 py-3\">{t(`data_sync.dashboard.direction.${row.direction}`, row.direction === 'import' ? 'Import' : 'Export')}</td>\n <td className=\"px-3 py-3\">\n <select\n className=\"flex h-10 w-full min-w-32 rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={scheduleState.scheduleType}\n onChange={(event) => updateScheduleEditor(row.key, {\n scheduleType: event.target.value === 'cron' ? 'cron' : 'interval',\n }, row.entityType)}\n disabled={controlsDisabled}\n >\n <option value=\"interval\">{t('data_sync.dashboard.schedule.interval', 'Interval')}</option>\n <option value=\"cron\">{t('data_sync.dashboard.schedule.cron', 'Cron')}</option>\n </select>\n </td>\n <td className=\"px-3 py-3\">\n <Input\n value={scheduleState.scheduleValue}\n onChange={(event) => updateScheduleEditor(row.key, { scheduleValue: event.target.value }, row.entityType)}\n disabled={controlsDisabled}\n placeholder={scheduleState.scheduleType === 'cron' ? '0 * * * *' : '1h'}\n />\n </td>\n <td className=\"px-3 py-3\">\n <Input\n value={scheduleState.timezone}\n onChange={(event) => updateScheduleEditor(row.key, { timezone: event.target.value }, row.entityType)}\n disabled={controlsDisabled}\n />\n </td>\n <td className=\"px-3 py-3\">\n <label className=\"flex min-h-10 items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={scheduleState.fullSync}\n onChange={(event) => updateScheduleEditor(row.key, { fullSync: event.target.checked }, row.entityType)}\n disabled={controlsDisabled}\n />\n <span>{t('data_sync.integrationTab.fullSyncShort', 'Full')}</span>\n </label>\n </td>\n <td className=\"px-3 py-3\">\n <label className=\"flex min-h-10 items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={scheduleState.isEnabled}\n onChange={(event) => updateScheduleEditor(row.key, { isEnabled: event.target.checked }, row.entityType)}\n disabled={controlsDisabled}\n />\n <span>{scheduleState.isEnabled ? t('data_sync.dashboard.schedule.status.shortEnabled', 'Scheduled') : t('data_sync.dashboard.schedule.status.shortDisabled', 'Paused')}</span>\n </label>\n </td>\n <td className=\"px-3 py-3 text-muted-foreground\">\n {scheduleState.lastRunAt\n ? new Date(scheduleState.lastRunAt).toLocaleString()\n : scheduleState.id\n ? t('data_sync.dashboard.schedule.neverRun', 'Saved, but no scheduled execution has completed yet.')\n : t('data_sync.dashboard.schedule.none', 'No recurring schedule saved for this target yet.')}\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void handleStartSync(row.entityType, row.direction, row.key)}\n disabled={controlsDisabled || !props.isEnabled || !props.hasCredentials}\n >\n {isRunning ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Play className=\"mr-2 h-4 w-4\" />}\n {isRunning\n ? t('data_sync.integrationTab.starting', 'Starting...')\n : t('data_sync.integrationTab.start', 'Run now')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void handleSaveSchedule(row.entityType, row.direction, row.key)}\n disabled={controlsDisabled}\n >\n {isSaving ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Save className=\"mr-2 h-4 w-4\" />}\n {isSaving\n ? t('data_sync.dashboard.schedule.saving', 'Saving...')\n : t('data_sync.dashboard.schedule.save', 'Save recurring schedule')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void handleDeleteSchedule(row.entityType, row.key)}\n disabled={controlsDisabled || !scheduleState.id}\n >\n {isDeleting ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Trash2 className=\"mr-2 h-4 w-4\" />}\n {isDeleting\n ? t('data_sync.dashboard.schedule.deleting', 'Removing...')\n : t('data_sync.dashboard.schedule.delete', 'Remove schedule')}\n </Button>\n </div>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )}\n\n <div className=\"grid gap-3 rounded-lg border border-dashed p-4 text-sm text-muted-foreground md:grid-cols-3\">\n <div className=\"space-y-1\">\n <Label>{t('data_sync.dashboard.schedule.interval', 'Interval')}</Label>\n <p>{t('data_sync.dashboard.schedule.intervalHelp', 'Example: `1h`, `6h`, or `24h` for repeating intervals.')}</p>\n </div>\n <div className=\"space-y-1\">\n <Label>{t('data_sync.dashboard.schedule.cron', 'Cron')}</Label>\n <p>{t('data_sync.dashboard.schedule.cronHelp', 'Example: `0 * * * *` runs at the start of every hour.')}</p>\n </div>\n <div className=\"space-y-1\">\n <Label>{t('data_sync.integrationTab.runNowTitle', 'Manual runs')}</Label>\n <p>{t('data_sync.integrationTab.runNowHelp', 'Run now uses the full-sync checkbox from the same row and starts progress tracking immediately in Data Sync.')}</p>\n </div>\n </div>\n </section>\n )\n}\n\nexport default IntegrationScheduleTab\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["supportedDirections"]
|
|
7
|
+
}
|
|
@@ -23,10 +23,42 @@ const listSyncRunsQuerySchema = z.object({
|
|
|
23
23
|
page: z.coerce.number().int().min(1).default(1),
|
|
24
24
|
pageSize: z.coerce.number().int().min(1).max(100).default(20)
|
|
25
25
|
});
|
|
26
|
+
const listSyncSchedulesQuerySchema = z.object({
|
|
27
|
+
integrationId: z.string().optional(),
|
|
28
|
+
entityType: z.string().optional(),
|
|
29
|
+
direction: z.enum(["import", "export"]).optional(),
|
|
30
|
+
page: z.coerce.number().int().min(1).default(1),
|
|
31
|
+
pageSize: z.coerce.number().int().min(1).max(100).default(20)
|
|
32
|
+
});
|
|
33
|
+
const createSyncScheduleSchema = z.object({
|
|
34
|
+
integrationId: z.string().min(1),
|
|
35
|
+
entityType: z.string().min(1),
|
|
36
|
+
direction: z.enum(["import", "export"]),
|
|
37
|
+
scheduleType: z.enum(["cron", "interval"]),
|
|
38
|
+
scheduleValue: z.string().min(1),
|
|
39
|
+
timezone: z.string().min(1).default("UTC"),
|
|
40
|
+
fullSync: z.boolean().default(false),
|
|
41
|
+
isEnabled: z.boolean().default(true)
|
|
42
|
+
});
|
|
43
|
+
const updateSyncScheduleSchema = z.object({
|
|
44
|
+
integrationId: z.string().min(1).optional(),
|
|
45
|
+
entityType: z.string().min(1).optional(),
|
|
46
|
+
direction: z.enum(["import", "export"]).optional(),
|
|
47
|
+
scheduleType: z.enum(["cron", "interval"]).optional(),
|
|
48
|
+
scheduleValue: z.string().min(1).optional(),
|
|
49
|
+
timezone: z.string().min(1).optional(),
|
|
50
|
+
fullSync: z.boolean().optional(),
|
|
51
|
+
isEnabled: z.boolean().optional()
|
|
52
|
+
}).refine((value) => Object.keys(value).length > 0, {
|
|
53
|
+
message: "At least one field must be updated"
|
|
54
|
+
});
|
|
26
55
|
export {
|
|
56
|
+
createSyncScheduleSchema,
|
|
27
57
|
listSyncRunsQuerySchema,
|
|
58
|
+
listSyncSchedulesQuerySchema,
|
|
28
59
|
retrySyncSchema,
|
|
29
60
|
runSyncSchema,
|
|
61
|
+
updateSyncScheduleSchema,
|
|
30
62
|
validateConnectionSchema
|
|
31
63
|
};
|
|
32
64
|
//# sourceMappingURL=validators.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/data_sync/data/validators.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\n\nexport const runSyncSchema = z.object({\n integrationId: z.string().min(1),\n entityType: z.string().min(1),\n direction: z.enum(['import', 'export']),\n fullSync: z.boolean().default(false),\n batchSize: z.number().int().min(1).max(1000).default(100),\n triggeredBy: z.string().optional(),\n})\n\nexport type RunSyncInput = z.infer<typeof runSyncSchema>\n\nexport const retrySyncSchema = z.object({\n fromBeginning: z.boolean().default(false),\n})\n\nexport type RetrySyncInput = z.infer<typeof retrySyncSchema>\n\nexport const validateConnectionSchema = z.object({\n integrationId: z.string().min(1),\n entityType: z.string().min(1),\n direction: z.enum(['import', 'export']),\n})\n\nexport const listSyncRunsQuerySchema = z.object({\n integrationId: z.string().optional(),\n entityType: z.string().optional(),\n direction: z.enum(['import', 'export']).optional(),\n status: z.enum(['pending', 'running', 'completed', 'failed', 'cancelled', 'paused']).optional(),\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n})\n\nexport type ListSyncRunsQuery = z.infer<typeof listSyncRunsQuerySchema>\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAEX,MAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,WAAW,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC;AAAA,EACtC,UAAU,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EACnC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,EAAE,QAAQ,GAAG;AAAA,EACxD,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAIM,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,eAAe,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAC1C,CAAC;AAIM,MAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC5B,WAAW,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC;AACxC,CAAC;AAEM,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,WAAW,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC,EAAE,SAAS;AAAA,EACjD,QAAQ,EAAE,KAAK,CAAC,WAAW,WAAW,aAAa,UAAU,aAAa,QAAQ,CAAC,EAAE,SAAS;AAAA,EAC9F,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EAC9C,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAC9D,CAAC;",
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\n\nexport const runSyncSchema = z.object({\n integrationId: z.string().min(1),\n entityType: z.string().min(1),\n direction: z.enum(['import', 'export']),\n fullSync: z.boolean().default(false),\n batchSize: z.number().int().min(1).max(1000).default(100),\n triggeredBy: z.string().optional(),\n})\n\nexport type RunSyncInput = z.infer<typeof runSyncSchema>\n\nexport const retrySyncSchema = z.object({\n fromBeginning: z.boolean().default(false),\n})\n\nexport type RetrySyncInput = z.infer<typeof retrySyncSchema>\n\nexport const validateConnectionSchema = z.object({\n integrationId: z.string().min(1),\n entityType: z.string().min(1),\n direction: z.enum(['import', 'export']),\n})\n\nexport const listSyncRunsQuerySchema = z.object({\n integrationId: z.string().optional(),\n entityType: z.string().optional(),\n direction: z.enum(['import', 'export']).optional(),\n status: z.enum(['pending', 'running', 'completed', 'failed', 'cancelled', 'paused']).optional(),\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n})\n\nexport type ListSyncRunsQuery = z.infer<typeof listSyncRunsQuerySchema>\n\nexport const listSyncSchedulesQuerySchema = z.object({\n integrationId: z.string().optional(),\n entityType: z.string().optional(),\n direction: z.enum(['import', 'export']).optional(),\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n})\n\nexport const createSyncScheduleSchema = z.object({\n integrationId: z.string().min(1),\n entityType: z.string().min(1),\n direction: z.enum(['import', 'export']),\n scheduleType: z.enum(['cron', 'interval']),\n scheduleValue: z.string().min(1),\n timezone: z.string().min(1).default('UTC'),\n fullSync: z.boolean().default(false),\n isEnabled: z.boolean().default(true),\n})\n\nexport const updateSyncScheduleSchema = z.object({\n integrationId: z.string().min(1).optional(),\n entityType: z.string().min(1).optional(),\n direction: z.enum(['import', 'export']).optional(),\n scheduleType: z.enum(['cron', 'interval']).optional(),\n scheduleValue: z.string().min(1).optional(),\n timezone: z.string().min(1).optional(),\n fullSync: z.boolean().optional(),\n isEnabled: z.boolean().optional(),\n}).refine((value) => Object.keys(value).length > 0, {\n message: 'At least one field must be updated',\n})\n\nexport type CreateSyncScheduleInput = z.infer<typeof createSyncScheduleSchema>\nexport type UpdateSyncScheduleInput = z.infer<typeof updateSyncScheduleSchema>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,11 +2,13 @@ import { asFunction, asValue } from "awilix";
|
|
|
2
2
|
import { SyncCursor, SyncMapping, SyncRun, SyncSchedule } from "./data/entities.js";
|
|
3
3
|
import { createExternalIdMappingService } from "./lib/id-mapping.js";
|
|
4
4
|
import { createSyncRunService } from "./lib/sync-run-service.js";
|
|
5
|
+
import { createSyncScheduleService } from "./lib/sync-schedule-service.js";
|
|
5
6
|
import { createSyncEngine } from "./lib/sync-engine.js";
|
|
6
7
|
function register(container) {
|
|
7
8
|
container.register({
|
|
8
9
|
externalIdMappingService: asFunction(({ em }) => createExternalIdMappingService(em)).scoped().proxy(),
|
|
9
10
|
dataSyncRunService: asFunction(({ em }) => createSyncRunService(em)).scoped().proxy(),
|
|
11
|
+
dataSyncScheduleService: asFunction(({ em, schedulerService }) => createSyncScheduleService(em, schedulerService)).scoped().proxy(),
|
|
10
12
|
dataSyncEngine: asFunction(({ em, dataSyncRunService, integrationCredentialsService, integrationLogService, progressService }) => createSyncEngine({
|
|
11
13
|
em,
|
|
12
14
|
syncRunService: dataSyncRunService,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/data_sync/di.ts"],
|
|
4
|
-
"sourcesContent": ["import { asFunction, asValue } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CredentialsService } from '../integrations/lib/credentials-service'\nimport type { IntegrationLogService } from '../integrations/lib/log-service'\nimport type { ProgressService } from '../progress/lib/progressService'\nimport { SyncCursor, SyncMapping, SyncRun, SyncSchedule } from './data/entities'\nimport { createExternalIdMappingService } from './lib/id-mapping'\nimport { createSyncRunService } from './lib/sync-run-service'\nimport { createSyncEngine } from './lib/sync-engine'\n\ntype Cradle = {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n integrationLogService: IntegrationLogService\n progressService: ProgressService\n}\n\nexport function register(container: AppContainer) {\n container.register({\n externalIdMappingService: asFunction(({ em }: Cradle) => createExternalIdMappingService(em)).scoped().proxy(),\n dataSyncRunService: asFunction(({ em }: Cradle) => createSyncRunService(em)).scoped().proxy(),\n dataSyncEngine: asFunction(({ em, dataSyncRunService, integrationCredentialsService, integrationLogService, progressService }: Cradle & {\n dataSyncRunService: ReturnType<typeof createSyncRunService>\n }) => createSyncEngine({\n em,\n syncRunService: dataSyncRunService,\n integrationCredentialsService,\n integrationLogService,\n progressService,\n })).scoped().proxy(),\n\n SyncRun: asValue(SyncRun),\n SyncCursor: asValue(SyncCursor),\n SyncMapping: asValue(SyncMapping),\n SyncSchedule: asValue(SyncSchedule),\n })\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY,eAAe;AAMpC,SAAS,YAAY,aAAa,SAAS,oBAAoB;AAC/D,SAAS,sCAAsC;AAC/C,SAAS,4BAA4B;AACrC,SAAS,wBAAwB;
|
|
4
|
+
"sourcesContent": ["import { asFunction, asValue } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CredentialsService } from '../integrations/lib/credentials-service'\nimport type { IntegrationLogService } from '../integrations/lib/log-service'\nimport type { ProgressService } from '../progress/lib/progressService'\nimport { SyncCursor, SyncMapping, SyncRun, SyncSchedule } from './data/entities'\nimport { createExternalIdMappingService } from './lib/id-mapping'\nimport { createSyncRunService } from './lib/sync-run-service'\nimport { createSyncScheduleService } from './lib/sync-schedule-service'\nimport { createSyncEngine } from './lib/sync-engine'\n\ntype Cradle = {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n integrationLogService: IntegrationLogService\n progressService: ProgressService\n schedulerService?: {\n register: (registration: Record<string, unknown>) => Promise<void>\n unregister: (scheduleId: string) => Promise<void>\n }\n}\n\nexport function register(container: AppContainer) {\n container.register({\n externalIdMappingService: asFunction(({ em }: Cradle) => createExternalIdMappingService(em)).scoped().proxy(),\n dataSyncRunService: asFunction(({ em }: Cradle) => createSyncRunService(em)).scoped().proxy(),\n dataSyncScheduleService: asFunction(({ em, schedulerService }: Cradle) => createSyncScheduleService(em, schedulerService)).scoped().proxy(),\n dataSyncEngine: asFunction(({ em, dataSyncRunService, integrationCredentialsService, integrationLogService, progressService }: Cradle & {\n dataSyncRunService: ReturnType<typeof createSyncRunService>\n }) => createSyncEngine({\n em,\n syncRunService: dataSyncRunService,\n integrationCredentialsService,\n integrationLogService,\n progressService,\n })).scoped().proxy(),\n\n SyncRun: asValue(SyncRun),\n SyncCursor: asValue(SyncCursor),\n SyncMapping: asValue(SyncMapping),\n SyncSchedule: asValue(SyncSchedule),\n })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY,eAAe;AAMpC,SAAS,YAAY,aAAa,SAAS,oBAAoB;AAC/D,SAAS,sCAAsC;AAC/C,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAC1C,SAAS,wBAAwB;AAa1B,SAAS,SAAS,WAAyB;AAChD,YAAU,SAAS;AAAA,IACjB,0BAA0B,WAAW,CAAC,EAAE,GAAG,MAAc,+BAA+B,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM;AAAA,IAC5G,oBAAoB,WAAW,CAAC,EAAE,GAAG,MAAc,qBAAqB,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM;AAAA,IAC5F,yBAAyB,WAAW,CAAC,EAAE,IAAI,iBAAiB,MAAc,0BAA0B,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM;AAAA,IAC1I,gBAAgB,WAAW,CAAC,EAAE,IAAI,oBAAoB,+BAA+B,uBAAuB,gBAAgB,MAEtH,iBAAiB;AAAA,MACrB;AAAA,MACA,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM;AAAA,IAEnB,SAAS,QAAQ,OAAO;AAAA,IACxB,YAAY,QAAQ,UAAU;AAAA,IAC9B,aAAa,QAAQ,WAAW;AAAA,IAChC,cAAc,QAAQ,YAAY;AAAA,EACpC,CAAC;AACH;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -37,7 +37,7 @@ function createExternalIdMappingService(em) {
|
|
|
37
37
|
return row?.externalId ?? null;
|
|
38
38
|
},
|
|
39
39
|
async storeExternalIdMapping(integrationId, entityType, localId, externalId, scope) {
|
|
40
|
-
const
|
|
40
|
+
const existingByLocalId = await findOneWithDecryption(
|
|
41
41
|
em,
|
|
42
42
|
SyncExternalIdMapping,
|
|
43
43
|
{
|
|
@@ -51,10 +51,32 @@ function createExternalIdMappingService(em) {
|
|
|
51
51
|
void 0,
|
|
52
52
|
scope
|
|
53
53
|
);
|
|
54
|
+
const existingByExternalId = await findOneWithDecryption(
|
|
55
|
+
em,
|
|
56
|
+
SyncExternalIdMapping,
|
|
57
|
+
{
|
|
58
|
+
integrationId,
|
|
59
|
+
internalEntityType: entityType,
|
|
60
|
+
externalId,
|
|
61
|
+
organizationId: scope.organizationId,
|
|
62
|
+
tenantId: scope.tenantId,
|
|
63
|
+
deletedAt: null
|
|
64
|
+
},
|
|
65
|
+
void 0,
|
|
66
|
+
scope
|
|
67
|
+
);
|
|
68
|
+
const existing = existingByExternalId ?? existingByLocalId;
|
|
54
69
|
if (existing) {
|
|
70
|
+
const now = /* @__PURE__ */ new Date();
|
|
71
|
+
existing.internalEntityId = localId;
|
|
55
72
|
existing.externalId = externalId;
|
|
56
73
|
existing.syncStatus = "synced";
|
|
57
|
-
existing.lastSyncedAt =
|
|
74
|
+
existing.lastSyncedAt = now;
|
|
75
|
+
existing.deletedAt = null;
|
|
76
|
+
if (existingByExternalId && existingByLocalId && existingByExternalId.id !== existingByLocalId.id) {
|
|
77
|
+
const duplicate = existing.id === existingByExternalId.id ? existingByLocalId : existingByExternalId;
|
|
78
|
+
duplicate.deletedAt = now;
|
|
79
|
+
}
|
|
58
80
|
await em.flush();
|
|
59
81
|
return existing;
|
|
60
82
|
}
|