@parhelia/localization 0.1.12556 → 0.1.12560

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/LocalizeItemDialog.d.ts.map +1 -1
  2. package/dist/LocalizeItemDialog.js +13 -17
  3. package/dist/api/discovery.d.ts +1 -4
  4. package/dist/api/discovery.d.ts.map +1 -1
  5. package/dist/api/discovery.js +3 -4
  6. package/dist/hooks/useTranslationWizard.d.ts.map +1 -1
  7. package/dist/hooks/useTranslationWizard.js +3 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +13 -28
  10. package/dist/services/translationService.d.ts +48 -7
  11. package/dist/services/translationService.d.ts.map +1 -1
  12. package/dist/services/translationService.js +3 -0
  13. package/dist/settings/TranslationServicesPanel.d.ts.map +1 -1
  14. package/dist/settings/TranslationServicesPanel.js +111 -42
  15. package/dist/setup/LocalizationSetupStep.d.ts.map +1 -1
  16. package/dist/setup/LocalizationSetupStep.js +8 -7
  17. package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
  18. package/dist/steps/ServiceLanguageSelectionStep.js +18 -1
  19. package/dist/steps/SubitemDiscoveryStep.d.ts.map +1 -1
  20. package/dist/steps/SubitemDiscoveryStep.js +95 -181
  21. package/dist/translation-center/BatchTranslationView.d.ts +1 -1
  22. package/dist/translation-center/BatchTranslationView.d.ts.map +1 -1
  23. package/dist/translation-center/BatchTranslationView.js +354 -96
  24. package/dist/translation-center/RecentTranslations.d.ts.map +1 -1
  25. package/dist/translation-center/RecentTranslations.js +20 -13
  26. package/dist/translation-center/TranslationManagement.d.ts.map +1 -1
  27. package/dist/translation-center/TranslationManagement.js +2 -4
  28. package/package.json +10 -11
@@ -1,13 +1,14 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useState, useEffect } from "react";
3
- import { Button, useEditContext, Splitter, ItemConfigPanel } from "@parhelia/core";
4
- import { CheckCircle as LucideCheckCircle, AlertCircle as LucideAlertCircle, RefreshCw as LucideRefreshCw, Plus as LucidePlus, Settings as LucideSettings, } from "lucide-react";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useState, useEffect, } from "react";
3
+ import { Button, useEditContext, Splitter, ItemConfigPanel, } from "@parhelia/core";
4
+ import { CheckCircle as LucideCheckCircle, AlertCircle as LucideAlertCircle, RefreshCw as LucideRefreshCw, Plus as LucidePlus, Settings as LucideSettings, Trash2 as LucideTrash2, } from "lucide-react";
5
5
  import { getAvailableTranslationServices, createProviderSettings, getTranslationStructure, ensureTranslationStructure, } from "../services/translationService";
6
6
  const CheckCircleIcon = LucideCheckCircle;
7
7
  const AlertCircleIcon = LucideAlertCircle;
8
8
  const RefreshCwIcon = LucideRefreshCw;
9
9
  const PlusIcon = LucidePlus;
10
10
  const SettingsIcon = LucideSettings;
