@parhelia/localization 0.1.10745

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 (70) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +29 -0
  3. package/dist/core/src/editor/ui/DragPreview.d.ts +15 -0
  4. package/dist/core/src/editor/ui/DragPreview.d.ts.map +1 -0
  5. package/dist/core/src/editor/ui/DragPreview.js +32 -0
  6. package/dist/core/src/editor/ui/PerfectTree.d.ts +79 -0
  7. package/dist/core/src/editor/ui/PerfectTree.d.ts.map +1 -0
  8. package/dist/core/src/editor/ui/PerfectTree.js +857 -0
  9. package/dist/localization/src/LocalizeItemCommand.d.ts +8 -0
  10. package/dist/localization/src/LocalizeItemCommand.d.ts.map +1 -0
  11. package/dist/localization/src/LocalizeItemCommand.js +44 -0
  12. package/dist/localization/src/LocalizeItemDialog.d.ts +4 -0
  13. package/dist/localization/src/LocalizeItemDialog.d.ts.map +1 -0
  14. package/dist/localization/src/LocalizeItemDialog.js +126 -0
  15. package/dist/localization/src/LocalizeItemUtils.d.ts +17 -0
  16. package/dist/localization/src/LocalizeItemUtils.d.ts.map +1 -0
  17. package/dist/localization/src/LocalizeItemUtils.js +93 -0
  18. package/dist/localization/src/api/discovery.d.ts +36 -0
  19. package/dist/localization/src/api/discovery.d.ts.map +1 -0
  20. package/dist/localization/src/api/discovery.js +29 -0
  21. package/dist/localization/src/constants.d.ts +15 -0
  22. package/dist/localization/src/constants.d.ts.map +1 -0
  23. package/dist/localization/src/constants.js +21 -0
  24. package/dist/localization/src/hooks/useTranslationWizard.d.ts +6 -0
  25. package/dist/localization/src/hooks/useTranslationWizard.d.ts.map +1 -0
  26. package/dist/localization/src/hooks/useTranslationWizard.js +78 -0
  27. package/dist/localization/src/index.d.ts +69 -0
  28. package/dist/localization/src/index.d.ts.map +1 -0
  29. package/dist/localization/src/index.js +152 -0
  30. package/dist/localization/src/services/translationService.d.ts +102 -0
  31. package/dist/localization/src/services/translationService.d.ts.map +1 -0
  32. package/dist/localization/src/services/translationService.js +37 -0
  33. package/dist/localization/src/setup/LocalizationSetupStep.d.ts +3 -0
  34. package/dist/localization/src/setup/LocalizationSetupStep.d.ts.map +1 -0
  35. package/dist/localization/src/setup/LocalizationSetupStep.js +108 -0
  36. package/dist/localization/src/sidebar/TranslationSidebar.d.ts +2 -0
  37. package/dist/localization/src/sidebar/TranslationSidebar.d.ts.map +1 -0
  38. package/dist/localization/src/sidebar/TranslationSidebar.js +93 -0
  39. package/dist/localization/src/steps/MetadataInputStep.d.ts +4 -0
  40. package/dist/localization/src/steps/MetadataInputStep.d.ts.map +1 -0
  41. package/dist/localization/src/steps/MetadataInputStep.js +38 -0
  42. package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts +3 -0
  43. package/dist/localization/src/steps/ServiceLanguageSelectionStep.d.ts.map +1 -0
  44. package/dist/localization/src/steps/ServiceLanguageSelectionStep.js +91 -0
  45. package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts +3 -0
  46. package/dist/localization/src/steps/SubitemDiscoveryStep.d.ts.map +1 -0
  47. package/dist/localization/src/steps/SubitemDiscoveryStep.js +391 -0
  48. package/dist/localization/src/steps/index.d.ts +5 -0
  49. package/dist/localization/src/steps/index.d.ts.map +1 -0
  50. package/dist/localization/src/steps/index.js +4 -0
  51. package/dist/localization/src/steps/types.d.ts +68 -0
  52. package/dist/localization/src/steps/types.d.ts.map +1 -0
  53. package/dist/localization/src/steps/types.js +1 -0
  54. package/dist/localization/src/translation-center/BatchTranslationView.d.ts +7 -0
  55. package/dist/localization/src/translation-center/BatchTranslationView.d.ts.map +1 -0
  56. package/dist/localization/src/translation-center/BatchTranslationView.js +487 -0
  57. package/dist/localization/src/translation-center/RecentTranslations.d.ts +2 -0
  58. package/dist/localization/src/translation-center/RecentTranslations.d.ts.map +1 -0
  59. package/dist/localization/src/translation-center/RecentTranslations.js +199 -0
  60. package/dist/localization/src/translation-center/TranslationManagement.d.ts +2 -0
  61. package/dist/localization/src/translation-center/TranslationManagement.d.ts.map +1 -0
  62. package/dist/localization/src/translation-center/TranslationManagement.js +25 -0
  63. package/dist/localization/src/types.d.ts +18 -0
  64. package/dist/localization/src/types.d.ts.map +1 -0
  65. package/dist/localization/src/types.js +1 -0
  66. package/dist/localization/src/utils/createVersions.d.ts +14 -0
  67. package/dist/localization/src/utils/createVersions.d.ts.map +1 -0
  68. package/dist/localization/src/utils/createVersions.js +26 -0
  69. package/package.json +47 -0
  70. package/styles.css +1 -0
