@parhelia/localization 0.1.12808 → 0.1.12836

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 (50) hide show
  1. package/dist/LocalizeItemCommand.d.ts.map +1 -1
  2. package/dist/LocalizeItemCommand.js +4 -2
  3. package/dist/LocalizeItemDialog.d.ts.map +1 -1
  4. package/dist/LocalizeItemDialog.js +7 -4
  5. package/dist/LocalizeItemUtils.d.ts.map +1 -1
  6. package/dist/LocalizeItemUtils.js +35 -25
  7. package/dist/api/discovery.d.ts.map +1 -1
  8. package/dist/api/discovery.js +20 -3
  9. package/dist/hooks/useTranslationWizard.d.ts.map +1 -1
  10. package/dist/hooks/useTranslationWizard.js +3 -3
  11. package/dist/services/translationService.d.ts +2 -2
  12. package/dist/services/translationService.d.ts.map +1 -1
  13. package/dist/services/translationService.js +21 -5
  14. package/dist/settings/TranslationServicesPanel.d.ts.map +1 -1
  15. package/dist/settings/TranslationServicesPanel.js +24 -21
  16. package/dist/setup/LocalizationSetupStep.d.ts.map +1 -1
  17. package/dist/setup/LocalizationSetupStep.js +29 -18
  18. package/dist/sidebar/TranslationSidebar.d.ts.map +1 -1
  19. package/dist/sidebar/TranslationSidebar.js +17 -10
  20. package/dist/steps/ItemSelectionStep.d.ts.map +1 -1
  21. package/dist/steps/ItemSelectionStep.js +2 -1
  22. package/dist/steps/ItemSelectionTree.d.ts.map +1 -1
  23. package/dist/steps/ItemSelectionTree.js +2 -1
  24. package/dist/steps/PromptCustomizationStep.d.ts.map +1 -1
  25. package/dist/steps/PromptCustomizationStep.js +6 -8
  26. package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
  27. package/dist/steps/ServiceLanguageSelectionStep.js +6 -4
  28. package/dist/steps/WizardStepShell.d.ts.map +1 -1
  29. package/dist/steps/types.d.ts.map +1 -1
  30. package/dist/translation-center/TranslationBatches.d.ts.map +1 -1
  31. package/dist/translation-center/TranslationBatches.js +187 -115
  32. package/dist/translation-center/TranslationManagement.js +4 -4
  33. package/dist/translation-center/TranslationsTitlebar.d.ts.map +1 -1
  34. package/dist/translation-center/TranslationsTitlebar.js +2 -2
  35. package/package.json +1 -1
  36. package/dist/constants.d.ts +0 -15
  37. package/dist/constants.d.ts.map +0 -1
  38. package/dist/constants.js +0 -21
  39. package/dist/steps/MetadataInputStep.d.ts +0 -4
  40. package/dist/steps/MetadataInputStep.d.ts.map +0 -1
  41. package/dist/steps/MetadataInputStep.js +0 -48
  42. package/dist/steps/SubitemDiscoveryStep.d.ts +0 -3
  43. package/dist/steps/SubitemDiscoveryStep.d.ts.map +0 -1
  44. package/dist/steps/SubitemDiscoveryStep.js +0 -313
  45. package/dist/steps/index.d.ts +0 -6
  46. package/dist/steps/index.d.ts.map +0 -1
  47. package/dist/steps/index.js +0 -5
  48. package/dist/utils/createVersions.d.ts +0 -14
  49. package/dist/utils/createVersions.d.ts.map +0 -1
  50. package/dist/utils/createVersions.js +0 -26