11
+ const Trash2Icon = LucideTrash2;
11
12
  const DEFAULT_STRUCTURE_SETTINGS = {
12
13
  translationFolderPath: "/sitecore/system/Parhelia/Settings/Translation",
13
14
  translationFolderTemplateId: "b1e40cfe-6e36-4a0e-a49e-19c10b2127b7",
@@ -52,13 +53,13 @@ export function TranslationServicesPanel() {
52
53
  setStructureLoading(true);
53
54
  const result = await getTranslationStructure(structureSettings);
54
55
  if (result.type === "success" && result.data) {
55
- const success = result.data.success !== false;
56
- const response = result.data.success !== undefined ? result.data.data : result.data;
57
- if (success && response) {
58
- setStructureState(response);
56
+ if (result.data.success && result.data.data) {
57
+ setStructureState(result.data.data);
59
58
  }
60
59
  else {
61
- throw new Error(result.data.error || result.details || "Failed to read translation structure");
60
+ throw new Error(result.data.error ||
61
+ result.details ||
62
+ "Failed to read translation structure");
62
63
  }
63
64
  }
64
65
  else {
@@ -78,13 +79,13 @@ export function TranslationServicesPanel() {
78
79
  setError(null);
79
80
  const result = await ensureTranslationStructure(structureSettings);
80
81
  if (result.type === "success" && result.data) {
81
- const success = result.data.success !== false;
82
- const response = result.data.success !== undefined ? result.data.data : result.data;
83
- if (success && response) {
84
- setStructureState(response);
82
+ if (result.data.success && result.data.data) {
83
+ setStructureState(result.data.data);
85
84
  return true;
86
85
  }
87
- throw new Error(result.data.error || result.details || "Failed to create translation structure");
86
+ throw new Error(result.data.error ||
87
+ result.details ||
88
+ "Failed to create translation structure");
88
89
  }
89
90
  throw new Error(result.details || "Failed to create translation structure");
90
91
  }
@@ -103,20 +104,24 @@ export function TranslationServicesPanel() {
103
104
  await loadStructure();
104
105
  const result = await getAvailableTranslationServices();
105
106
  if (result.type === "success" && result.data) {
106
- // Backend returns ApiResponse<T>, so data is wrapped: { success: true, data: [...] }
107
- const availableServices = Array.isArray(result.data)
108
- ? result.data
109
- : result.data.data || [];
107
+ if (!result.data.success || !result.data.data) {
108
+ throw new Error(result.data.error ||
109
+ result.details ||
110
+ "Failed to load translation services. The translation API may not be available.");
111
+ }
112
+ const availableServices = result.data.data;
110
113
  const serviceStates = availableServices.map((svc) => ({
111
114
  ...svc,
112
- creating: false
115
+ creating: false,
116
+ deleting: false,
113
117
  }));
114
118
  setServices(serviceStates);
115
119
  setState(availableServices.length === 0 ? "error" : "success");
116
120
  }
117
121
  else {
118
122
  setState("error");
119
- setError(result.details || "Failed to load translation services. The translation API may not be available.");
123
+ setError(result.details ||
124
+ "Failed to load translation services. The translation API may not be available.");
120
125
  }
121
126
  }
122
127
  catch (e) {
@@ -125,32 +130,38 @@ export function TranslationServicesPanel() {
125
130
  }
126
131
  }, [loadStructure]);
127
132
  const handleStructureInputChange = useCallback((field) => (e) => {
128
- setStructureSettings(prev => ({
133
+ setStructureSettings((prev) => ({
129
134
  ...prev,
130
135
  [field]: e.target.value,
131
136
  }));
132
137
  }, []);
133
138
  const createSettings = useCallback(async (serviceName, templateId) => {
134
139
  try {
135
- setServices(prev => prev.map(s => s.serviceName === serviceName ? { ...s, creating: true } : s));
140
+ setServices((prev) => prev.map((s) => s.serviceName === serviceName ? { ...s, creating: true } : s));
136
141
  setError(null);
137
142
  const language = editContext?.item?.language ??
138
143
  editContext?.currentItemDescriptor?.language ??
139
144
  "en";
140
145
  const result = await createProviderSettings(serviceName, templateId, language, structureSettings);
141
146
  if (result.type === "success" && result.data) {
142
- // Backend returns ApiResponse<T>, so unwrap: { success: true, data: { itemId, itemPath } }
143
- const response = result.data.success ? result.data.data : result.data;
144
- const success = result.data.success !== false;
145
- if (success && response) {
146
- setServices(prev => prev.map(s => s.serviceName === serviceName
147
- ? { ...s, isConfigured: true, creating: false, settingsItemId: response.itemId }
147
+ const payload = result.data.data;
148
+ if (result.data.success && payload) {
149
+ const settingsItemId = payload.itemId;
150
+ setServices((prev) => prev.map((s) => s.serviceName === serviceName
151
+ ? {
152
+ ...s,
153
+ isConfigured: true,
154
+ creating: false,
155
+ settingsItemId,
156
+ }
148
157
  : s));
149
158
  // Reload data to ensure consistency
150
159
  setTimeout(() => loadData(), 500);
151
160
  }
152
161
  else {
153
- throw new Error(result.data.error || result.details || "Failed to create settings");
162
+ throw new Error(result.data.error ||
163
+ result.details ||
164
+ "Failed to create settings");
154
165
  }
155
166
  }
156
167
  else {
@@ -163,7 +174,8 @@ export function TranslationServicesPanel() {
163
174
  let displayMessage = errorMessage;
164
175
  if (errorMessage.includes("Translation Providers folder not found") ||
165
176
  errorMessage.includes("Translation folder not found")) {
166
- displayMessage = "The required folder structure is missing. Configure and create the parent items in the Settings section above.";
177
+ displayMessage =
178
+ "The required folder structure is missing. Configure and create the parent items in the Settings section above.";
167
179
  }
168
180
  else if (errorMessage.includes("already exists")) {
169
181
  displayMessage = `Settings for ${serviceName} already exist. Please refresh the list.`;
@@ -171,41 +183,98 @@ export function TranslationServicesPanel() {
171
183
  setTimeout(() => loadData(), 500);
172
184
  }
173
185
  setError(`Failed to create settings for ${serviceName}: ${displayMessage}`);
174
- setServices(prev => prev.map(s => s.serviceName === serviceName ? { ...s, creating: false } : s));
186
+ setServices((prev) => prev.map((s) => s.serviceName === serviceName ? { ...s, creating: false } : s));
175
187
  }
176
188
  }, [editContext, loadData, structureSettings]);
189
+ const deleteSettings = useCallback(async (service) => {
190
+ if (!editContext || !service.settingsItemId)
191
+ return;
192
+ try {
193
+ setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
194
+ ? { ...s, deleting: true }
195
+ : s));
196
+ setError(null);
197
+ const language = editContext.item?.language ??
198
+ editContext.currentItemDescriptor?.language ??
199
+ "en";
200
+ const deleted = await editContext.operations.deleteItems([
201
+ {
202
+ id: service.settingsItemId,
203
+ language,
204
+ version: 0,
205
+ },
206
+ ]);
207
+ if (deleted) {
208
+ if (selectedConfigTarget?.key === `service:${service.serviceName}` ||
209
+ selectedConfigTarget?.itemId === service.settingsItemId) {
210
+ setSelectedConfigTarget(null);
211
+ }
212
+ setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
213
+ ? {
214
+ ...s,
215
+ isConfigured: false,
216
+ settingsItemId: undefined,
217
+ deleting: false,
218
+ }
219
+ : s));
220
+ await loadData();
221
+ }
222
+ else {
223
+ setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
224
+ ? { ...s, deleting: false }
225
+ : s));
226
+ }
227
+ }
228
+ catch (e) {
229
+ setError(e?.message ||
230
+ `Failed to delete settings for ${service.displayName || service.serviceName}`);
231
+ setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
232
+ ? { ...s, deleting: false }
233
+ : s));
234
+ }
235
+ }, [editContext, loadData, selectedConfigTarget]);
177
236
  useEffect(() => {
178
237
  loadData();
179
238
  }, [loadData]);
180
239
  const statusIcon = (currentState) => {
181
240
  if (currentState === "success")
182
- return _jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600", strokeWidth: 1 });
241
+ return (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600", strokeWidth: 1 }));
183
242
  if (currentState === "error")
184
- return _jsx(AlertCircleIcon, { className: "h-4 w-4 text-red-600", strokeWidth: 1 });
243
+ return (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-red-600", strokeWidth: 1 }));
185
244
  return (_jsx(RefreshCwIcon, { className: "h-4 w-4 animate-spin text-amber-600", strokeWidth: 1 }));
186
245
  };
187
- const configuredCount = services.filter(s => s.isConfigured).length;
246
+ const configuredCount = services.filter((s) => s.isConfigured).length;
188
247
  const totalCount = services.length;
189
248
  const translationFolderExists = !!structureState?.translationFolder?.exists;
190
249
  const providersFolderExists = !!structureState?.translationProvidersFolder?.exists;
191
250
  // Build the services list content
192
251
  const servicesListContent = (_jsx("div", { className: "h-full overflow-auto p-4", children: _jsx("div", { className: "mx-auto", children: _jsx("div", { className: "rounded-lg bg-white p-4 md:p-6", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-gray-900", children: "Prerequisites" }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [translationFolderExists && providersFolderExists ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600", strokeWidth: 1 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500", strokeWidth: 1 })), _jsx("span", { className: "text-sm text-gray-700", children: translationFolderExists && providersFolderExists
193
252
  ? "All parent items found"
194
- : "Parent items missing" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: loadStructure, disabled: structureLoading || ensuringStructure, children: [_jsx(RefreshCwIcon, { strokeWidth: 1, className: `h-4 w-4 ${structureLoading ? "animate-spin" : ""}` }), "Refresh"] }), !providersFolderExists && (_jsxs(Button, { size: "sm", onClick: ensureStructure, disabled: ensuringStructure || structureLoading, children: [ensuringStructure ? (_jsx(RefreshCwIcon, { strokeWidth: 1, className: "h-4 w-4 animate-spin" })) : (_jsx(PlusIcon, { strokeWidth: 1.5, className: "h-4 w-4" })), "Ensure Items"] }))] })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: `flex items-center justify-between rounded border p-3 text-sm transition-shadow hover:shadow-sm ${selectedConfigTarget?.key === `structure:${structureState?.translationFolder?.itemId}`
253
+ : "Parent items missing" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: loadStructure, disabled: structureLoading || ensuringStructure, children: [_jsx(RefreshCwIcon, { strokeWidth: 1, className: `h-4 w-4 ${structureLoading ? "animate-spin" : ""}` }), "Refresh"] }), !providersFolderExists && (_jsxs(Button, { size: "sm", onClick: ensureStructure, disabled: ensuringStructure || structureLoading, children: [ensuringStructure ? (_jsx(RefreshCwIcon, { strokeWidth: 1, className: "h-4 w-4 animate-spin" })) : (_jsx(PlusIcon, { strokeWidth: 1.5, className: "h-4 w-4" })), "Ensure Items"] }))] })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: `flex items-center justify-between rounded border p-3 text-sm transition-shadow hover:shadow-sm ${selectedConfigTarget?.key ===
254
+ `structure:${structureState?.translationFolder?.itemId}`
195
255
  ? "border-blue-400 bg-blue-50"
196
- : "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1", children: [translationFolderExists ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600 flex-shrink-0", strokeWidth: 1.5 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500 flex-shrink-0", strokeWidth: 1.5 })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-gray-900", children: "Translation folder" }), _jsx("div", { className: "text-xs text-gray-500 truncate", children: structureSettings.translationFolderPath })] })] }), _jsx("div", { className: "flex items-center gap-2 ml-4", children: translationFolderExists && structureState?.translationFolder?.itemId && (_jsxs(Button, { size: "sm", variant: selectedConfigTarget?.key === `structure:${structureState.translationFolder.itemId}`
256
+ : "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0", children: [translationFolderExists ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600 shrink-0", strokeWidth: 1.5 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500 shrink-0", strokeWidth: 1.5 })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-gray-900", children: "Translation folder" }), _jsx("div", { className: "text-xs text-gray-500 truncate", children: structureSettings.translationFolderPath })] })] }), _jsx("div", { className: "flex items-center gap-2 ml-4 shrink-0", children: translationFolderExists &&
257
+ structureState?.translationFolder?.itemId && (_jsxs(Button, { size: "sm", variant: selectedConfigTarget?.key ===
258
+ `structure:${structureState.translationFolder.itemId}`
197
259
  ? "default"
198
- : "ghost", onClick: () => handleOpenStructureConfig(structureState.translationFolder.itemId, "Configure: Translation folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), "Configure"] })) })] }), _jsxs("div", { className: `flex items-center justify-between rounded border p-3 text-sm transition-shadow hover:shadow-sm ${selectedConfigTarget?.key === `structure:${structureState?.translationProvidersFolder?.itemId}`
260
+ : "ghost", onClick: () => handleOpenStructureConfig(structureState.translationFolder.itemId, "Configure: Translation folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), editContext?.isMobile ? "" : "Configure"] })) })] }), _jsxs("div", { className: `flex items-center justify-between rounded border p-3 text-sm transition-shadow hover:shadow-sm ${selectedConfigTarget?.key ===
261
+ `structure:${structureState?.translationProvidersFolder?.itemId}`
199
262
  ? "border-blue-400 bg-blue-50"
200
- : "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1", children: [providersFolderExists ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600 flex-shrink-0", strokeWidth: 1.5 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500 flex-shrink-0", strokeWidth: 1.5 })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-gray-900", children: "Translation providers folder" }), _jsx("div", { className: "text-xs text-gray-500 truncate", children: structureSettings.translationProvidersPath })] })] }), _jsx("div", { className: "flex items-center gap-2 ml-4", children: providersFolderExists && structureState?.translationProvidersFolder?.itemId && (_jsxs(Button, { size: "sm", variant: selectedConfigTarget?.key === `structure:${structureState.translationProvidersFolder.itemId}`
263
+ : "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0", children: [providersFolderExists ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600 shrink-0", strokeWidth: 1.5 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500 shrink-0", strokeWidth: 1.5 })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-gray-900", children: "Translation providers folder" }), _jsx("div", { className: "text-xs text-gray-500 truncate", children: structureSettings.translationProvidersPath })] })] }), _jsx("div", { className: "flex items-center gap-2 ml-4 shrink-0", children: providersFolderExists &&
264
+ structureState?.translationProvidersFolder?.itemId && (_jsxs(Button, { size: "sm", variant: selectedConfigTarget?.key ===
265
+ `structure:${structureState.translationProvidersFolder.itemId}`
201
266
  ? "default"
202
- : "ghost", onClick: () => handleOpenStructureConfig(structureState.translationProvidersFolder.itemId, "Configure: Translation Providers folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), "Configure"] })) })] })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-gray-900", children: "Providers" }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [statusIcon(state), _jsx("span", { className: "text-sm text-gray-700", children: totalCount > 0
267
+ : "ghost", onClick: () => handleOpenStructureConfig(structureState.translationProvidersFolder.itemId, "Configure: Translation Providers folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), editContext?.isMobile ? "" : "Configure"] })) })] })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-gray-900", children: "Providers" }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [statusIcon(state), _jsx("span", { className: "text-sm text-gray-700", children: totalCount > 0
203
268
  ? `${configuredCount} of ${totalCount} service${totalCount !== 1 ? "s" : ""} configured`
204
269
  : "No services found" })] })] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: loadData, children: [_jsx(RefreshCwIcon, { strokeWidth: 1, className: "h-4 w-4" }), "Refresh"] })] }), totalCount === 0 && state === "success" && (_jsx("div", { className: "rounded border border-yellow-200 bg-yellow-50 p-3 text-sm text-yellow-800", children: "No translation services are registered in dependency injection. Please check the server configuration." })), services.length > 0 && (_jsx("div", { className: "space-y-1", children: services.map((service) => {
205
- const isSelected = selectedConfigTarget?.key === `service:${service.serviceName}`;
270
+ const isSelected = selectedConfigTarget?.key ===
271
+ `service:${service.serviceName}`;
206
272
  return (_jsxs("div", { className: `flex items-center justify-between rounded border p-3 text-sm transition-shadow hover:shadow-sm ${isSelected
207
273
  ? "border-blue-400 bg-blue-50"
208
- : "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1", children: [service.isConfigured ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600 flex-shrink-0", strokeWidth: 1.5 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500 flex-shrink-0", strokeWidth: 1.5 })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-gray-900", children: service.displayName || service.serviceName }), service.displayName && service.displayName !== service.serviceName && (_jsxs("div", { className: "text-xs text-gray-500", children: ["Service: ", service.serviceName] })), service.isConfigured && service.supportedLanguages && service.supportedLanguages.length > 0 && (_jsxs("div", { className: "text-xs text-gray-500 mt-1", children: ["Languages: ", service.supportedLanguages.join(", ")] })), !service.isConfigured && (_jsx("div", { className: "text-xs text-amber-600 mt-1", children: "Not configured" }))] })] }), _jsx("div", { className: "flex items-center gap-2 ml-4", children: service.isConfigured && service.settingsItemId ? (_jsxs(Button, { size: "sm", variant: isSelected ? "default" : "ghost", onClick: () => handleOpenConfig(service), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), "Configure"] })) : (_jsxs(Button, { size: "sm", variant: "outline", onClick: () => createSettings(service.serviceName, service.templateId), disabled: service.creating, title: `Create settings using template: ${service.templateName}`, className: "shrink-0", children: [service.creating ? (_jsx(RefreshCwIcon, { strokeWidth: 1, className: "h-4 w-4 animate-spin" })) : (_jsx(PlusIcon, { strokeWidth: 1.5, className: "h-4 w-4" })), "Create Settings"] })) })] }, service.serviceName));
274
+ : "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0", children: [service.isConfigured ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600 shrink-0", strokeWidth: 1.5 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500 shrink-0", strokeWidth: 1.5 })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-gray-900 truncate", children: service.displayName || service.serviceName }), service.displayName &&
275
+ service.displayName !== service.serviceName && (_jsxs("div", { className: "text-xs text-gray-500 truncate", children: ["Service: ", service.serviceName] })), service.isConfigured &&
276
+ service.supportedLanguages &&
277
+ service.supportedLanguages.length > 0 && (_jsxs("div", { className: "text-xs text-gray-500 mt-1 truncate", children: ["Languages:", " ", service.supportedLanguages.join(", ")] })), !service.isConfigured && (_jsx("div", { className: "text-xs text-amber-600 mt-1", children: "Not configured" }))] })] }), _jsx("div", { className: "flex items-center gap-2 ml-4 shrink-0", children: service.isConfigured && service.settingsItemId ? (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: isSelected ? "default" : "ghost", onClick: () => handleOpenConfig(service), className: "shrink-0", disabled: service.deleting, children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), editContext?.isMobile ? "" : "Configure"] }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: () => deleteSettings(service), className: "shrink-0 text-red-600 hover:text-red-700", disabled: service.deleting, title: `Delete settings for ${service.displayName || service.serviceName}`, children: [service.deleting ? (_jsx(RefreshCwIcon, { strokeWidth: 1, className: "h-4 w-4 animate-spin" })) : (_jsx(Trash2Icon, { className: "h-4 w-4", strokeWidth: 1.5 })), editContext?.isMobile ? "" : "Delete"] })] })) : (_jsxs(Button, { size: "sm", variant: "outline", onClick: () => createSettings(service.serviceName, service.templateId), disabled: service.creating, title: `Create settings using template: ${service.templateName}`, className: "shrink-0", children: [service.creating ? (_jsx(RefreshCwIcon, { strokeWidth: 1, className: "h-4 w-4 animate-spin" })) : (_jsx(PlusIcon, { strokeWidth: 1.5, className: "h-4 w-4" })), "Create Settings"] })) })] }, service.serviceName));
209
278
  }) })), error && (_jsx("div", { className: "rounded border border-red-200 bg-red-50 p-3 text-sm text-red-700 whitespace-pre-wrap", children: error }))] }) }) }) }));
210
279
  // Build splitter panels - only show config panel when a service is selected
211
280
  const panels = selectedConfigTarget?.itemId
@@ -232,6 +301,6 @@ export function TranslationServicesPanel() {
232
301
  },
233
302
  ];
234
303
  const SplitterComponent = Splitter;
235
- return (_jsx(SplitterComponent, { panels: panels, localStorageKey: "translation-services-panel-splitter", direction: "horizontal", className: "h-full" }));
304
+ return (_jsx(SplitterComponent, { panels: panels, localStorageKey: "translation-services-panel-splitter", direction: editContext?.isMobile ? "vertical" : "horizontal", className: "h-full w-full" }));
236
305
  }
237
306
  export default TranslationServicesPanel;
@@ -1 +1 @@
1
- {"version":3,"file":"LocalizationSetupStep.d.ts","sourceRoot":"","sources":["../../src/setup/LocalizationSetupStep.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,qBAAqB,4CA2NpC;AAED,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"LocalizationSetupStep.d.ts","sourceRoot":"","sources":["../../src/setup/LocalizationSetupStep.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,qBAAqB,4CA6NpC;AAED,eAAe,qBAAqB,CAAC"}
@@ -29,8 +29,12 @@ export function LocalizationSetupStep() {
29
29
  setServices([]);
30
30
  const result = await getAvailableTranslationServices();
31
31
  if (result.type === "success" && result.data) {
32
- // Backend returns ApiResponse<T>, so data is wrapped: { success: true, data: [...] }
33
- const availableServices = Array.isArray(result.data) ? result.data : result.data.data || [];
32
+ if (!result.data.success || !result.data.data) {
33
+ throw new Error(result.data.error ||
34
+ result.details ||
35
+ "Failed to check translation services. The translation API may not be available.");
36
+ }
37
+ const availableServices = result.data.data;
34
38
  if (availableServices.length === 0) {
35
39
  setState("error");
36
40
  setError("No translation services registered in dependency injection.");
@@ -75,12 +79,9 @@ export function LocalizationSetupStep() {
75
79
  "en";
76
80
  const result = await createProviderSettings(serviceName, templateId, language);
77
81
  if (result.type === "success" && result.data) {
78
- // Backend returns ApiResponse<T>, so unwrap: { success: true, data: { itemId, itemPath } }
79
- const response = result.data.success ? result.data.data : result.data;
80
- const success = result.data.success !== false;
81
- if (success && response) {
82
+ if (result.data.success && result.data.data) {
82
83
  setServices(prev => prev.map(s => s.serviceName === serviceName
83
- ? { ...s, isConfigured: true, creating: false, settingsItemId: response.itemId }
84
+ ? { ...s, isConfigured: true, creating: false, settingsItemId: result.data?.data?.itemId }
84
85
  : s));
85
86
  setTimeout(() => checkLocalization(), 500);
86
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAGtE,wBAAgB,4BAA4B,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CAiZ7K"}
1
+ {"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAkBtE,wBAAgB,4BAA4B,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CA8Z7K"}
@@ -1,6 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
3
  // Version creation now offered via VersionOnlyTranslationProvider. No custom button here.
4
+ function getTranslationStatusBadgeClass(status) {
5
+ switch (status) {
6
+ case "Completed":
7
+ return "text-green-700 bg-green-100";
8
+ case "Error":
9
+ return "text-red-700 bg-red-100";
10
+ case "In Progress":
11
+ return "text-purple-800 bg-purple-100";
12
+ case "Not started":
13
+ return "text-[var(--color-gray-2)] bg-[var(--color-gray-4)]";
14
+ default:
15
+ return "text-[var(--color-gray-2)] bg-[var(--color-gray-4)]";
16
+ }
17
+ }
4
18
  export function ServiceLanguageSelectionStep({ stepIndex, isActive = true, data, setData, onStepCompleted, editContext, setFooterActions, requestClose }) {
5
19
  const [languageSelection, setLanguageSelection] = useState({});
6
20
  const dataRef = useRef(data);
@@ -220,5 +234,8 @@ export function ServiceLanguageSelectionStep({ stepIndex, isActive = true, data,
220
234
  }
221
235
  }, onChange: handleSelectAllLanguages, className: "h-4 w-4 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": "select-all-languages-checkbox" }), _jsx("span", { className: "ml-2 text-xs text-[var(--color-gray-2)]", children: "Select All" })] }))] }), _jsx("p", { className: "text-xs text-[var(--color-gray-2)] mb-3", children: "Select the languages you want to translate this content into." }), _jsx("div", { className: "border border-[var(--color-gray-3)] rounded-lg min-h-[200px] max-h-64 overflow-y-auto bg-[var(--color-gray-5)]", children: data.translationProviders.length === 0 || allLanguages.length === 0 ? (
222
236
  // Loading skeleton
223
- _jsx("div", { className: "p-3 space-y-2", children: [...Array(4)].map((_, i) => (_jsxs("div", { className: "flex items-center animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "ml-2 h-4 bg-[var(--color-gray-3)] rounded w-32" }), _jsx("div", { className: "ml-auto h-4 bg-[var(--color-gray-3)] rounded w-16" })] }, i))) })) : (_jsx("div", { className: "p-3 grid grid-cols-1 gap-2", children: allLanguages.map((lang) => (_jsxs("label", { className: "flex items-center py-1.5 px-2 rounded-md hover:bg-[var(--color-gray-4)] transition-colors cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: languageSelection[lang.code] || false, onChange: () => handleLanguageToggle(lang.code), className: "h-4 w-4 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": `language-checkbox-${lang.code}` }), _jsxs("span", { className: "ml-2 text-sm text-[var(--color-dark)] flex items-center gap-2", children: [_jsxs("span", { children: [lang.name, " (", lang.code, ")"] }), lang.sourceLanguage && (_jsxs("span", { className: "text-xs text-[var(--color-gray-2)]", children: ["from: ", lang.sourceLanguage] }))] }), !lang.hasVersions && (_jsx("span", { className: "ml-auto text-2xs text-orange-600 bg-orange-100 px-2 py-1 rounded-full font-medium", children: "No versions" }))] }, lang.code))) })) })] })] })] }));
237
+ _jsx("div", { className: "p-3 space-y-2", children: [...Array(4)].map((_, i) => (_jsxs("div", { className: "flex items-center animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "ml-2 h-4 bg-[var(--color-gray-3)] rounded w-32" }), _jsx("div", { className: "ml-auto h-4 bg-[var(--color-gray-3)] rounded w-16" })] }, i))) })) : (_jsx("div", { className: "p-3 grid grid-cols-1 gap-2", children: allLanguages.map((lang) => {
238
+ const statusLabel = lang.translationStatus?.status || "Not started";
239
+ return (_jsxs("label", { className: "flex items-start gap-2 py-1.5 px-2 rounded-md hover:bg-[var(--color-gray-4)] transition-colors cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: languageSelection[lang.code] || false, onChange: () => handleLanguageToggle(lang.code), className: "mt-0.5 h-4 w-4 shrink-0 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": `language-checkbox-${lang.code}` }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "text-sm text-[var(--color-dark)]", children: [lang.name, " (", lang.code, ")"] }), _jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-[var(--color-gray-2)]", children: [lang.sourceLanguage && _jsxs("span", { children: ["from: ", lang.sourceLanguage] }), !lang.hasVersions && (_jsx("span", { className: "text-2xs text-orange-600 bg-orange-100 px-2 py-1 rounded-full font-medium", children: "No versions" })), lang.hasVersions && (_jsx("span", { title: lang.translationStatus?.message, className: `text-2xs px-2 py-1 rounded-full font-medium ${getTranslationStatusBadgeClass(statusLabel)}`, "data-testid": `translation-status-badge-${lang.code}`, children: statusLabel }))] })] })] }, lang.code));
240
+ }) })) })] })] })] }));
224
241
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SubitemDiscoveryStep.d.ts","sourceRoot":"","sources":["../../src/steps/SubitemDiscoveryStep.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAW/C,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CA8f5L"}
1
+ {"version":3,"file":"SubitemDiscoveryStep.d.ts","sourceRoot":"","sources":["../../src/steps/SubitemDiscoveryStep.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAW/C,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CAoa5L"}