@@ -0,0 +1,152 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ServiceLanguageSelectionStep } from "./steps/ServiceLanguageSelectionStep";
3
+ import { SubitemDiscoveryStep } from "./steps/SubitemDiscoveryStep";
4
+ import { localizeItemCommand } from "./LocalizeItemCommand";
5
+ import { TranslationSidebar } from "./sidebar/TranslationSidebar";
6
+ import { TranslationManagement } from "./translation-center/TranslationManagement";
7
+ import { RecentTranslations } from "./translation-center/RecentTranslations";
8
+ import { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
9
+ import { Globe, Languages } from "lucide-react";
10
+ import { pageEditorViewBase } from "@parhelia/core";
11
+ const DEFAULT_TRANSLATION_WIZARD_CONFIGURATION = {
12
+ steps: [
13
+ {
14
+ id: "service-language-selection",
15
+ name: "Configure Translation",
16
+ description: "Select translation provider and target languages for your content.",
17
+ component: ServiceLanguageSelectionStep,
18
+ },
19
+ {
20
+ id: "subitem-discovery",
21
+ name: "Discover Subitems",
22
+ description: "Find and include child items that should also be translated.",
23
+ component: SubitemDiscoveryStep,
24
+ skipCondition: (data) => !data.includeSubitems,
25
+ },
26
+ ],
27
+ };
28
+ const SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION = {
29
+ steps: [
30
+ {
31
+ id: "service-language-selection",
32
+ name: "Configure Translation",
33
+ description: "Select translation provider and target languages for your content.",
34
+ component: ServiceLanguageSelectionStep,
35
+ },
36
+ // No SubitemDiscoveryStep - single item translation only
37
+ ],
38
+ };
39
+ export { DEFAULT_TRANSLATION_WIZARD_CONFIGURATION, SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION };
40
+ export { LocalizeItemDialog } from "./LocalizeItemDialog";
41
+ export { localizeItemCommand } from "./LocalizeItemCommand";
42
+ export { TranslationManagement } from "./translation-center/TranslationManagement";
43
+ export { RecentTranslations } from "./translation-center/RecentTranslations";
44
+ export { BatchTranslationView } from "./translation-center/BatchTranslationView";
45
+ export { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
46
+ export { defaultTranslateAll, generateBatchId, performDefaultTranslation } from "./LocalizeItemUtils";
47
+ export * from "./steps/types";
48
+ /**
49
+ * Configures the localization extension for the editor.
50
+ *
51
+ * @example
52
+ * // Full-featured (default): Multi-item translation with subitem discovery
53
+ * configureLocalization(configuration);
54
+ * // or explicitly:
55
+ * configureLocalization(configuration, { multiItem: true });
56
+ *
57
+ * @example
58
+ * // Single-item only: Restricts translation to one item at a time
59
+ * configureLocalization(configuration, { multiItem: false });
60
+ *
61
+ * @param configuration - The editor configuration object
62
+ * @param options - Optional configuration for localization features
63
+ */
64
+ export function configureLocalization(configuration, options) {
65
+ const enableMultiItem = options?.multiItem !== false; // Default to true
66
+ // Add localization setup step
67
+ if (!configuration.setup) {
68
+ configuration.setup = { steps: [] };
69
+ }
70
+ // Check if the step is already added (to prevent duplicates)
71
+ const hasLocalizationSetup = configuration.setup.steps.some((step) => step === LocalizationSetupStep || step.name === "LocalizationSetupStep");
72
+ if (!hasLocalizationSetup) {
73
+ configuration.setup.steps.push(LocalizationSetupStep);
74
+ }
75
+ // Initialize the translation wizard configuration if it doesn't exist
76
+ if (!configuration.translationWizard) {
77
+ configuration.translationWizard = enableMultiItem
78
+ ? DEFAULT_TRANSLATION_WIZARD_CONFIGURATION
79
+ : SINGLE_ITEM_TRANSLATION_WIZARD_CONFIGURATION;
80
+ }
81
+ // Store settings in configuration for other components to access
82
+ if (!configuration.localization) {
83
+ configuration.localization = {};
84
+ }
85
+ configuration.localization.multiItem = enableMultiItem;
86
+ configuration.localization.translationManagement = options?.translationManagement !== false;
87
+ if (!configuration.commands.allItemCommands) {
88
+ configuration.commands.allItemCommands = [];
89
+ }
90
+ // Check if the command is already added (to prevent duplicates)
91
+ const hasLocalizeCommand = configuration.commands.allItemCommands.some((cmd) => cmd.id === "localizeItem");
92
+ if (!hasLocalizeCommand) {
93
+ configuration.commands.allItemCommands.push(localizeItemCommand);
94
+ }
95
+ // Add Translation view to the editor views
96
+ if (configuration.editor && configuration.editor.views) {
97
+ const hasTranslateView = configuration.editor.views.some((view) => view.name === "translate");
98
+ if (!hasTranslateView) {
99
+ configuration.editor.views.push({
100
+ name: "translate",
101
+ icon: _jsx(Languages, { strokeWidth: 1 }),
102
+ title: "Translate",
103
+ leftSidebar: {
104
+ panels: [
105
+ {
106
+ name: "translation",
107
+ title: "Languages & Versions",
108
+ content: _jsx(TranslationSidebar, {}),
109
+ initialSize: 20,
110
+ },
111
+ ],
112
+ },
113
+ ...pageEditorViewBase,
114
+ });
115
+ }
116
+ }
117
+ // Add Translation Management view
118
+ if (options?.translationManagement !== false && configuration.editor && configuration.editor.views) {
119
+ const hasTranslationManagementView = configuration.editor.views.some((view) => view.name === "translation-management");
120
+ if (!hasTranslationManagementView) {
121
+ configuration.editor.views.push({
122
+ name: "translation-management",
123
+ icon: _jsx(Globe, { strokeWidth: 1 }),
124
+ title: "Translation Management",
125
+ defaultCenterPanelView: _jsx(TranslationManagement, {}),
126
+ });
127
+ }
128
+ }
129
+ // Configure translation center with Recent Translations panel
130
+ if (!configuration.translationCenter) {
131
+ configuration.translationCenter = {
132
+ groups: []
133
+ };
134
+ }
135
+ // Add recent translations panel to translation center
136
+ const hasRecentTranslationsGroup = configuration.translationCenter.groups?.some((group) => group.panels?.some((panel) => panel.id === "recent-translations"));
137
+ if (!hasRecentTranslationsGroup) {
138
+ configuration.translationCenter.groups.push({
139
+ title: "Translation Tracking",
140
+ icon: _jsx(Languages, { strokeWidth: 1 }),
141
+ panels: [
142
+ {
143
+ id: "recent-translations",
144
+ title: "Recent Translations",
145
+ icon: _jsx(Languages, { strokeWidth: 1 }),
146
+ content: _jsx(RecentTranslations, {}),
147
+ },
148
+ ],
149
+ });
150
+ }
151
+ return configuration;
152
+ }
@@ -0,0 +1,102 @@
1
+ import { TranslationStatus } from "../types";
2
+ import { TranslationProviderInfo } from "../steps/types";
3
+ export type TranslationRequestResponse = {
4
+ jobId: string;
5
+ };
6
+ export declare function requestTranslation({ itemId, targetLanguage, sourceLanguage, sessionId, provider, metadata, batchId }: {
7
+ itemId: string;
8
+ targetLanguage: string;
9
+ sourceLanguage: string;
10
+ sessionId: string;
11
+ provider: string;
12
+ metadata: string;
13
+ batchId?: string;
14
+ }): Promise<import("@parhelia/core").ExecutionResult<TranslationRequestResponse>>;
15
+ export declare function getTranslationStatus(itemId: string): Promise<import("@parhelia/core").ExecutionResult<TranslationStatus[]>>;
16
+ export declare function getTranslationProviders(): Promise<import("@parhelia/core").ExecutionResult<TranslationProviderInfo[]>>;
17
+ export type AvailableTranslationService = {
18
+ serviceName: string;
19
+ displayName: string;
20
+ isConfigured: boolean;
21
+ supportedLanguages?: string[];
22
+ settingsItemId?: string;
23
+ templateId: string;
24
+ templateName: string;
25
+ };
26
+ export declare function getAvailableTranslationServices(): Promise<import("@parhelia/core").ExecutionResult<AvailableTranslationService[]>>;
27
+ export declare function createProviderSettings(serviceName: string, templateId: string, language?: string): Promise<import("@parhelia/core").ExecutionResult<{
28
+ success: boolean;
29
+ itemId: string;
30
+ itemPath: string;
31
+ message: string;
32
+ }>>;
33
+ export type TranslationJobRecord = {
34
+ itemId: string;
35
+ targetLanguage: string;
36
+ sourceLanguage: string;
37
+ status: string;
38
+ timestamp: string;
39
+ hash?: string;
40
+ message?: string;
41
+ batchId?: string;
42
+ };
43
+ export declare function listTranslationJobs(take?: number, skip?: number): Promise<import("@parhelia/core").ExecutionResult<TranslationJobRecord[]>>;
44
+ export declare function listBatchTranslationJobs(batchId: string): Promise<import("@parhelia/core").ExecutionResult<TranslationJobRecord[]>>;
45
+ export type TranslationBatchInfo = {
46
+ batchId: string;
47
+ createdAtUtc?: string;
48
+ startedAtUtc?: string;
49
+ completedAtUtc?: string;
50
+ initiatedByUser?: string;
51
+ initiatorSessionId?: string;
52
+ provider?: string;
53
+ status?: string;
54
+ expectedJobs?: number;
55
+ completedJobs?: number;
56
+ errorJobs?: number;
57
+ includeSubitems?: boolean | null;
58
+ scopeItemId?: string | null;
59
+ scopeItemPath?: string | null;
60
+ metadata?: string | null;
61
+ lastUpdatedUtc?: string;
62
+ exists?: boolean;
63
+ };
64
+ export declare function getBatchInfo(batchId: string): Promise<import("@parhelia/core").ExecutionResult<TranslationBatchInfo>>;
65
+ export declare function listBatches(take?: number, skip?: number): Promise<import("@parhelia/core").ExecutionResult<TranslationBatchInfo[]>>;
66
+ export declare function subscribeToBatch(batchId: string, sessionId: string): Promise<import("@parhelia/core").ExecutionResult<{
67
+ success: boolean;
68
+ message: string;
69
+ }>>;
70
+ export declare function unsubscribeFromBatch(batchId: string, sessionId: string): Promise<import("@parhelia/core").ExecutionResult<{
71
+ success: boolean;
72
+ message: string;
73
+ }>>;
74
+ export type BatchTranslationItem = {
75
+ itemId: string;
76
+ targetLanguage: string;
77
+ sourceLanguage: string;
78
+ metadata?: string;
79
+ };
80
+ export type BatchTranslationResult = {
81
+ itemId: string;
82
+ targetLanguage: string;
83
+ jobId: string;
84
+ };
85
+ export type BatchTranslationError = {
86
+ itemId: string;
87
+ targetLanguage: string;
88
+ error: string;
89
+ };
90
+ export type BatchTranslationResponse = {
91
+ batchId: string;
92
+ started: BatchTranslationResult[];
93
+ skipped: BatchTranslationError[];
94
+ };
95
+ export declare function requestBatchTranslation({ sessionId, batchId, provider, batchMetadata, translations }: {
96
+ sessionId: string;
97
+ batchId: string;
98
+ provider: string;
99
+ batchMetadata?: string;
100
+ translations: BatchTranslationItem[];
101
+ }): Promise<import("@parhelia/core").ExecutionResult<BatchTranslationResponse>>;
102
+ //# sourceMappingURL=translationService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translationService.d.ts","sourceRoot":"","sources":["../../../../src/services/translationService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { get, post } from "@parhelia/core";
2
+ export async function requestTranslation({ itemId, targetLanguage, sourceLanguage, sessionId, provider, metadata, batchId }) {
3
+ return await post("/parhelia/translation/requestTranslation", { itemId, targetLanguage, sourceLanguage, sessionId, provider, metadata, batchId });
4
+ }
5
+ export async function getTranslationStatus(itemId) {
6
+ return await get(`/parhelia/translation/getTranslations?itemId=${itemId}`);
7
+ }
8
+ export async function getTranslationProviders() {
9
+ return await get(`/parhelia/translation/providers`);
10
+ }
11
+ export async function getAvailableTranslationServices() {
12
+ return await get(`/parhelia/translation/availableServices`);
13
+ }
14
+ export async function createProviderSettings(serviceName, templateId, language = "en") {
15
+ return await post("/parhelia/translation/createProviderSettings", { serviceName, templateId, language });
16
+ }
17
+ export async function listTranslationJobs(take = 100, skip = 0) {
18
+ return await get(`/parhelia/translation/jobs?take=${take}&skip=${skip}`);
19
+ }
20
+ export async function listBatchTranslationJobs(batchId) {
21
+ return await get(`/parhelia/translation/batchJobs?batchId=${encodeURIComponent(batchId)}`);
22
+ }
23
+ export async function getBatchInfo(batchId) {
24
+ return await get(`/parhelia/translation/batchInfo?batchId=${encodeURIComponent(batchId)}`);
25
+ }
26
+ export async function listBatches(take = 100, skip = 0) {
27
+ return await get(`/parhelia/translation/batches?take=${take}&skip=${skip}`);
28
+ }
29
+ export async function subscribeToBatch(batchId, sessionId) {
30
+ return await post("/parhelia/translation/subscribeToBatch", { batchId, sessionId });
31
+ }
32
+ export async function unsubscribeFromBatch(batchId, sessionId) {
33
+ return await post("/parhelia/translation/unsubscribeFromBatch", { batchId, sessionId });
34
+ }
35
+ export async function requestBatchTranslation({ sessionId, batchId, provider, batchMetadata, translations }) {
36
+ return await post("/parhelia/translation/requestBatchTranslation", { sessionId, batchId, provider, batchMetadata: batchMetadata || "", translations });
37
+ }
@@ -0,0 +1,3 @@
1
+ export declare function LocalizationSetupStep(): import("react/jsx-runtime").JSX.Element;
2
+ export default LocalizationSetupStep;
3
+ //# sourceMappingURL=LocalizationSetupStep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalizationSetupStep.d.ts","sourceRoot":"","sources":["../../../../src/setup/LocalizationSetupStep.tsx"],"names":[],"mappings":"AAeA,wBAAgB,qBAAqB,4CA+MpC;AAED,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useCallback, useState } from "react";
3
+ import { Card, Button, useEditContext } from "@parhelia/core";
4
+ import { CheckCircle, AlertCircle, Languages, RefreshCw, Plus } from "lucide-react";
5
+ import { getAvailableTranslationServices, createProviderSettings } from "../services/translationService";
6
+ export function LocalizationSetupStep() {
7
+ const editContext = useEditContext();
8
+ const [state, setState] = useState("checking");
9
+ const [error, setError] = useState(null);
10
+ const [services, setServices] = useState([]);
11
+ const statusIcon = useCallback((state) => {
12
+ if (state === "success")
13
+ return _jsx(CheckCircle, { className: "h-4 w-4 text-green-600", strokeWidth: 1 });
14
+ if (state === "error")
15
+ return _jsx(AlertCircle, { className: "h-4 w-4 text-red-600", strokeWidth: 1 });
16
+ if (state === "partial")
17
+ return _jsx(AlertCircle, { className: "h-4 w-4 text-amber-600", strokeWidth: 1 });
18
+ return (_jsx(RefreshCw, { className: "h-4 w-4 animate-spin text-amber-600", strokeWidth: 1 }));
19
+ }, []);
20
+ const checkLocalization = useCallback(async () => {
21
+ try {
22
+ setState("checking");
23
+ setError(null);
24
+ setServices([]);
25
+ const result = await getAvailableTranslationServices();
26
+ if (result.type === "success" && result.data) {
27
+ // Backend returns ApiResponse<T>, so data is wrapped: { success: true, data: [...] }
28
+ const availableServices = Array.isArray(result.data) ? result.data : result.data.data || [];
29
+ if (availableServices.length === 0) {
30
+ setState("error");
31
+ setError("No translation services registered in dependency injection.");
32
+ }
33
+ else {
34
+ const serviceStates = availableServices.map((svc) => ({
35
+ ...svc,
36
+ creating: false
37
+ }));
38
+ setServices(serviceStates);
39
+ const configuredCount = serviceStates.filter(s => s.isConfigured).length;
40
+ const totalCount = serviceStates.length;
41
+ if (configuredCount === 0) {
42
+ setState("error");
43
+ setError(`Found ${totalCount} translation service${totalCount !== 1 ? 's' : ''} in DI, but none have Sitecore Settings items configured.`);
44
+ }
45
+ else if (configuredCount < totalCount) {
46
+ setState("partial");
47
+ // Don't set error message - the status is already shown in the header
48
+ }
49
+ else {
50
+ setState("success");
51
+ }
52
+ }
53
+ }
54
+ else {
55
+ setState("error");
56
+ setError(result.details || "Failed to check translation services. The translation API may not be available.");
57
+ }
58
+ }
59
+ catch (e) {
60
+ setState("error");
61
+ setError(e?.message || "Failed to check localization setup");
62
+ }
63
+ }, []);
64
+ const createSettings = useCallback(async (serviceName, templateId) => {
65
+ try {
66
+ setServices(prev => prev.map(s => s.serviceName === serviceName ? { ...s, creating: true } : s));
67
+ setError(null);
68
+ const language = editContext?.contentEditorItem?.language || "en";
69
+ const result = await createProviderSettings(serviceName, templateId, language);
70
+ if (result.type === "success" && result.data) {
71
+ // Backend returns ApiResponse<T>, so unwrap: { success: true, data: { itemId, itemPath } }
72
+ const response = result.data.success ? result.data.data : result.data;
73
+ const success = result.data.success !== false;
74
+ if (success && response) {
75
+ setServices(prev => prev.map(s => s.serviceName === serviceName
76
+ ? { ...s, isConfigured: true, creating: false, settingsItemId: response.itemId }
77
+ : s));
78
+ setTimeout(() => checkLocalization(), 500);
79
+ }
80
+ else {
81
+ throw new Error(result.data.error || result.details || "Failed to create settings");
82
+ }
83
+ }
84
+ else {
85
+ throw new Error(result.details || "Failed to create settings");
86
+ }
87
+ }
88
+ catch (e) {
89
+ setError(`Failed to create settings for ${serviceName}: ${e?.message || "Unknown error"}`);
90
+ setServices(prev => prev.map(s => s.serviceName === serviceName ? { ...s, creating: false } : s));
91
+ }
92
+ }, [editContext, checkLocalization]);
93
+ React.useEffect(() => {
94
+ checkLocalization();
95
+ }, [checkLocalization]);
96
+ const configuredCount = services.filter(s => s.isConfigured).length;
97
+ const totalCount = services.length;
98
+ return (_jsx(Card, { icon: _jsx(Languages, { strokeWidth: 1, className: "h-5 w-5" }), title: "Localization setup", description: "Verifies that translation services registered in DI have corresponding Sitecore Settings items.", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [statusIcon(state), _jsx("span", { className: "text-sm text-gray-700", children: state === "success"
99
+ ? `All ${totalCount} translation service${totalCount !== 1 ? 's' : ''} configured`
100
+ : state === "partial"
101
+ ? `${configuredCount} of ${totalCount} services configured`
102
+ : state === "error" && totalCount > 0
103
+ ? `${totalCount} service${totalCount !== 1 ? 's' : ''} found, none configured`
104
+ : state === "error"
105
+ ? "No services found"
106
+ : "Checking..." })] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: checkLocalization, children: [_jsx(RefreshCw, { strokeWidth: 1, className: "h-4 w-4" }), "Recheck"] })] }), services.length > 0 && (_jsxs("div", { className: "space-y-2 rounded border border-gray-200 bg-gray-50 p-3", children: [_jsx("div", { className: "text-xs font-semibold text-gray-600 uppercase", children: "Translation Services" }), services.map((service) => (_jsxs("div", { className: "flex items-center justify-between rounded bg-white p-2 shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [service.isConfigured ? (_jsx(CheckCircle, { className: "h-3.5 w-3.5 text-green-600", strokeWidth: 1.5 })) : (_jsx(AlertCircle, { className: "h-3.5 w-3.5 text-amber-600", strokeWidth: 1.5 })), _jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-gray-800", 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", children: ["Languages: ", service.supportedLanguages.join(", ")] }))] })] }), !service.isConfigured && (_jsxs(Button, { size: "xs", onClick: () => createSettings(service.serviceName, service.templateId), disabled: service.creating, title: `Create using template: ${service.templateName}`, children: [service.creating ? (_jsx(RefreshCw, { strokeWidth: 1, className: "h-3 w-3 animate-spin" })) : (_jsx(Plus, { strokeWidth: 1.5, className: "h-3 w-3" })), "Create Settings"] }))] }, service.serviceName)))] })), error && (_jsx("div", { className: "rounded border border-yellow-200 bg-yellow-50 p-2 text-xs whitespace-pre-wrap text-yellow-800", children: error }))] }) }));
107
+ }
108
+ export default LocalizationSetupStep;
@@ -0,0 +1,2 @@
1
+ export declare function TranslationSidebar(): import("react/jsx-runtime").JSX.Element | null;
2
+ //# sourceMappingURL=TranslationSidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TranslationSidebar.d.ts","sourceRoot":"","sources":["../../../../src/sidebar/TranslationSidebar.tsx"],"names":[],"mappings":"AAOA,wBAAgB,kBAAkB,mDA0KjC"}
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, useEditContext, SimpleTable, confirmCreateVersion, Progress } from "@parhelia/core";
3
+ import { getTranslationStatus } from "../services/translationService";
4
+ import { useEffect, useState } from "react";
5
+ import { localizeItemCommand } from "../LocalizeItemCommand";
6
+ export function TranslationSidebar() {
7
+ const editContext = useEditContext();
8
+ if (!editContext)
9
+ return null;
10
+ const [translationStatus, setTranslationStatus] = useState(null);
11
+ const languages = editContext?.itemLanguages || [];
12
+ const [translationProgress, setTranslationProgress] = useState(new Map());
13
+ const item = editContext.contentEditorItem;
14
+ const selectLanguage = (language) => {
15
+ if (!item)
16
+ return;
17
+ if (!language.versions) {
18
+ confirmCreateVersion(editContext, language);
19
+ return;
20
+ }
21
+ editContext.loadItem({
22
+ ...item.descriptor,
23
+ language: language.languageCode,
24
+ version: 0,
25
+ });
26
+ };
27
+ const languageName = (language) => {
28
+ return (_jsxs("div", { className: "flex items-center gap-2", onClick: () => selectLanguage(language), children: [_jsx("img", { src: language.icon, className: "h-5" }), " ", language.name] }, language.languageCode));
29
+ };
30
+ const loadTranslationStatus = async () => {
31
+ if (!item)
32
+ return;
33
+ const status = await getTranslationStatus(item.descriptor.id);
34
+ setTranslationStatus(status.data || []);
35
+ };
36
+ useEffect(() => {
37
+ loadTranslationStatus();
38
+ setTranslationProgress(new Map());
39
+ }, [editContext.currentItemDescriptor?.id]);
40
+ useEffect(() => {
41
+ const removeSocketMessageListener = editContext.addSocketMessageListener((message) => {
42
+ if (message.type === "translation-started" || message.type === "translation-finished" || message.type === "translation-error") {
43
+ if (message.payload.itemId === item?.descriptor.id) {
44
+ loadTranslationStatus();
45
+ }
46
+ }
47
+ if (message.type === "translation-progress") {
48
+ if (message.payload.itemId === item?.descriptor.id) {
49
+ setTranslationProgress((p) => {
50
+ p.set(message.payload.language, message.payload);
51
+ return new Map(p);
52
+ });
53
+ }
54
+ }
55
+ });
56
+ return removeSocketMessageListener;
57
+ }, [editContext, setTranslationProgress]);
58
+ const languageData = languages.map((x) => ({
59
+ ...x,
60
+ translationStatus: translationStatus?.find((y) => y.targetLanguage === x.languageCode),
61
+ translationProgress: translationProgress.get(x.languageCode),
62
+ }));
63
+ languageData.sort((a, b) => {
64
+ // Sort languages with 0 versions to bottom
65
+ if (a.versions === 0 && b.versions > 0)
66
+ return 1;
67
+ if (b.versions === 0 && a.versions > 0)
68
+ return -1;
69
+ // Sort by name for languages that both have versions or both don't
70
+ return a.name.localeCompare(b.name);
71
+ });
72
+ return (_jsxs("div", { className: "flex h-full flex-col gap-2", "data-testid": "translation-sidebar", children: [_jsxs("div", { className: "flex w-full flex-wrap items-center justify-between gap-2 bg-gray-50 p-3", children: [_jsx("div", { className: "flex items-center gap-2 text-sm", children: _jsx("span", { className: "text-gray-500 font-medium", children: "Current Item Languages & Versions" }) }), item && (_jsxs(Button, { size: "sm", "data-testid": "translation-sidebar-localize-button", onClick: () => {
73
+ editContext.executeCommand({
74
+ command: localizeItemCommand,
75
+ data: {
76
+ items: [item],
77
+ },
78
+ });
79
+ }, children: [_jsx("i", { className: "pi pi-plus" }), "Translate"] }))] }), _jsx("div", { className: "relative flex-1", children: _jsx("div", { className: "absolute inset-0 overflow-y-auto", children: _jsx(SimpleTable, { "data-testid": "translation-sidebar-language-table", columns: [
80
+ { header: "Language", body: languageName },
81
+ { header: "Ver.", body: (x) => x.versions.toString() },
82
+ {
83
+ header: "Src",
84
+ body: (x) => x.translationStatus?.sourceLanguage,
85
+ },
86
+ {
87
+ header: "Status",
88
+ body: (x) => (_jsxs("div", { className: "flex items-center gap-2", "data-testid": `translation-status-${x.languageCode}`, children: [x.translationStatus?.status === "In Progress" && (_jsxs("div", { className: "relative w-full", children: [_jsx(Progress, { className: "h-5", value: (x.translationProgress?.progress || 0) * 100, showValue: false, "data-testid": `translation-progress-${x.languageCode}` }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center text-white text-xs", children: x.translationProgress?.message })] })), x.translationStatus?.status !== "In Progress" && (_jsx("div", { title: x.translationStatus?.message, children: x.translationStatus?.status || "Not started" }))] })),
89
+ },
90
+ ], items: languageData, onRowClick: (data) => selectLanguage(data.item), rowClassName: (item) => item.languageCode == editContext.currentItemDescriptor?.language
91
+ ? "bg-neutral-100"
92
+ : "" }) }) })] }));
93
+ }
@@ -0,0 +1,4 @@
1
+ import { TranslationStepProps } from "./types";
2
+ /** Example Step for metadata input testing */
3
+ export declare function MetadataInputStep({ data, setData, onStepCompleted, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
4
+ //# sourceMappingURL=MetadataInputStep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetadataInputStep.d.ts","sourceRoot":"","sources":["../../../../src/steps/MetadataInputStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,eAAe,GAChB,EAAE,oBAAoB,2CA0EtB"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ /** Example Step for metadata input testing */
4
+ export function MetadataInputStep({ data, setData, onStepCompleted, }) {
5
+ // Convert Map<string, Map<string, string>> to a flat object for local state management
6
+ const [itemMetadata, setItemMetadata] = useState(() => {
7
+ const result = {};
8
+ data.itemMetadata.forEach((languageMap, itemId) => {
9
+ result[itemId] = Object.fromEntries(languageMap);
10
+ });
11
+ return result;
12
+ });
13
+ const allItems = data.includeSubitems ? data.discoveredItems : data.items;
14
+ const handleMetadataChange = (itemId, language, metadata) => {
15
+ const updated = {
16
+ ...itemMetadata,
17
+ [itemId]: {
18
+ ...(itemMetadata[itemId] || {}),
19
+ [language]: metadata
20
+ }
21
+ };
22
+ setItemMetadata(updated);
23
+ // Convert back to Map structure for wizard data
24
+ const metadataMap = new Map();
25
+ Object.entries(updated).forEach(([itemId, languageMap]) => {
26
+ metadataMap.set(itemId, new Map(Object.entries(languageMap)));
27
+ });
28
+ setData({ ...data, itemMetadata: metadataMap });
29
+ console.log('Updated itemMetadata:', metadataMap);
30
+ };
31
+ // Data is saved automatically on change
32
+ useEffect(() => {
33
+ // Mark step as completed since metadata is optional
34
+ onStepCompleted(true);
35
+ }, [onStepCompleted]);
36
+ // No footer actions needed since data is saved automatically
37
+ return (_jsxs("div", { className: "flex flex-col gap-4 p-4", "data-testid": "metadata-input-step", children: [_jsx("h3", { className: "text-lg font-medium", children: "Enter Metadata Per Item Per Language" }), _jsx("p", { className: "text-sm text-gray-600", children: "Provide custom metadata for each item and language combination (optional)." }), _jsx("div", { className: "max-h-64 overflow-y-auto space-y-4", "data-testid": "metadata-input-container", children: allItems.map((item) => (_jsxs("div", { className: "border rounded p-3", "data-testid": `metadata-item-${item.id}`, children: [_jsx("label", { className: "block text-sm font-medium mb-2", "data-testid": `metadata-item-label-${item.id}`, children: item.name || item.id }), data.targetLanguages?.map((language) => (_jsxs("div", { className: "mb-2", "data-testid": `metadata-field-${item.id}-${language}`, children: [_jsxs("label", { className: "block text-xs font-medium mb-1 text-gray-600", "data-testid": `metadata-language-label-${item.id}-${language}`, children: [language.toUpperCase(), ":"] }), _jsx("textarea", { className: "w-full border rounded p-2 text-sm", rows: 2, value: itemMetadata[item.id]?.[language] || "", onChange: (e) => handleMetadataChange(item.id, language, e.target.value), placeholder: `Enter metadata for ${language}...`, "data-testid": `metadata-textarea-${item.id}-${language}` })] }, `${item.id}-${language}`)))] }, item.id))) })] }));
38
+ }
@@ -0,0 +1,3 @@
1
+ import { TranslationStepProps } from "./types";
2
+ export declare function ServiceLanguageSelectionStep({ data, setData, onStepCompleted, editContext, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
3
+ //# sourceMappingURL=ServiceLanguageSelectionStep.d.ts.map
@@ -0,0 +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,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CAuMjJ"}