@deenruv/merchant-plugin 1.0.17-dev.30 → 1.0.17-dev.32
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/dist/plugin-ui/index.js +1 -1
- package/dist/plugin-ui/locales/en/index.d.ts +59 -0
- package/dist/plugin-ui/locales/en/merchant.json +59 -0
- package/dist/plugin-ui/locales/pl/index.d.ts +59 -0
- package/dist/plugin-ui/locales/pl/merchant.json +60 -1
- package/dist/plugin-ui/pages/FacebookPage.js +21 -12
- package/dist/plugin-ui/pages/GooglePage.js +34 -21
- package/dist/plugin-ui/translations.spec.d.ts +1 -0
- package/dist/plugin-ui/translations.spec.js +52 -0
- package/package.json +7 -7
package/dist/plugin-ui/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createDeenruvUIPlugin, DEENRUV_UI_VERSION } from "@deenruv/react-ui-devkit";
|
|
2
|
+
import { createDeenruvUIPlugin, DEENRUV_UI_VERSION, } from "@deenruv/react-ui-devkit";
|
|
3
3
|
import { GooglePage } from "./pages/GooglePage.js";
|
|
4
4
|
import { FacebookPage } from "./pages/FacebookPage.js";
|
|
5
5
|
import { GlobeIcon } from "lucide-react";
|
|
@@ -4,5 +4,64 @@ declare const _default: {
|
|
|
4
4
|
facebookCommerce: string;
|
|
5
5
|
merchantPlatforms: string;
|
|
6
6
|
};
|
|
7
|
+
common: {
|
|
8
|
+
brand: string;
|
|
9
|
+
save: string;
|
|
10
|
+
loading: string;
|
|
11
|
+
autoUpdate: string;
|
|
12
|
+
updateAllOnSave: string;
|
|
13
|
+
connectionStatus: string;
|
|
14
|
+
connected: string;
|
|
15
|
+
disconnected: string;
|
|
16
|
+
productsCount: string;
|
|
17
|
+
removeOldItems: string;
|
|
18
|
+
settingsSaved: string;
|
|
19
|
+
settingsSaveFailed: string;
|
|
20
|
+
settingsSaveFailedWithError: string;
|
|
21
|
+
settingsLoadFailed: string;
|
|
22
|
+
unknownError: string;
|
|
23
|
+
oldItemsRemoved: string;
|
|
24
|
+
oldItemsRemoveFailed: string;
|
|
25
|
+
};
|
|
26
|
+
google: {
|
|
27
|
+
title: string;
|
|
28
|
+
merchantId: string;
|
|
29
|
+
dataSource: string;
|
|
30
|
+
contentLanguage: string;
|
|
31
|
+
feedLabel: string;
|
|
32
|
+
credentials: string;
|
|
33
|
+
invalidCredentialsFile: string;
|
|
34
|
+
credentialsReadFailed: string;
|
|
35
|
+
diagnostics: {
|
|
36
|
+
title: string;
|
|
37
|
+
status: string;
|
|
38
|
+
unknown: string;
|
|
39
|
+
dataSourceVerified: string;
|
|
40
|
+
latency: string;
|
|
41
|
+
disapprovedProducts: string;
|
|
42
|
+
productIssues: string;
|
|
43
|
+
yes: string;
|
|
44
|
+
no: string;
|
|
45
|
+
test: string;
|
|
46
|
+
completed: string;
|
|
47
|
+
};
|
|
48
|
+
sync: {
|
|
49
|
+
all: string;
|
|
50
|
+
queued: string;
|
|
51
|
+
queueFailed: string;
|
|
52
|
+
cleanupQueued: string;
|
|
53
|
+
cleanupQueueFailed: string;
|
|
54
|
+
recent: string;
|
|
55
|
+
empty: string;
|
|
56
|
+
successful: string;
|
|
57
|
+
failed: string;
|
|
58
|
+
attempts: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
facebook: {
|
|
62
|
+
title: string;
|
|
63
|
+
catalogId: string;
|
|
64
|
+
accessToken: string;
|
|
65
|
+
};
|
|
7
66
|
}[];
|
|
8
67
|
export default _default;
|
|
@@ -3,5 +3,64 @@
|
|
|
3
3
|
"googleMerchant": "Google Merchant",
|
|
4
4
|
"facebookCommerce": "Facebook Commerce",
|
|
5
5
|
"merchantPlatforms": "Merchant Platforms"
|
|
6
|
+
},
|
|
7
|
+
"common": {
|
|
8
|
+
"brand": "Brand",
|
|
9
|
+
"save": "Save",
|
|
10
|
+
"loading": "Loading platform settings",
|
|
11
|
+
"autoUpdate": "Automatically update when a product changes",
|
|
12
|
+
"updateAllOnSave": "Update all products when saving",
|
|
13
|
+
"connectionStatus": "Connection status",
|
|
14
|
+
"connected": "Connected",
|
|
15
|
+
"disconnected": "Disconnected",
|
|
16
|
+
"productsCount": "Product count",
|
|
17
|
+
"removeOldItems": "Remove old items",
|
|
18
|
+
"settingsSaved": "Settings saved successfully",
|
|
19
|
+
"settingsSaveFailed": "Failed to save settings",
|
|
20
|
+
"settingsSaveFailedWithError": "Failed to save settings: {{message}}",
|
|
21
|
+
"settingsLoadFailed": "Failed to load platform settings",
|
|
22
|
+
"unknownError": "Unknown error",
|
|
23
|
+
"oldItemsRemoved": "Old items removed successfully",
|
|
24
|
+
"oldItemsRemoveFailed": "Failed to remove old items"
|
|
25
|
+
},
|
|
26
|
+
"google": {
|
|
27
|
+
"title": "Google Merchant",
|
|
28
|
+
"merchantId": "Merchant ID",
|
|
29
|
+
"dataSource": "Data source",
|
|
30
|
+
"contentLanguage": "Content language",
|
|
31
|
+
"feedLabel": "Feed label",
|
|
32
|
+
"credentials": "Google account credentials",
|
|
33
|
+
"invalidCredentialsFile": "The credentials file is not valid JSON",
|
|
34
|
+
"credentialsReadFailed": "Failed to read the credentials file",
|
|
35
|
+
"diagnostics": {
|
|
36
|
+
"title": "Connection diagnostics",
|
|
37
|
+
"status": "Diagnostic status",
|
|
38
|
+
"unknown": "UNKNOWN",
|
|
39
|
+
"dataSourceVerified": "Data source verified",
|
|
40
|
+
"latency": "Latency",
|
|
41
|
+
"disapprovedProducts": "Disapproved products",
|
|
42
|
+
"productIssues": "Product issues",
|
|
43
|
+
"yes": "yes",
|
|
44
|
+
"no": "no",
|
|
45
|
+
"test": "Test connection",
|
|
46
|
+
"completed": "Connection diagnostic completed"
|
|
47
|
+
},
|
|
48
|
+
"sync": {
|
|
49
|
+
"all": "Synchronize all",
|
|
50
|
+
"queued": "Full synchronization queued",
|
|
51
|
+
"queueFailed": "Failed to queue full synchronization",
|
|
52
|
+
"cleanupQueued": "Orphan cleanup queued",
|
|
53
|
+
"cleanupQueueFailed": "Failed to queue orphan cleanup",
|
|
54
|
+
"recent": "Recent synchronizations",
|
|
55
|
+
"empty": "No synchronization history",
|
|
56
|
+
"successful": "successful",
|
|
57
|
+
"failed": "failed",
|
|
58
|
+
"attempts": "attempts"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"facebook": {
|
|
62
|
+
"title": "Facebook Commerce",
|
|
63
|
+
"catalogId": "Catalog ID",
|
|
64
|
+
"accessToken": "Facebook access token"
|
|
6
65
|
}
|
|
7
66
|
}
|
|
@@ -4,5 +4,64 @@ declare const _default: {
|
|
|
4
4
|
facebookCommerce: string;
|
|
5
5
|
merchantPlatforms: string;
|
|
6
6
|
};
|
|
7
|
+
common: {
|
|
8
|
+
brand: string;
|
|
9
|
+
save: string;
|
|
10
|
+
loading: string;
|
|
11
|
+
autoUpdate: string;
|
|
12
|
+
updateAllOnSave: string;
|
|
13
|
+
connectionStatus: string;
|
|
14
|
+
connected: string;
|
|
15
|
+
disconnected: string;
|
|
16
|
+
productsCount: string;
|
|
17
|
+
removeOldItems: string;
|
|
18
|
+
settingsSaved: string;
|
|
19
|
+
settingsSaveFailed: string;
|
|
20
|
+
settingsSaveFailedWithError: string;
|
|
21
|
+
settingsLoadFailed: string;
|
|
22
|
+
unknownError: string;
|
|
23
|
+
oldItemsRemoved: string;
|
|
24
|
+
oldItemsRemoveFailed: string;
|
|
25
|
+
};
|
|
26
|
+
google: {
|
|
27
|
+
title: string;
|
|
28
|
+
merchantId: string;
|
|
29
|
+
dataSource: string;
|
|
30
|
+
contentLanguage: string;
|
|
31
|
+
feedLabel: string;
|
|
32
|
+
credentials: string;
|
|
33
|
+
invalidCredentialsFile: string;
|
|
34
|
+
credentialsReadFailed: string;
|
|
35
|
+
diagnostics: {
|
|
36
|
+
title: string;
|
|
37
|
+
status: string;
|
|
38
|
+
unknown: string;
|
|
39
|
+
dataSourceVerified: string;
|
|
40
|
+
latency: string;
|
|
41
|
+
disapprovedProducts: string;
|
|
42
|
+
productIssues: string;
|
|
43
|
+
yes: string;
|
|
44
|
+
no: string;
|
|
45
|
+
test: string;
|
|
46
|
+
completed: string;
|
|
47
|
+
};
|
|
48
|
+
sync: {
|
|
49
|
+
all: string;
|
|
50
|
+
queued: string;
|
|
51
|
+
queueFailed: string;
|
|
52
|
+
cleanupQueued: string;
|
|
53
|
+
cleanupQueueFailed: string;
|
|
54
|
+
recent: string;
|
|
55
|
+
empty: string;
|
|
56
|
+
successful: string;
|
|
57
|
+
failed: string;
|
|
58
|
+
attempts: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
facebook: {
|
|
62
|
+
title: string;
|
|
63
|
+
catalogId: string;
|
|
64
|
+
accessToken: string;
|
|
65
|
+
};
|
|
7
66
|
}[];
|
|
8
67
|
export default _default;
|
|
@@ -2,6 +2,65 @@
|
|
|
2
2
|
"nav": {
|
|
3
3
|
"googleMerchant": "Google Merchant",
|
|
4
4
|
"facebookCommerce": "Facebook Commerce",
|
|
5
|
-
"merchantPlatforms": "
|
|
5
|
+
"merchantPlatforms": "Platformy sprzedażowe"
|
|
6
|
+
},
|
|
7
|
+
"common": {
|
|
8
|
+
"brand": "Marka",
|
|
9
|
+
"save": "Zapisz",
|
|
10
|
+
"loading": "Ładowanie ustawień platformy",
|
|
11
|
+
"autoUpdate": "Aktualizuj automatycznie po zmianie produktu",
|
|
12
|
+
"updateAllOnSave": "Zaktualizuj wszystkie produkty podczas zapisywania",
|
|
13
|
+
"connectionStatus": "Stan połączenia",
|
|
14
|
+
"connected": "Połączono",
|
|
15
|
+
"disconnected": "Rozłączono",
|
|
16
|
+
"productsCount": "Liczba produktów",
|
|
17
|
+
"removeOldItems": "Usuń stare pozycje",
|
|
18
|
+
"settingsSaved": "Ustawienia zostały zapisane",
|
|
19
|
+
"settingsSaveFailed": "Nie udało się zapisać ustawień",
|
|
20
|
+
"settingsSaveFailedWithError": "Nie udało się zapisać ustawień: {{message}}",
|
|
21
|
+
"settingsLoadFailed": "Nie udało się wczytać ustawień platformy",
|
|
22
|
+
"unknownError": "Nieznany błąd",
|
|
23
|
+
"oldItemsRemoved": "Stare pozycje zostały usunięte",
|
|
24
|
+
"oldItemsRemoveFailed": "Nie udało się usunąć starych pozycji"
|
|
25
|
+
},
|
|
26
|
+
"google": {
|
|
27
|
+
"title": "Google Merchant",
|
|
28
|
+
"merchantId": "Identyfikator sprzedawcy",
|
|
29
|
+
"dataSource": "Źródło danych",
|
|
30
|
+
"contentLanguage": "Język treści",
|
|
31
|
+
"feedLabel": "Etykieta pliku danych",
|
|
32
|
+
"credentials": "Dane logowania konta Google",
|
|
33
|
+
"invalidCredentialsFile": "Plik danych logowania nie zawiera poprawnego JSON-a",
|
|
34
|
+
"credentialsReadFailed": "Nie udało się odczytać pliku danych logowania",
|
|
35
|
+
"diagnostics": {
|
|
36
|
+
"title": "Diagnostyka połączenia",
|
|
37
|
+
"status": "Stan diagnostyki",
|
|
38
|
+
"unknown": "NIEZNANY",
|
|
39
|
+
"dataSourceVerified": "Źródło danych zweryfikowane",
|
|
40
|
+
"latency": "Opóźnienie",
|
|
41
|
+
"disapprovedProducts": "Odrzucone produkty",
|
|
42
|
+
"productIssues": "Problemy z produktami",
|
|
43
|
+
"yes": "tak",
|
|
44
|
+
"no": "nie",
|
|
45
|
+
"test": "Sprawdź połączenie",
|
|
46
|
+
"completed": "Diagnostyka połączenia zakończona"
|
|
47
|
+
},
|
|
48
|
+
"sync": {
|
|
49
|
+
"all": "Synchronizuj wszystko",
|
|
50
|
+
"queued": "Pełna synchronizacja została dodana do kolejki",
|
|
51
|
+
"queueFailed": "Nie udało się dodać pełnej synchronizacji do kolejki",
|
|
52
|
+
"cleanupQueued": "Usuwanie osieroconych pozycji zostało dodane do kolejki",
|
|
53
|
+
"cleanupQueueFailed": "Nie udało się dodać usuwania osieroconych pozycji do kolejki",
|
|
54
|
+
"recent": "Ostatnie synchronizacje",
|
|
55
|
+
"empty": "Brak historii synchronizacji",
|
|
56
|
+
"successful": "zakończonych powodzeniem",
|
|
57
|
+
"failed": "nieudanych",
|
|
58
|
+
"attempts": "próby"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"facebook": {
|
|
62
|
+
"title": "Facebook Commerce",
|
|
63
|
+
"catalogId": "Identyfikator katalogu",
|
|
64
|
+
"accessToken": "Token dostępu Facebooka"
|
|
6
65
|
}
|
|
7
66
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
import { Label, Input, PageBlock, useLazyQuery, useMutation, Checkbox, Button, } from "@deenruv/react-ui-devkit";
|
|
3
|
+
import { Label, Input, PageBlock, useLazyQuery, useMutation, Checkbox, Button, useTranslation, } from "@deenruv/react-ui-devkit";
|
|
4
4
|
import { getMerchantPlatformInfo, getMerchantPlatformSettings, } from "../graphql/queries";
|
|
5
5
|
import { saveMerchantPlatformSettings, removeOrphanItems, } from "../graphql/mutations";
|
|
6
6
|
import { toast } from "sonner";
|
|
7
|
+
import { translationNS } from "../translation-ns.js";
|
|
7
8
|
export const FacebookPage = () => {
|
|
9
|
+
const { t } = useTranslation(translationNS);
|
|
8
10
|
const [fetchMerchantPlatformSettings] = useLazyQuery(getMerchantPlatformSettings);
|
|
9
11
|
const [fetchMerchantPlatformInfo] = useLazyQuery(getMerchantPlatformInfo);
|
|
10
12
|
const [mutate] = useMutation(saveMerchantPlatformSettings);
|
|
@@ -14,6 +16,7 @@ export const FacebookPage = () => {
|
|
|
14
16
|
connectionStatus: false,
|
|
15
17
|
});
|
|
16
18
|
const [isLoading, setIsLoading] = useState(true);
|
|
19
|
+
const [loadError, setLoadError] = useState(false);
|
|
17
20
|
const [settingsForm, setSettingsForm] = useState({
|
|
18
21
|
brand: "",
|
|
19
22
|
merchantId: "",
|
|
@@ -24,6 +27,7 @@ export const FacebookPage = () => {
|
|
|
24
27
|
const refetch = async () => {
|
|
25
28
|
try {
|
|
26
29
|
setIsLoading(true);
|
|
30
|
+
setLoadError(false);
|
|
27
31
|
const [settingsData, infoData] = await Promise.all([
|
|
28
32
|
fetchMerchantPlatformSettings({ platform: "facebook" }),
|
|
29
33
|
fetchMerchantPlatformInfo({ platform: "facebook" }),
|
|
@@ -55,6 +59,7 @@ export const FacebookPage = () => {
|
|
|
55
59
|
}
|
|
56
60
|
catch (error) {
|
|
57
61
|
console.error(error);
|
|
62
|
+
setLoadError(true);
|
|
58
63
|
setIsLoading(false);
|
|
59
64
|
}
|
|
60
65
|
};
|
|
@@ -74,19 +79,21 @@ export const FacebookPage = () => {
|
|
|
74
79
|
},
|
|
75
80
|
});
|
|
76
81
|
if (saveMerchantPlatformSettings) {
|
|
77
|
-
toast.success("
|
|
82
|
+
toast.success(t("common.settingsSaved"));
|
|
78
83
|
refetch();
|
|
79
84
|
}
|
|
80
85
|
else {
|
|
81
|
-
toast.error("
|
|
86
|
+
toast.error(t("common.settingsSaveFailed"));
|
|
82
87
|
}
|
|
83
88
|
}
|
|
84
89
|
catch (error) {
|
|
85
90
|
console.error(error);
|
|
86
|
-
toast.error("
|
|
91
|
+
toast.error(t("common.settingsSaveFailedWithError", {
|
|
92
|
+
message: error instanceof Error ? error.message : t("common.unknownError"),
|
|
93
|
+
}));
|
|
87
94
|
}
|
|
88
95
|
};
|
|
89
|
-
return (_jsx(PageBlock, { children: _jsxs("div", { style: { position: "relative" }, className: "flex flex-col p-4 relative max-w-[800px] mx-auto", children: [isLoading && (_jsx("div", { style: {
|
|
96
|
+
return (_jsx(PageBlock, { children: _jsxs("div", { style: { position: "relative" }, className: "flex flex-col p-4 relative max-w-[800px] mx-auto", children: [isLoading && (_jsx("div", { role: "status", "aria-label": t("common.loading"), style: {
|
|
90
97
|
position: "absolute",
|
|
91
98
|
top: 0,
|
|
92
99
|
left: 0,
|
|
@@ -104,27 +111,29 @@ export const FacebookPage = () => {
|
|
|
104
111
|
alignItems: "center",
|
|
105
112
|
width: "42px",
|
|
106
113
|
height: "42px",
|
|
107
|
-
}, className: "spinner" }) })), " ", _jsxs("form", { className: "flex flex-col gap-4", onSubmit: onSubmit, children: [_jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "
|
|
114
|
+
}, className: "spinner" }) })), _jsx("h2", { className: "mb-4 text-xl font-semibold", children: t("facebook.title") }), loadError ? (_jsx("div", { className: "mb-4 rounded border border-red-400 p-3 text-red-700", role: "alert", children: t("common.settingsLoadFailed") })) : null, _jsxs("form", { className: "flex flex-col gap-4", onSubmit: onSubmit, children: [_jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: t("common.brand") }), _jsx(Input, { className: "w-full", value: settingsForm.brand, onChange: (e) => setSettingsForm({ ...settingsForm, brand: e.target.value }) })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: t("facebook.catalogId") }), _jsx(Input, { className: "w-full", value: settingsForm.merchantId, onChange: (e) => setSettingsForm({
|
|
108
115
|
...settingsForm,
|
|
109
116
|
merchantId: e.target.value,
|
|
110
|
-
}) })] })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx(Label, { children: "
|
|
117
|
+
}) })] })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx(Label, { children: t("facebook.accessToken") }), _jsx(Input, { className: "w-full", value: settingsForm.credentials, onChange: (e) => setSettingsForm({
|
|
111
118
|
...settingsForm,
|
|
112
119
|
credentials: e.target.value,
|
|
113
120
|
}) })] }), _jsxs("div", { className: "flex gap-2 items-center", children: [_jsx(Checkbox, { id: "facebook-account-credentials", checked: settingsForm.autoUpdate, onCheckedChange: (checked) => setSettingsForm({
|
|
114
121
|
...settingsForm,
|
|
115
122
|
autoUpdate: typeof checked === "boolean" ? checked : false,
|
|
116
|
-
}) }), _jsx(Label, { htmlFor: "facebook-account-credentials", children: "
|
|
123
|
+
}) }), _jsx(Label, { htmlFor: "facebook-account-credentials", children: t("common.autoUpdate") })] })] }), _jsx("div", { className: "flex justify-end", children: _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex gap-2", children: [_jsx(Label, { htmlFor: "auto-update-on-products-change", children: t("common.updateAllOnSave") }), _jsx(Checkbox, { id: "auto-update-on-products-change", checked: settingsForm.firstSync, onCheckedChange: (checked) => setSettingsForm({
|
|
117
124
|
...settingsForm,
|
|
118
125
|
firstSync: typeof checked === "boolean" ? checked : false,
|
|
119
|
-
}) })] }), _jsx(Button, { children: "
|
|
126
|
+
}) })] }), _jsx(Button, { children: t("common.save") })] }) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("span", { children: t("common.connectionStatus") }), _jsx("strong", { children: serviceInfo.connectionStatus
|
|
127
|
+
? t("common.connected")
|
|
128
|
+
: t("common.disconnected") })] }), serviceInfo.connectionStatus ? (_jsx("div", { className: "mt-8", children: _jsx(Button, { onClick: async () => {
|
|
120
129
|
try {
|
|
121
130
|
await removeOldItems({ platform: "facebook" });
|
|
122
|
-
toast.success("
|
|
131
|
+
toast.success(t("common.oldItemsRemoved"));
|
|
123
132
|
refetch();
|
|
124
133
|
}
|
|
125
134
|
catch (error) {
|
|
126
135
|
console.error(error);
|
|
127
|
-
toast.error("
|
|
136
|
+
toast.error(t("common.oldItemsRemoveFailed"));
|
|
128
137
|
}
|
|
129
|
-
}, children: "
|
|
138
|
+
}, children: t("common.removeOldItems") }) })) : null] }) }));
|
|
130
139
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
import { Label, Input, PageBlock, useLazyQuery, useMutation, Checkbox, Button, } from "@deenruv/react-ui-devkit";
|
|
3
|
+
import { Label, Input, PageBlock, useLazyQuery, useMutation, Checkbox, Button, useTranslation, } from "@deenruv/react-ui-devkit";
|
|
4
4
|
import { getGoogleMerchantDiagnostic, getGoogleMerchantSyncHistory, getMerchantPlatformSettings, } from "../graphql/queries";
|
|
5
5
|
import { removeOrphanItems, saveMerchantPlatformSettings, sendAllProductsToMerchantPlatform, } from "../graphql/mutations";
|
|
6
6
|
import { toast } from "sonner";
|
|
7
|
+
import { translationNS } from "../translation-ns.js";
|
|
7
8
|
export const GooglePage = () => {
|
|
9
|
+
const { t, i18n } = useTranslation(translationNS);
|
|
8
10
|
const [fetchMerchantPlatformSettings] = useLazyQuery(getMerchantPlatformSettings);
|
|
9
11
|
const [fetchMerchantPlatformInfo] = useLazyQuery(getGoogleMerchantDiagnostic);
|
|
10
12
|
const [fetchSyncHistory] = useLazyQuery(getGoogleMerchantSyncHistory);
|
|
@@ -32,16 +34,19 @@ export const GooglePage = () => {
|
|
|
32
34
|
}
|
|
33
35
|
catch (error) {
|
|
34
36
|
console.error("Invalid JSON file:", error);
|
|
37
|
+
toast.error(t("google.invalidCredentialsFile"));
|
|
35
38
|
}
|
|
36
39
|
};
|
|
37
40
|
reader.onerror = (e) => {
|
|
38
41
|
console.error("Error reading file", e);
|
|
42
|
+
toast.error(t("google.credentialsReadFailed"));
|
|
39
43
|
};
|
|
40
44
|
if (file)
|
|
41
45
|
reader.readAsText(file);
|
|
42
46
|
}
|
|
43
47
|
};
|
|
44
48
|
const [isLoading, setIsLoading] = useState(true);
|
|
49
|
+
const [loadError, setLoadError] = useState(false);
|
|
45
50
|
const [settingsForm, setSettingsForm] = useState({
|
|
46
51
|
brand: "",
|
|
47
52
|
merchantId: "",
|
|
@@ -55,6 +60,7 @@ export const GooglePage = () => {
|
|
|
55
60
|
const refetch = async () => {
|
|
56
61
|
try {
|
|
57
62
|
setIsLoading(true);
|
|
63
|
+
setLoadError(false);
|
|
58
64
|
const [settingsData, infoData, historyData] = await Promise.all([
|
|
59
65
|
fetchMerchantPlatformSettings({ platform: "google" }),
|
|
60
66
|
fetchMerchantPlatformInfo(),
|
|
@@ -99,6 +105,7 @@ export const GooglePage = () => {
|
|
|
99
105
|
}
|
|
100
106
|
catch (error) {
|
|
101
107
|
console.error(error);
|
|
108
|
+
setLoadError(true);
|
|
102
109
|
setIsLoading(false);
|
|
103
110
|
}
|
|
104
111
|
};
|
|
@@ -118,19 +125,21 @@ export const GooglePage = () => {
|
|
|
118
125
|
},
|
|
119
126
|
});
|
|
120
127
|
if (saveMerchantPlatformSettings) {
|
|
121
|
-
toast.success("
|
|
128
|
+
toast.success(t("common.settingsSaved"));
|
|
122
129
|
refetch();
|
|
123
130
|
}
|
|
124
131
|
else {
|
|
125
|
-
toast.error("
|
|
132
|
+
toast.error(t("common.settingsSaveFailed"));
|
|
126
133
|
}
|
|
127
134
|
}
|
|
128
135
|
catch (error) {
|
|
129
136
|
console.error(error);
|
|
130
|
-
toast.error(
|
|
137
|
+
toast.error(t("common.settingsSaveFailedWithError", {
|
|
138
|
+
message: error instanceof Error ? error.message : t("common.unknownError"),
|
|
139
|
+
}));
|
|
131
140
|
}
|
|
132
141
|
};
|
|
133
|
-
return (_jsx(PageBlock, { children: _jsxs("div", { style: { position: "relative" }, className: "flex flex-col p-4 relative max-w-[800px] mx-auto", children: [isLoading && (_jsx("div", { style: {
|
|
142
|
+
return (_jsx(PageBlock, { children: _jsxs("div", { style: { position: "relative" }, className: "flex flex-col p-4 relative max-w-[800px] mx-auto", children: [isLoading && (_jsx("div", { role: "status", "aria-label": t("common.loading"), style: {
|
|
134
143
|
position: "absolute",
|
|
135
144
|
top: 0,
|
|
136
145
|
left: 0,
|
|
@@ -148,53 +157,57 @@ export const GooglePage = () => {
|
|
|
148
157
|
alignItems: "center",
|
|
149
158
|
width: "42px",
|
|
150
159
|
height: "42px",
|
|
151
|
-
}, className: "spinner" }) })), _jsxs("form", { className: "flex flex-col gap-4", onSubmit: onSubmit, children: [_jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "
|
|
160
|
+
}, className: "spinner" }) })), _jsx("h2", { className: "mb-4 text-xl font-semibold", children: t("google.title") }), loadError ? (_jsx("div", { className: "mb-4 rounded border border-red-400 p-3 text-red-700", role: "alert", children: t("common.settingsLoadFailed") })) : null, _jsxs("form", { className: "flex flex-col gap-4", onSubmit: onSubmit, children: [_jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: t("common.brand") }), _jsx(Input, { className: "w-full", value: settingsForm.brand, onChange: (e) => setSettingsForm({ ...settingsForm, brand: e.target.value }) })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: t("google.merchantId") }), _jsx(Input, { className: "w-full", required: true, value: settingsForm.merchantId, onChange: (e) => setSettingsForm({
|
|
152
161
|
...settingsForm,
|
|
153
162
|
merchantId: e.target.value,
|
|
154
|
-
}) })] })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "
|
|
163
|
+
}) })] })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: t("google.dataSource") }), _jsx(Input, { className: "w-full", placeholder: "accounts/{merchantId}/dataSources/{id}", required: true, value: settingsForm.dataSource, onChange: (e) => setSettingsForm({
|
|
155
164
|
...settingsForm,
|
|
156
165
|
dataSource: e.target.value,
|
|
157
|
-
}) })] }), _jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "
|
|
166
|
+
}) })] }), _jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: t("google.contentLanguage") }), _jsx(Input, { required: true, value: settingsForm.contentLanguage, onChange: (e) => setSettingsForm({
|
|
158
167
|
...settingsForm,
|
|
159
168
|
contentLanguage: e.target.value,
|
|
160
|
-
}) })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "
|
|
169
|
+
}) })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: t("google.feedLabel") }), _jsx(Input, { required: true, value: settingsForm.feedLabel, onChange: (e) => setSettingsForm({
|
|
161
170
|
...settingsForm,
|
|
162
171
|
feedLabel: e.target.value,
|
|
163
|
-
}) })] })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx(Label, { children: "
|
|
172
|
+
}) })] })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx(Label, { children: t("google.credentials") }), _jsx(Input, { style: {
|
|
164
173
|
border: "none",
|
|
165
174
|
backgroundColor: "transparent",
|
|
166
175
|
background: "none",
|
|
167
176
|
}, type: "file", accept: ".json", onChange: handleSelectedFile })] }), _jsxs("div", { className: "flex gap-2 items-center", children: [_jsx(Checkbox, { id: "google-account-credentials", checked: settingsForm.autoUpdate, onCheckedChange: (checked) => setSettingsForm({
|
|
168
177
|
...settingsForm,
|
|
169
178
|
autoUpdate: typeof checked === "boolean" ? checked : false,
|
|
170
|
-
}) }), _jsx(Label, { htmlFor: "google-account-credentials", children: "
|
|
179
|
+
}) }), _jsx(Label, { htmlFor: "google-account-credentials", children: t("common.autoUpdate") })] })] }), _jsx("div", { className: "flex justify-end", children: _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex gap-2", children: [_jsx(Label, { htmlFor: "auto-update-on-products-change", children: t("common.updateAllOnSave") }), _jsx(Checkbox, { id: "auto-update-on-products-change", checked: settingsForm.firstSync, onCheckedChange: (checked) => setSettingsForm({
|
|
171
180
|
...settingsForm,
|
|
172
181
|
firstSync: typeof checked === "boolean" ? checked : false,
|
|
173
|
-
}) })] }), _jsx(Button, { children: "
|
|
182
|
+
}) })] }), _jsx(Button, { children: t("common.save") })] }) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("span", { children: t("common.connectionStatus") }), _jsx("strong", { children: serviceInfo.connectionStatus
|
|
183
|
+
? t("common.connected")
|
|
184
|
+
: t("common.disconnected") })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("span", { children: t("common.productsCount") }), _jsx("span", { children: serviceInfo.productsCount })] }), _jsxs("div", { className: "flex flex-col gap-1 text-sm", children: [_jsx("h3", { className: "font-semibold", children: t("google.diagnostics.title") }), _jsxs("span", { children: [t("google.diagnostics.status"), ":", " ", diagnostic?.connectionStatus ?? t("google.diagnostics.unknown")] }), _jsxs("span", { children: [t("google.diagnostics.dataSourceVerified"), ":", " ", diagnostic?.dataSourceVerified
|
|
185
|
+
? t("google.diagnostics.yes")
|
|
186
|
+
: t("google.diagnostics.no")] }), _jsxs("span", { children: [t("google.diagnostics.latency"), ": ", diagnostic?.latencyMs ?? 0, " ms"] }), _jsxs("span", { children: [t("google.diagnostics.disapprovedProducts"), ":", " ", diagnostic?.disapprovedProductsCount ?? 0] }), _jsxs("span", { children: [t("google.diagnostics.productIssues"), ":", " ", diagnostic?.issuesCount ?? 0] }), diagnostic?.lastError ? (_jsxs("div", { className: "rounded border border-red-400 p-2 text-red-700", children: [diagnostic.lastError.code, ": ", diagnostic.lastError.message] })) : null, (diagnostic?.issues ?? []).slice(0, 20).map((issue) => (_jsxs("div", { className: "rounded border border-amber-400 p-2", children: [issue.offerId, " \u00B7 ", issue.severity, " \u00B7 ", issue.code, ":", " ", issue.description] }, `${issue.offerId}-${issue.code}`)))] }), _jsxs("div", { className: "mt-4 flex gap-2", children: [_jsx(Button, { type: "button", onClick: async () => {
|
|
174
187
|
await refetch();
|
|
175
|
-
toast.success("
|
|
176
|
-
}, children: "
|
|
188
|
+
toast.success(t("google.diagnostics.completed"));
|
|
189
|
+
}, children: t("google.diagnostics.test") }), _jsx(Button, { type: "button", onClick: async () => {
|
|
177
190
|
try {
|
|
178
191
|
await sendAllProducts({ platform: "google" });
|
|
179
|
-
toast.success("
|
|
192
|
+
toast.success(t("google.sync.queued"));
|
|
180
193
|
await refetch();
|
|
181
194
|
}
|
|
182
195
|
catch (error) {
|
|
183
196
|
console.error(error);
|
|
184
|
-
toast.error("
|
|
197
|
+
toast.error(t("google.sync.queueFailed"));
|
|
185
198
|
}
|
|
186
|
-
}, children: "
|
|
199
|
+
}, children: t("google.sync.all") })] }), serviceInfo.connectionStatus ? (_jsx("div", { className: "mt-8", children: _jsx(Button, { onClick: async () => {
|
|
187
200
|
try {
|
|
188
201
|
await removeOldItems({ platform: "google" });
|
|
189
|
-
toast.success("
|
|
202
|
+
toast.success(t("google.sync.cleanupQueued"));
|
|
190
203
|
refetch();
|
|
191
204
|
}
|
|
192
205
|
catch (error) {
|
|
193
206
|
console.error(error);
|
|
194
|
-
toast.error("
|
|
207
|
+
toast.error(t("google.sync.cleanupQueueFailed"));
|
|
195
208
|
}
|
|
196
|
-
}, children: "
|
|
209
|
+
}, children: t("common.removeOldItems") }) })) : null, _jsxs("div", { className: "mt-8 flex flex-col gap-2", children: [_jsx("h3", { className: "font-semibold", children: t("google.sync.recent") }), syncHistory.length === 0 ? (_jsx("span", { children: t("google.sync.empty") })) : (syncHistory.map((run) => (_jsxs("div", { className: "rounded border p-2 text-sm", children: [_jsxs("div", { children: [run.status, " \u00B7 ", run.succeeded, "/", run.total, " ", t("google.sync.successful"), " \u00B7 ", run.failed, " ", t("google.sync.failed")] }), _jsx("div", { children: new Date(run.createdAt).toLocaleString(i18n.language) }), run.errorSummary ? (_jsx("div", { className: "text-red-700", children: run.errorSummary })) : null, run.items
|
|
197
210
|
.filter((item) => item.status === "FAILED")
|
|
198
211
|
.slice(0, 20)
|
|
199
|
-
.map((item) => (_jsxs("div", { className: "mt-1 text-red-700", children: [item.offerId, " \u00B7 ", item.errorCode ?? "ERROR", " \u00B7 attempts
|
|
212
|
+
.map((item) => (_jsxs("div", { className: "mt-1 text-red-700", children: [item.offerId, " \u00B7 ", item.errorCode ?? "ERROR", " \u00B7", " ", t("google.sync.attempts"), ": ", item.attempts, " \u00B7", " ", item.errorMessage] }, `${run.id}-${item.offerId}-${item.operation}`)))] }, run.id))))] })] }) }));
|
|
200
213
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath, URL } from "node:url";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
const readJson = (relativePath) => JSON.parse(readFileSync(fileURLToPath(new URL(relativePath, import.meta.url)), "utf8"));
|
|
5
|
+
const flattenKeys = (tree, prefix = "") => Object.entries(tree).flatMap(([key, value]) => {
|
|
6
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
7
|
+
return typeof value === "string" ? [path] : flattenKeys(value, path);
|
|
8
|
+
});
|
|
9
|
+
const source = ["./pages/GooglePage.tsx", "./pages/FacebookPage.tsx"]
|
|
10
|
+
.map((relativePath) => readFileSync(fileURLToPath(new URL(relativePath, import.meta.url)), "utf8"))
|
|
11
|
+
.join("\n");
|
|
12
|
+
describe("merchant plugin translations", () => {
|
|
13
|
+
const en = readJson("./locales/en/merchant.json");
|
|
14
|
+
const pl = readJson("./locales/pl/merchant.json");
|
|
15
|
+
it("keeps complete recursive EN/PL key parity", () => {
|
|
16
|
+
expect(flattenKeys(pl).sort()).toEqual(flattenKeys(en).sort());
|
|
17
|
+
});
|
|
18
|
+
it("defines every statically referenced translation key", () => {
|
|
19
|
+
const referencedKeys = Array.from(source.matchAll(/\bt\("([^"]+)"/g), (match) => match[1]);
|
|
20
|
+
const availableKeys = new Set(flattenKeys(en));
|
|
21
|
+
expect(referencedKeys.length).toBeGreaterThan(0);
|
|
22
|
+
expect(referencedKeys.filter((key) => !availableKeys.has(key))).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
it("preserves platform brands and translates the Polish navigation group", () => {
|
|
25
|
+
expect(en.nav).toMatchObject({
|
|
26
|
+
googleMerchant: "Google Merchant",
|
|
27
|
+
facebookCommerce: "Facebook Commerce",
|
|
28
|
+
});
|
|
29
|
+
expect(pl.nav).toMatchObject({
|
|
30
|
+
googleMerchant: "Google Merchant",
|
|
31
|
+
facebookCommerce: "Facebook Commerce",
|
|
32
|
+
merchantPlatforms: "Platformy sprzedażowe",
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
it("formats Google synchronization timestamps in the active UI language", () => {
|
|
36
|
+
expect(source).toContain("toLocaleString(i18n.language)");
|
|
37
|
+
});
|
|
38
|
+
it("does not reintroduce audited hard-coded shell copy", () => {
|
|
39
|
+
for (const literal of [
|
|
40
|
+
">Brand<",
|
|
41
|
+
">Save<",
|
|
42
|
+
">Connection status<",
|
|
43
|
+
">Remove old items<",
|
|
44
|
+
'toast.success("Settings saved successfully")',
|
|
45
|
+
'toast.error("Failed to save settings")',
|
|
46
|
+
">Recent synchronizations<",
|
|
47
|
+
">No synchronization history<",
|
|
48
|
+
]) {
|
|
49
|
+
expect(source).not.toContain(literal);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deenruv/merchant-plugin",
|
|
3
|
-
"version": "1.0.17-dev.
|
|
3
|
+
"version": "1.0.17-dev.32",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"react-i18next": "^14.0.5",
|
|
33
33
|
"recharts": "^2.12.7",
|
|
34
34
|
"sonner": "^1.4.41",
|
|
35
|
-
"@deenruv/admin-
|
|
36
|
-
"@deenruv/common": "^1.0.17-dev.
|
|
37
|
-
"@deenruv/ui-devkit": "^1.0.17-dev.
|
|
38
|
-
"@deenruv/admin-
|
|
39
|
-
"@deenruv/react-ui-devkit": "^1.0.17-dev.
|
|
35
|
+
"@deenruv/admin-types": "^1.0.17-dev.32",
|
|
36
|
+
"@deenruv/common": "^1.0.17-dev.32",
|
|
37
|
+
"@deenruv/ui-devkit": "^1.0.17-dev.32",
|
|
38
|
+
"@deenruv/admin-ui": "^1.0.17-dev.32",
|
|
39
|
+
"@deenruv/react-ui-devkit": "^1.0.17-dev.32"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@graphql-typed-document-node/core": "3.2.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"rimraf": "^5.0.10",
|
|
49
49
|
"tslib": "^2.6.2",
|
|
50
50
|
"typescript": "5.3.3",
|
|
51
|
-
"@deenruv/core": "^1.0.17-dev.
|
|
51
|
+
"@deenruv/core": "^1.0.17-dev.32"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@deenruv/core": "^1.0.0 || ^1.0.17-dev.0"
|