@@ -10,9 +10,9 @@ export function TranslationManagement() {
10
10
  ?.flatMap((x) => x.panels)
11
11
  ?.find((panel) => panel.id === "translation-batches");
12
12
  if (!config) {
13
- return (_jsx("div", { className: "flex h-full flex-col items-center justify-center bg-[var(--color-gray-5)]", children: _jsxs("div", { className: "flex items-center gap-2 text-[var(--color-gray-2)]", children: [_jsx("i", { className: "pi pi-spin pi-spinner text-[#9650fb]" }), "Loading..."] }) }));
13
+ return (_jsx("div", { className: "flex h-full flex-col items-center justify-center bg-neutral-grey-5", children: _jsxs("div", { className: "flex items-center gap-2 text-neutral-grey-50", children: [_jsx("i", { className: "pi pi-spin pi-spinner text-[var(--color-highlight-100)]" }), "Loading..."] }) }));
14
14
  }
15
- const managementContent = translationBatchesPanel ? (_jsx("div", { className: "flex-1 min-h-0", children: translationBatchesPanel.content })) : (_jsxs("div", { className: "flex h-full flex-col items-center justify-center text-[var(--color-gray-2)]", children: [_jsx("i", { className: "pi pi-language mb-4 text-4xl text-[var(--color-gray-3)]" }), _jsx("p", { className: "font-medium text-[var(--color-gray-1)]", children: "Translation batches not available" })] }));
15
+ const managementContent = translationBatchesPanel ? (_jsx("div", { className: "flex-1 min-h-0", children: translationBatchesPanel.content })) : (_jsxs("div", { className: "flex h-full flex-col items-center justify-center text-neutral-grey-50", children: [_jsx("i", { className: "pi pi-language mb-4 text-4xl text-neutral-grey-15" }), _jsx("p", { className: "font-medium text-neutral-grey-100", children: "Translation batches not available" })] }));
16
16
  const panels = [
17
17
  {
18
18
  name: "translation-management",
@@ -24,8 +24,8 @@ export function TranslationManagement() {
24
24
  panels.push({
25
25
  name: "editor-preview",
26
26
  defaultSize: isMobile ? 45 : 500,
27
- content: (_jsx("div", { className: "h-full overflow-hidden border-l border-gray-200 bg-gray-50", children: _jsx(ItemEditorPane, { name: "translation-management-preview", emptyDescription: "Select a translation batch item or language row to preview and edit it here." }) })),
27
+ content: (_jsx("div", { className: "h-full overflow-hidden border-l border-border-default bg-neutral-grey-5", children: _jsx(ItemEditorPane, { name: "translation-management-preview", emptyDescription: "Select a translation batch item or language row to preview and edit it here." }) })),
28
28
  });
29
29
  }
30
- return (_jsx("div", { className: "absolute inset-0 flex flex-col bg-[var(--color-gray-5)]", "data-testid": "translation-management", children: _jsx(Splitter, { panels: panels, direction: isMobile ? "vertical" : "horizontal", localStorageKey: "translation-management.panelSizes" }) }));
30
+ return (_jsx("div", { className: "absolute inset-0 flex flex-col bg-neutral-grey-5", "data-testid": "translation-management", children: _jsx(Splitter, { panels: panels, direction: isMobile ? "vertical" : "horizontal", localStorageKey: "translation-management.panelSizes" }) }));
31
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TranslationsTitlebar.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationsTitlebar.tsx"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,wBAAgB,oBAAoB,mDA8CnC"}
1
+ {"version":3,"file":"TranslationsTitlebar.d.ts","sourceRoot":"","sources":["../../src/translation-center/TranslationsTitlebar.tsx"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,oBAAoB,mDA8CnC"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { SimpleIconButton, useEditContext, cn, } from "@parhelia/core";
2
+ import { SimpleIconButton, useEditContext, cn } from "@parhelia/core";
3
3
  import { SquarePen } from "lucide-react";
4
4
  /**
5
5
  * TranslationsTitlebar - Titlebar content for the Translation Management workspace.
@@ -12,5 +12,5 @@ export function TranslationsTitlebar() {
12
12
  return null;
13
13
  const showSubtitle = !editContext.isMobile;
14
14
  const { showAgentsWorkspaceEditor, setShowAgentsWorkspaceEditor } = editContext;
15
- return (_jsxs("div", { className: cn("flex w-full flex-row items-center gap-3 pl-2", editContext.isMobile && "border-b px-1.5 py-1"), children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col items-start justify-center leading-tight", "data-testid": "translations-titlebar-identity", children: [_jsx("span", { className: "truncate text-sm font-medium leading-tight text-gray-900", children: "Translation Management" }), showSubtitle && (_jsx("span", { className: "truncate text-[11px] leading-tight text-gray-500", children: "Track and manage content translations" }))] }), !editContext.isMobile && (_jsx("div", { className: "flex shrink-0 items-center gap-2", children: _jsx(SimpleIconButton, { icon: _jsx(SquarePen, { className: "h-5 w-5", strokeWidth: 1 }), label: showAgentsWorkspaceEditor ? "Hide Editor" : "Show Editor", size: "large", "data-testid": "translations-editor-panel-toggle", selected: showAgentsWorkspaceEditor, onClick: () => setShowAgentsWorkspaceEditor(!showAgentsWorkspaceEditor) }) }))] }));
15
+ return (_jsxs("div", { className: cn("flex w-full flex-row items-center gap-3 pl-2", editContext.isMobile && "border-b px-1.5 py-1"), children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col items-start justify-center leading-tight", "data-testid": "translations-titlebar-identity", children: [_jsx("span", { className: "truncate text-sm font-medium leading-tight text-neutral-grey-100", children: "Translation Management" }), showSubtitle && (_jsx("span", { className: "truncate text-[11px] leading-tight text-neutral-grey-50", children: "Track and manage content translations" }))] }), !editContext.isMobile && (_jsx("div", { className: "flex shrink-0 items-center gap-2", children: _jsx(SimpleIconButton, { icon: _jsx(SquarePen, { className: "h-5 w-5", strokeWidth: 1 }), label: showAgentsWorkspaceEditor ? "Hide Editor" : "Show Editor", size: "large", "data-testid": "translations-editor-panel-toggle", selected: showAgentsWorkspaceEditor, onClick: () => setShowAgentsWorkspaceEditor(!showAgentsWorkspaceEditor) }) }))] }));
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parhelia/localization",
3
- "version": "0.1.12808",
3
+ "version": "0.1.12836",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,15 +0,0 @@
1
- export declare const DISCOVERY_CONFIRMATION_THRESHOLD = 100;
2
- export declare const EXCLUDED_TEMPLATE_IDS: string[];
3
- export declare const DIALOG_DIMENSIONS: {
4
- width: string;
5
- height: string;
6
- };
7
- export declare const STEP_STATES: {
8
- readonly COMPLETED: "completed";
9
- readonly CURRENT: "current";
10
- readonly PENDING: "pending";
11
- };
12
- export declare const STORAGE_KEYS: {
13
- readonly TRANSLATION_PROVIDER: "editor.translationProvider";
14
- };
15
- //# sourceMappingURL=constants.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gCAAgC,MAAM,CAAC;AAGpD,eAAO,MAAM,qBAAqB,UAEjC,CAAC;AAGF,eAAO,MAAM,iBAAiB;;;CAG7B,CAAC;AAGF,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAGX,eAAO,MAAM,YAAY;;CAEf,CAAC"}
package/dist/constants.js DELETED
@@ -1,21 +0,0 @@
1
- // Discovery configuration
2
- export const DISCOVERY_CONFIRMATION_THRESHOLD = 100; // Pause every N subitems
3
- // Template IDs to exclude from translation
4
- export const EXCLUDED_TEMPLATE_IDS = [
5
- "de8bfdac-5f9f-40c3-a680-2fce9997ae28" // System template ID
6
- ];
7
- // Dialog dimensions
8
- export const DIALOG_DIMENSIONS = {
9
- width: "75vw",
10
- height: "75vh"
11
- };
12
- // Step progress indicators
13
- export const STEP_STATES = {
14
- COMPLETED: "completed",
15
- CURRENT: "current",
16
- PENDING: "pending"
17
- };
18
- // Local storage keys
19
- export const STORAGE_KEYS = {
20
- TRANSLATION_PROVIDER: "editor.translationProvider"
21
- };
@@ -1,4 +0,0 @@
1
- import { TranslationStepProps } from "./types";
2
- /** Example Step for metadata input testing */
3
- export declare function MetadataInputStep({ stepIndex, isActive, data, setData, onStepCompleted, }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
4
- //# sourceMappingURL=MetadataInputStep.d.ts.map
@@ -1 +0,0 @@
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,SAAS,EACT,QAAe,EACf,IAAI,EACJ,OAAO,EACP,eAAe,GAChB,EAAE,oBAAoB,2CAsFtB"}
@@ -1,48 +0,0 @@
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({ stepIndex, isActive = true, 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
- // Prefer the user's tree selection (per-item subitem flags) when present,
14
- // otherwise the legacy expanded list, otherwise the initial wizard items.
15
- const allItems = (data.selectionTreeItems && data.selectionTreeItems.length > 0
16
- ? data.selectionTreeItems.map((entry) => entry.descriptor)
17
- : null) ??
18
- (data.discoveredItems && data.discoveredItems.length > 0
19
- ? data.discoveredItems
20
- : data.items);
21
- const handleMetadataChange = (itemId, language, metadata) => {
22
- const updated = {
23
- ...itemMetadata,
24
- [itemId]: {
25
- ...(itemMetadata[itemId] || {}),
26
- [language]: metadata
27
- }
28
- };
29
- setItemMetadata(updated);
30
- // Convert back to Map structure for wizard data
31
- const metadataMap = new Map();
32
- Object.entries(updated).forEach(([itemId, languageMap]) => {
33
- metadataMap.set(itemId, new Map(Object.entries(languageMap)));
34
- });
35
- setData({ ...data, itemMetadata: metadataMap });
36
- console.log('Updated itemMetadata:', metadataMap);
37
- };
38
- // Data is saved automatically on change
39
- useEffect(() => {
40
- // Only update completion when this step is active
41
- if (!isActive)
42
- return;
43
- // Mark step as completed since metadata is optional
44
- onStepCompleted(true);
45
- }, [isActive, onStepCompleted]); // Check when active state changes
46
- // No footer actions needed since data is saved automatically
47
- return (_jsxs("div", { className: "flex flex-col gap-4 p-6", "data-testid": "metadata-input-step", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-xl font-semibold text-[var(--color-dark)] mb-2", children: "Enter Metadata Per Item Per Language" }), _jsx("p", { className: "text-sm text-[var(--color-gray-2)]", 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 border-[var(--color-gray-3)] rounded-lg p-4 bg-background shadow-sm", "data-testid": `metadata-item-${item.id}`, children: [_jsx("label", { className: "block text-sm font-medium text-[var(--color-dark)] mb-3", "data-testid": `metadata-item-label-${item.id}`, children: item.name || item.id }), data.targetLanguages?.map((language) => (_jsxs("div", { className: "mb-3", "data-testid": `metadata-field-${item.id}-${language}`, children: [_jsxs("label", { className: "block text-xs font-medium mb-1.5 text-[var(--color-gray-2)]", "data-testid": `metadata-language-label-${item.id}-${language}`, children: [language.toUpperCase(), ":"] }), _jsx("textarea", { className: "w-full border border-[var(--color-gray-3)] rounded-md p-2.5 text-sm bg-[var(--color-gray-5)] text-[var(--color-dark)] focus:outline-none focus:ring-2 focus:ring-[#9650fb] focus:border-[#9650fb] transition-colors", 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))) })] }));
48
- }
@@ -1,3 +0,0 @@
1
- import { TranslationStepProps } from "./types";
2
- export declare function SubitemDiscoveryStep({ stepIndex, isActive, data, setData, editContext, onStepCompleted, setBeforeNextCallback, setFooterActions, requestClose }: TranslationStepProps): import("react/jsx-runtime").JSX.Element;
3
- //# sourceMappingURL=SubitemDiscoveryStep.d.ts.map
@@ -1 +0,0 @@
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,2CAga5L"}
@@ -1,313 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState, useRef, useMemo, useCallback } from "react";
3
- import { Button, PerfectTree } from "@parhelia/core";
4
- import { convertFullItemToStub, convertStubToFullItem } from "@parhelia/core";
5
- import { discoverItemsTree, convertBackendTreeToTreeNodes, flattenSelectableItemsFromBackendTrees } from "../api/discovery";
6
- // We need to implement a basic Spinner component since it's not in core
7
- const Spinner = ({ ...props }) => (_jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-[#9650fb]", ...props }));
8
- export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData, editContext, onStepCompleted, setBeforeNextCallback, setFooterActions, requestClose }) {
9
- // Core state - simplified
10
- const [isDiscovering, setIsDiscovering] = useState(false);
11
- const [discoveryComplete, setDiscoveryComplete] = useState(false);
12
- const [selectedItemIds, setSelectedItemIds] = useState(new Set());
13
- const [treeNodes, setTreeNodes] = useState([]);
14
- const [allDiscoveredItems, setAllDiscoveredItems] = useState([]);
15
- const [userHasInteracted, setUserHasInteracted] = useState(false);
16
- const [discoveredCount, setDiscoveredCount] = useState(0);
17
- const [totalItemsCount, setTotalItemsCount] = useState(0);
18
- const [treeInitialized, setTreeInitialized] = useState(false);
19
- const [expandedIds, setExpandedIds] = useState(new Set());
20
- // Refs for async safety
21
- const isMountedRef = useRef(true);
22
- const inFlightRef = useRef(false);
23
- const shiftToggleRef = useRef(false);
24
- // Cleanup
25
- useEffect(() => {
26
- isMountedRef.current = true;
27
- return () => { isMountedRef.current = false; };
28
- }, []);
29
- // Commit callback - simplified
30
- useEffect(() => {
31
- const commit = async () => {
32
- const selectedItems = allDiscoveredItems.filter(item => selectedItemIds.has(item.id));
33
- const convertedItems = selectedItems.map(convertStubToFullItem);
34
- setData({ ...data, discoveredItems: convertedItems });
35
- return true;
36
- };
37
- setBeforeNextCallback?.(commit);
38
- }, [setBeforeNextCallback, allDiscoveredItems, selectedItemIds, setData, data]);
39
- // Initialize discovery or simple display
40
- useEffect(() => {
41
- if (!data.includeSubitems) {
42
- // Simple mode: just show base items
43
- const stubs = data.items.map(convertFullItemToStub);
44
- setAllDiscoveredItems(stubs);
45
- setDiscoveredCount(stubs.length);
46
- const nodes = buildTreeNodes(stubs);
47
- setTreeNodes(nodes);
48
- setExpandedIds(new Set(nodes.map(n => n.key)));
49
- const countItems = (nodeList) => {
50
- let total = 0;
51
- const walk = (nodeList) => {
52
- nodeList.forEach(node => {
53
- total++;
54
- if (node.children)
55
- walk(node.children);
56
- });
57
- };
58
- walk(nodeList);
59
- return total;
60
- };
61
- setTotalItemsCount(countItems(nodes));
62
- setDiscoveryComplete(true);
63
- setTreeInitialized(true);
64
- return;
65
- }
66
- // Discovery mode: fetch tree from backend
67
- if (inFlightRef.current)
68
- return;
69
- inFlightRef.current = true;
70
- const runDiscovery = async () => {
71
- setIsDiscovering(true);
72
- try {
73
- const rootIds = data.items.map(i => i.descriptor.id);
74
- const resp = await discoverItemsTree({
75
- rootItemIds: rootIds,
76
- language: editContext?.item?.language ??
77
- editContext?.currentItemDescriptor?.language ??
78
- "en",
79
- }, editContext.sessionId ?? undefined);
80
- if (!isMountedRef.current)
81
- return;
82
- const items = flattenSelectableItemsFromBackendTrees(resp.trees);
83
- const language = editContext?.item?.language ??
84
- editContext?.currentItemDescriptor?.language ??
85
- "en";
86
- const stubs = items.map(x => ({
87
- id: x.id,
88
- name: x.name,
89
- path: x.path,
90
- parentId: "",
91
- idPath: "",
92
- database: "master",
93
- descriptor: { id: x.id, language, version: 0 },
94
- icon: "",
95
- largeIcon: "",
96
- templateId: "",
97
- templateName: "",
98
- hasLayout: false,
99
- hasChildren: x.hasChildren,
100
- versions: 0,
101
- language,
102
- version: 0,
103
- }));
104
- setAllDiscoveredItems(stubs);
105
- setDiscoveredCount(stubs.length);
106
- const nodes = convertBackendTreeToTreeNodes(resp.trees);
107
- setTreeNodes(nodes);
108
- setExpandedIds(new Set(nodes.map(n => n.key)));
109
- const countItems = (nodeList) => {
110
- let total = 0;
111
- const walk = (nodeList) => {
112
- nodeList.forEach(node => {
113
- total++;
114
- if (node.children)
115
- walk(node.children);
116
- });
117
- };
118
- walk(nodeList);
119
- return total;
120
- };
121
- setTotalItemsCount(countItems(nodes));
122
- setDiscoveryComplete(true);
123
- setTreeInitialized(true);
124
- }
125
- catch (error) {
126
- console.error("Discovery error:", error);
127
- }
128
- finally {
129
- if (isMountedRef.current)
130
- setIsDiscovering(false);
131
- inFlightRef.current = false;
132
- }
133
- };
134
- runDiscovery();
135
- }, [data.items, data.includeSubitems, editContext]);
136
- // Set initial selection when tree is ready
137
- useEffect(() => {
138
- if (!isActive || !discoveryComplete || treeNodes.length === 0 || userHasInteracted)
139
- return;
140
- let initialSelection;
141
- if (data.discoveredItems?.length > 0) {
142
- // Restore previous selection
143
- initialSelection = new Set(data.discoveredItems.map(item => item.descriptor.id));
144
- }
145
- else {
146
- // Default: select all selectable items in the tree
147
- const allSelectableIds = new Set();
148
- const walk = (nodes) => {
149
- nodes.forEach(node => {
150
- if (node?.data?.isFolder !== true) {
151
- allSelectableIds.add(node.key);
152
- }
153
- if (node.children)
154
- walk(node.children);
155
- });
156
- };
157
- walk(treeNodes);
158
- initialSelection = allSelectableIds;
159
- }
160
- setSelectedItemIds(initialSelection);
161
- }, [isActive, discoveryComplete, treeNodes, data.discoveredItems, userHasInteracted]);
162
- // Update completion status
163
- useEffect(() => {
164
- if (isActive && discoveryComplete) {
165
- onStepCompleted(selectedItemIds.size > 0);
166
- }
167
- }, [isActive, discoveryComplete, selectedItemIds.size, onStepCompleted]);
168
- const buildTreeNodes = useCallback((items) => {
169
- // Fallback: Build simple tree from items for non-subitem discovery
170
- const itemMap = new Map();
171
- const childrenMap = new Map();
172
- items.forEach(item => {
173
- itemMap.set(item.id, item);
174
- if (item.parentId) {
175
- if (!childrenMap.has(item.parentId))
176
- childrenMap.set(item.parentId, []);
177
- childrenMap.get(item.parentId).push(item);
178
- }
179
- });
180
- const buildNode = (item) => ({
181
- key: item.id,
182
- label: item.name || item.id,
183
- data: { id: item.id, name: item.name, path: item.path, isFolder: item.hasLayout !== true, hasChildren: item.hasChildren },
184
- children: (childrenMap.get(item.id) || []).map(buildNode)
185
- });
186
- const originalItemIds = new Set(data.items.map(item => item.descriptor.id));
187
- const rootItems = items.filter(item => !item.parentId || !itemMap.has(item.parentId) || originalItemIds.has(item.id));
188
- return rootItems.map(buildNode);
189
- }, [data.items]);
190
- // PerfectTree integrations
191
- const expandedKeys = useMemo(() => Array.from(expandedIds), [expandedIds]);
192
- const selectedKeys = useMemo(() => Array.from(selectedItemIds), [selectedItemIds]);
193
- const getDescendantIds = (nodeKey) => {
194
- const findNode = (nodes) => {
195
- for (const node of nodes) {
196
- if (node.key === nodeKey)
197
- return node;
198
- if (node.children) {
199
- const found = findNode(node.children);
200
- if (found)
201
- return found;
202
- }
203
- }
204
- return null;
205
- };
206
- const node = findNode(treeNodes);
207
- if (!node?.children)
208
- return [];
209
- const descendants = [];
210
- const traverse = (children) => {
211
- children.forEach(child => {
212
- descendants.push(child.key);
213
- if (child.children)
214
- traverse(child.children);
215
- });
216
- };
217
- traverse(node.children);
218
- return descendants;
219
- };
220
- const handleCancel = () => {
221
- // Simplified cancel - just stop discovery
222
- inFlightRef.current = false;
223
- setIsDiscovering(false);
224
- setDiscoveryComplete(true);
225
- };
226
- const showDiscoveryStatus = isDiscovering || !discoveryComplete;
227
- return (_jsx("div", { className: "flex h-full min-h-0 flex-col gap-4 p-6", "data-testid": "subitem-discovery-step", children: _jsxs("div", { className: "flex min-h-0 w-full min-w-0 flex-1 flex-col rounded-lg border border-[var(--color-gray-3)] bg-background p-4 shadow-sm", "data-testid": "discovery-status-container", children: [showDiscoveryStatus && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-4 flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [isDiscovering && _jsx(Spinner, { "data-testid": "discovery-spinner" }), _jsx("span", { className: "font-medium text-[var(--color-dark)]", "data-testid": "discovery-status-text", children: isDiscovering ? 'Discovering subitems...' : 'Ready to discover' })] }), isDiscovering && (_jsx(Button, { size: "sm", variant: "outline", onClick: handleCancel, "data-testid": "discovery-cancel-button", children: "Cancel" }))] }), _jsx("div", { className: "mb-3 text-sm text-[var(--color-gray-2)]", "data-testid": "discovery-summary", children: discoveredCount > 0 ? (_jsxs("p", { "data-testid": "discovery-total-count", children: [_jsxs("span", { className: "font-medium text-[#9650fb]", children: [totalItemsCount, " total items found"] }), _jsx("br", {}), _jsxs("span", { className: "text-xs text-[var(--color-gray-2)]", children: ["Original items: ", data.items.length, " | Subitems discovered: ", Math.max(0, totalItemsCount - data.items.length)] }), isDiscovering && (_jsxs(_Fragment, { children: [_jsx("br", {}), _jsx("span", { className: "text-xs text-[var(--color-gray-2)] italic", children: "Item tree will appear when discovery completes" })] }))] })) : (_jsx("p", { className: "text-[var(--color-gray-2)]", children: "No items discovered yet." })) })] })), _jsx("div", { className: showDiscoveryStatus ? "mt-4 flex min-h-0 flex-1 flex-col" : "flex min-h-0 flex-1 flex-col", children: !isDiscovering && discoveryComplete && treeInitialized && allDiscoveredItems.length > 0 && treeNodes.length > 0 ? (_jsxs("div", { className: "flex min-h-0 w-full min-w-0 flex-1 flex-col", "data-testid": "item-selection-section", children: [_jsxs("div", { className: showDiscoveryStatus ? "mb-3 flex flex-col gap-2 border-t border-[var(--color-gray-3)] pt-4" : "mb-3 flex flex-col gap-2", children: [_jsx("h3", { className: "text-lg font-semibold text-[var(--color-dark)]", children: "Select Items to Translate" }), _jsx("span", { className: "text-sm text-[var(--color-gray-2)]", "data-testid": "selection-summary-header", children: selectedItemIds.size > 0 ? _jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium text-[#9650fb]", children: selectedItemIds.size }), " selected"] }) : "No items selected" }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => {
228
- if (!isActive)
229
- return;
230
- const allIds = new Set();
231
- const walk = (nodes) => {
232
- nodes.forEach(node => {
233
- allIds.add(node.key);
234
- if (node.children)
235
- walk(node.children);
236
- });
237
- };
238
- walk(treeNodes);
239
- setUserHasInteracted(true);
240
- setSelectedItemIds(allIds);
241
- onStepCompleted(allIds.size > 0);
242
- }, "data-testid": "select-all-items-button", children: "Select All" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
243
- if (!isActive)
244
- return;
245
- setUserHasInteracted(true);
246
- setSelectedItemIds(new Set());
247
- onStepCompleted(false);
248
- }, "data-testid": "select-none-items-button", children: "Select None" })] })] }), _jsx("div", { className: "text-xs text-[var(--color-gray-2)] mb-3", "data-testid": "selection-summary", children: _jsx("span", { children: "Tip: Hold Shift and click a checkbox to toggle all descendants." }) }), treeNodes.length > 0 && (_jsx("div", { className: "min-h-0 w-full min-w-0 flex-1 overflow-auto rounded-lg border border-[var(--color-gray-3)] bg-[var(--color-gray-5)]", "data-testid": "item-tree-view", children: _jsx(PerfectTree, { nodes: treeNodes, expandedKeys: expandedKeys, selectedKeys: selectedKeys, onToggleExpand: (key) => {
249
- setExpandedIds(prev => {
250
- const next = new Set(prev);
251
- if (next.has(key))
252
- next.delete(key);
253
- else
254
- next.add(key);
255
- return next;
256
- });
257
- }, onSelect: (key, event) => {
258
- if (!isActive)
259
- return; // Don't handle selection when inactive
260
- setUserHasInteracted(true);
261
- const targetNode = key;
262
- const shift = event?.shiftKey === true;
263
- const next = new Set(selectedItemIds);
264
- const findNode = (nodes) => {
265
- for (const n of nodes) {
266
- if (n.key === targetNode)
267
- return n;
268
- if (n.children) {
269
- const f = findNode(n.children);
270
- if (f)
271
- return f;
272
- }
273
- }
274
- return null;
275
- };
276
- const n = findNode(treeNodes);
277
- if (!n)
278
- return;
279
- const ids = shift ? [key, ...getDescendantIds(key)] : [key];
280
- if (!next.has(key))
281
- ids.forEach((id) => next.add(id));
282
- else
283
- ids.forEach((id) => next.delete(id));
284
- setSelectedItemIds(next);
285
- // Update completion when user changes selection
286
- onStepCompleted(next.size > 0);
287
- }, renderNode: (node) => {
288
- const isChecked = selectedItemIds.has(node.key);
289
- const icon = node?.data?.icon;
290
- return (_jsxs("div", { className: "flex items-center gap-2", children: [icon && (_jsx("img", { src: icon, alt: "", className: "w-4 h-4" })), _jsx("input", { type: "checkbox", checked: isChecked, "data-testid": `tree-item-checkbox-${node.key}`, onMouseDown: (e) => {
291
- shiftToggleRef.current = e.shiftKey;
292
- }, onChange: (e) => {
293
- if (!isActive)
294
- return;
295
- setUserHasInteracted(true);
296
- const next = new Set(selectedItemIds);
297
- const withDesc = shiftToggleRef.current && (node.children?.length ?? 0) > 0;
298
- const ids = withDesc ? [node.key, ...getDescendantIds(node.key)] : [node.key];
299
- if (e.currentTarget.checked)
300
- ids.forEach((id) => next.add(id));
301
- else
302
- ids.forEach((id) => next.delete(id));
303
- setSelectedItemIds(next);
304
- shiftToggleRef.current = false;
305
- // Update completion when user changes selection
306
- onStepCompleted(next.size > 0);
307
- } }), _jsx("span", { children: node.label })] }));
308
- } }) }))] })) : isDiscovering ? (
309
- // Loading skeleton for discovery results
310
- _jsxs("div", { className: "flex w-full min-w-0 flex-col", children: [_jsx("div", { className: "p-3 bg-[#f6eeff] border border-[#9650fb]/20 rounded-lg mb-4", children: _jsx("div", { className: "h-4 bg-[#9650fb]/20 rounded w-64 animate-pulse" }) }), _jsxs("div", { className: "flex min-h-0 flex-1 flex-col border-t border-[var(--color-gray-3)] pt-4", children: [_jsxs("div", { className: "mb-3 flex flex-col gap-2", children: [_jsx("div", { className: "h-6 bg-[var(--color-gray-3)] rounded w-48 animate-pulse" }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-40 animate-pulse" }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx("div", { className: "h-8 bg-[var(--color-gray-3)] rounded w-20 animate-pulse" }), _jsx("div", { className: "h-8 bg-[var(--color-gray-3)] rounded w-20 animate-pulse" })] })] }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-64 mb-3 animate-pulse" }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto rounded-lg border border-[var(--color-gray-3)] bg-[var(--color-gray-5)]", children: _jsx("div", { className: "p-3 space-y-2", children: [...Array(8)].map((_, i) => (_jsxs("div", { className: "flex items-center gap-3 animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "h-4 bg-[var(--color-gray-3)] rounded w-48" })] }, i))) }) })] })] })) : (
311
- // Empty state
312
- _jsx("div", { className: "flex h-80 shrink-0 items-center justify-center overflow-auto rounded-lg border border-[var(--color-gray-3)] bg-[var(--color-gray-5)]", children: _jsxs("div", { className: "text-center text-[var(--color-gray-2)]", children: [_jsx("p", { className: "font-medium text-[var(--color-gray-1)]", children: "Ready to discover subitems" }), _jsx("p", { className: "text-sm mt-1", children: "Discovery will start automatically" })] }) })) })] }) }));
313
- }
@@ -1,6 +0,0 @@
1
- export { ServiceLanguageSelectionStep } from "./ServiceLanguageSelectionStep";
2
- export { ItemSelectionStep } from "./ItemSelectionStep";
3
- export { SubitemDiscoveryStep } from "./SubitemDiscoveryStep";
4
- export { MetadataInputStep } from "./MetadataInputStep";
5
- export * from "./types";
6
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/steps/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,cAAc,SAAS,CAAC"}
@@ -1,5 +0,0 @@
1
- export { ServiceLanguageSelectionStep } from "./ServiceLanguageSelectionStep";
2
- export { ItemSelectionStep } from "./ItemSelectionStep";
3
- export { SubitemDiscoveryStep } from "./SubitemDiscoveryStep";
4
- export { MetadataInputStep } from "./MetadataInputStep";
5
- export * from "./types";
@@ -1,14 +0,0 @@
1
- import { EditContextType } from "@parhelia/core";
2
- import { TranslationWizardData, TranslationDialogResult } from "../steps/types";
3
- export type ItemVersionDescriptor = {
4
- id: string;
5
- version?: number;
6
- };
7
- export declare function createVersionsOnlyAndClose(params: {
8
- items: ItemVersionDescriptor[];
9
- targetLanguages: string[];
10
- editContext: EditContextType;
11
- data: TranslationWizardData;
12
- requestClose: (result: TranslationDialogResult | null) => void;
13
- }): Promise<void>;
14
- //# sourceMappingURL=createVersions.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createVersions.d.ts","sourceRoot":"","sources":["../../src/utils/createVersions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGhF,MAAM,MAAM,qBAAqB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE;IACN,KAAK,EAAE,qBAAqB,EAAE,CAAC;IAC/B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,eAAe,CAAC;IAC7B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,YAAY,EAAE,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,KAAK,IAAI,CAAC;CAChE,GACA,OAAO,CAAC,IAAI,CAAC,CAyBf"}
@@ -1,26 +0,0 @@
1
- import { createVersion as createVersionEdit, createVersionsBatch as createVersionsBatchEdit } from "@parhelia/core";
2
- export async function createVersionsOnlyAndClose(params) {
3
- const { items, targetLanguages, editContext, data, requestClose } = params;
4
- try {
5
- if (items.length === 1 && targetLanguages.length === 1) {
6
- const item = items[0];
7
- await createVersionEdit({ id: item.id, language: targetLanguages[0], version: item.version || 0 }, editContext.sessionId);
8
- }
9
- else {
10
- const descriptors = items.flatMap(it => targetLanguages.map(lang => ({ id: it.id, language: lang, version: it.version || 0 })));
11
- await createVersionsBatchEdit(descriptors, editContext.sessionId);
12
- }
13
- editContext?.showToast?.("Versions created");
14
- const result = {
15
- translationProvider: data.translationProvider,
16
- targetLanguages: data.targetLanguages,
17
- includeSubitems: data.includeSubitems,
18
- discoveredItems: data.discoveredItems,
19
- };
20
- requestClose(result);
21
- }
22
- catch (e) {
23
- console.error("Create versions failed", e);
24
- editContext?.showToast?.("Failed to create versions");
25
- }
26
- }