@parhelia/localization 0.1.12468 → 0.1.12470

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.
@@ -4,6 +4,7 @@ export type DiscoveredItem = {
4
4
  path: string;
5
5
  parentId?: string | null;
6
6
  hasChildren: boolean;
7
+ hasLayout?: boolean;
7
8
  };
8
9
  export type TreeNode = {
9
10
  key: string;
@@ -15,6 +16,8 @@ export type BackendTreeNode = {
15
16
  id: string;
16
17
  name: string;
17
18
  path: string;
19
+ hasLayout: boolean;
20
+ isTranslatable: boolean;
18
21
  isFolder: boolean;
19
22
  icon?: string;
20
23
  children: BackendTreeNode[];
@@ -30,5 +33,5 @@ export type DiscoverItemsTreeResponse = {
30
33
  };
31
34
  export declare function discoverItemsTree(req: DiscoverItemsTreeRequest, sessionId?: string): Promise<DiscoverItemsTreeResponse>;
32
35
  export declare function convertBackendTreeToTreeNodes(trees: BackendTreeNode[]): TreeNode[];
33
- export declare function flattenSelectableItemsFromBackendTrees(trees: BackendTreeNode[]): DiscoveredItem[];
36
+ export declare function flattenPagesFromBackendTrees(trees: BackendTreeNode[]): DiscoveredItem[];
34
37
  //# sourceMappingURL=discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/api/discovery.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAInG,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,yBAAyB,CAAC,CAWpC;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,CAQlF;AAED,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,EAAE,CAQjG"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/api/discovery.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAInG,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,wBAAwB,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,yBAAyB,CAAC,CAWpC;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,CAQlF;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,EAAE,CAQvF"}
@@ -12,15 +12,16 @@ export function convertBackendTreeToTreeNodes(trees) {
12
12
  const convert = (n) => ({
13
13
  key: n.id,
14
14
  label: n.name,
15
- data: { id: n.id, name: n.name, path: n.path, isFolder: n.isFolder, icon: n.icon, parentId: undefined, hasChildren: (n.children?.length ?? 0) > 0 },
15
+ data: { id: n.id, name: n.name, path: n.path, hasLayout: n.hasLayout, isTranslatable: n.isTranslatable, icon: n.icon, parentId: undefined, hasChildren: (n.children?.length ?? 0) > 0 },
16
16
  children: n.children?.map(convert) || [],
17
17
  });
18
18
  return trees.map(convert);
19
19
  }
20
- export function flattenSelectableItemsFromBackendTrees(trees) {
20
+ export function flattenPagesFromBackendTrees(trees) {
21
21
  const out = [];
22
22
  const walk = (n) => {
23
- out.push({ id: n.id, name: n.name, path: n.path, hasChildren: (n.children?.length ?? 0) > 0, parentId: undefined });
23
+ if (n.isTranslatable)
24
+ out.push({ id: n.id, name: n.name, path: n.path, hasChildren: (n.children?.length ?? 0) > 0, parentId: undefined });
24
25
  n.children?.forEach(walk);
25
26
  };
26
27
  trees.forEach(walk);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAatD,OAAO,EACL,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AAUxB,QAAA,MAAM,wCAAwC;;;;;;;;;;;;8BAalB,qBAAqB;;CAchD,CAAC;AAEF,QAAA,MAAM,4CAA4C;;;;;;;CAUjD,CAAC;AAkCF,OAAO,EAAE,wCAAwC,EAAE,4CAA4C,EAAE,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,mBAAmB,EAClC,OAAO,CAAC,EAAE,yBAAyB,uBAsHpC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAatD,OAAO,EACL,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AAUxB,QAAA,MAAM,wCAAwC;;;;;;;;;;;;8BAalB,qBAAqB;;CAchD,CAAC;AAEF,QAAA,MAAM,4CAA4C;;;;;;;CAUjD,CAAC;AAkCF,OAAO,EAAE,wCAAwC,EAAE,4CAA4C,EAAE,CAAC;AAClG,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,mBAAmB,EAClC,OAAO,CAAC,EAAE,yBAAyB,uBAiIpC"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { localizeItemCommand } from "./LocalizeItemCommand";
6
6
  import { TranslationSidebar } from "./sidebar/TranslationSidebar";
7
7
  import { TranslationManagement } from "./translation-center/TranslationManagement";
8
8
  import { RecentTranslations } from "./translation-center/RecentTranslations";
9
+ import { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
9
10
  import { TranslationServicesPanel } from "./settings/TranslationServicesPanel";
10
11
  import { Languages as LucideLanguages, LayoutGrid as LucideLayoutGrid, Globe as LucideGlobe, } from "lucide-react";
11
12
  const LanguagesIcon = LucideLanguages;
@@ -178,10 +179,20 @@ export function configureLocalization(configuration, options) {
178
179
  icon: _jsx(LanguagesIcon, { strokeWidth: 1 }),
179
180
  content: _jsx(TranslationServicesPanel, {}),
180
181
  };
182
+ const localizationSetupPanel = {
183
+ id: "localization-setup",
184
+ title: "Localization Setup",
185
+ subtitle: "Verify translation services and create provider settings items",
186
+ icon: _jsx(LanguagesIcon, { strokeWidth: 1 }),
187
+ content: _jsx(LocalizationSetupStep, {}),
188
+ };
181
189
  const ensureLocalizationPanels = (group) => {
182
190
  if (!group.panels.some((p) => p.id === "translation-services")) {
183
191
  group.panels.push(translationServicesPanel);
184
192
  }
193
+ if (!group.panels.some((p) => p.id === "localization-setup")) {
194
+ group.panels.push(localizationSetupPanel);
195
+ }
185
196
  };
186
197
  if (localizationGroupIndex >= 0) {
187
198
  const group = configuration.settings.groups[localizationGroupIndex];
@@ -195,7 +206,7 @@ export function configureLocalization(configuration, options) {
195
206
  const localizationGroup = {
196
207
  title: "Localization",
197
208
  icon: _jsx(LanguagesIcon, { strokeWidth: 1 }),
198
- panels: [translationServicesPanel],
209
+ panels: [translationServicesPanel, localizationSetupPanel],
199
210
  };
200
211
  if (aboutIndex >= 0) {
201
212
  groups.splice(aboutIndex, 0, localizationGroup);
@@ -1 +1 @@
1
- {"version":3,"file":"TranslationServicesPanel.d.ts","sourceRoot":"","sources":["../../src/settings/TranslationServicesPanel.tsx"],"names":[],"mappings":"AAgEA;;;GAGG;AACH,wBAAgB,wBAAwB,4CAktBvC;AAED,eAAe,wBAAwB,CAAC"}
1
+ {"version":3,"file":"TranslationServicesPanel.d.ts","sourceRoot":"","sources":["../../src/settings/TranslationServicesPanel.tsx"],"names":[],"mappings":"AA6DA;;;GAGG;AACH,wBAAgB,wBAAwB,4CA+mBvC;AAED,eAAe,wBAAwB,CAAC"}
@@ -1,14 +1,13 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useState, useEffect, } from "react";
3
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";
4
+ import { CheckCircle as LucideCheckCircle, AlertCircle as LucideAlertCircle, RefreshCw as LucideRefreshCw, Plus as LucidePlus, Settings as LucideSettings, } 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;
12
11
  const DEFAULT_STRUCTURE_SETTINGS = {
13
12
  translationFolderPath: "/sitecore/system/Parhelia/Settings/Translation",
14
13
  translationFolderTemplateId: "b1e40cfe-6e36-4a0e-a49e-19c10b2127b7",
@@ -119,7 +118,6 @@ export function TranslationServicesPanel() {
119
118
  const serviceStates = availableServices.map((svc) => ({
120
119
  ...svc,
121
120
  creating: false,
122
- deleting: false,
123
121
  }));
124
122
  setServices(serviceStates);
125
123
  setState(availableServices.length === 0 ? "error" : "success");
@@ -195,53 +193,6 @@ export function TranslationServicesPanel() {
195
193
  setServices((prev) => prev.map((s) => s.serviceName === serviceName ? { ...s, creating: false } : s));
196
194
  }
197
195
  }, [editContext, loadData, structureSettings]);
198
- const deleteSettings = useCallback(async (service) => {
199
- if (!editContext || !service.settingsItemId)
200
- return;
201
- try {
202
- setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
203
- ? { ...s, deleting: true }
204
- : s));
205
- setError(null);
206
- const language = editContext.item?.language ??
207
- editContext.currentItemDescriptor?.language ??
208
- "en";
209
- const deleted = await editContext.operations.deleteItems([
210
- {
211
- id: service.settingsItemId,
212
- language,
213
- version: 0,
214
- },
215
- ]);
216
- if (deleted) {
217
- if (selectedConfigTarget?.key === `service:${service.serviceName}` ||
218
- selectedConfigTarget?.itemId === service.settingsItemId) {
219
- setSelectedConfigTarget(null);
220
- }
221
- setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
222
- ? {
223
- ...s,
224
- isConfigured: false,
225
- settingsItemId: undefined,
226
- deleting: false,
227
- }
228
- : s));
229
- await loadData();
230
- }
231
- else {
232
- setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
233
- ? { ...s, deleting: false }
234
- : s));
235
- }
236
- }
237
- catch (e) {
238
- setError(e?.message ||
239
- `Failed to delete settings for ${service.displayName || service.serviceName}`);
240
- setServices((prev) => prev.map((s) => s.serviceName === service.serviceName
241
- ? { ...s, deleting: false }
242
- : s));
243
- }
244
- }, [editContext, loadData, selectedConfigTarget]);
245
196
  useEffect(() => {
246
197
  loadData();
247
198
  }, [loadData]);
@@ -283,7 +234,7 @@ export function TranslationServicesPanel() {
283
234
  : "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 &&
284
235
  service.displayName !== service.serviceName && (_jsxs("div", { className: "text-xs text-gray-500 truncate", children: ["Service: ", service.serviceName] })), service.isConfigured &&
285
236
  service.supportedLanguages &&
286
- 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));
237
+ 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(Button, { size: "sm", variant: isSelected ? "default" : "ghost", onClick: () => handleOpenConfig(service), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), editContext?.isMobile ? "" : "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));
287
238
  }) })), error && (_jsx("div", { className: "rounded border border-red-200 bg-red-50 p-3 text-sm text-red-700 whitespace-pre-wrap", children: error }))] }) }) }) }));
288
239
  // Build splitter panels - only show config panel when a service is selected
289
240
  const panels = selectedConfigTarget?.itemId
@@ -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,2CAoa5L"}
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,2CA4f5L"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useState, useRef, useMemo, useCallback } from "react";
3
3
  import { Button, PerfectTree } from "@parhelia/core";
4
4
  import { convertFullItemToStub, convertStubToFullItem } from "@parhelia/core";
5
- import { discoverItemsTree, convertBackendTreeToTreeNodes, flattenSelectableItemsFromBackendTrees } from "../api/discovery";
5
+ import { discoverItemsTree, convertBackendTreeToTreeNodes, flattenPagesFromBackendTrees } from "../api/discovery";
6
6
  // We need to implement a basic Spinner component since it's not in core
7
7
  const Spinner = ({ ...props }) => (_jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-[#9650fb]", ...props }));
8
8
  export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData, editContext, onStepCompleted, setBeforeNextCallback, setFooterActions, requestClose }) {
@@ -15,6 +15,7 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
15
15
  const [userHasInteracted, setUserHasInteracted] = useState(false);
16
16
  const [discoveredCount, setDiscoveredCount] = useState(0);
17
17
  const [totalItemsCount, setTotalItemsCount] = useState(0);
18
+ const [translatableItemsCount, setTranslatableItemsCount] = useState(0);
18
19
  const [treeInitialized, setTreeInitialized] = useState(false);
19
20
  const [expandedIds, setExpandedIds] = useState(new Set());
20
21
  // Refs for async safety
@@ -46,19 +47,26 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
46
47
  const nodes = buildTreeNodes(stubs);
47
48
  setTreeNodes(nodes);
48
49
  setExpandedIds(new Set(nodes.map(n => n.key)));
50
+ // Count total items and translatable items in the tree
49
51
  const countItems = (nodeList) => {
50
52
  let total = 0;
53
+ let translatable = 0;
51
54
  const walk = (nodeList) => {
52
55
  nodeList.forEach(node => {
53
56
  total++;
57
+ if (node?.data?.isTranslatable === true) {
58
+ translatable++;
59
+ }
54
60
  if (node.children)
55
61
  walk(node.children);
56
62
  });
57
63
  };
58
64
  walk(nodeList);
59
- return total;
65
+ return { total, translatable };
60
66
  };
61
- setTotalItemsCount(countItems(nodes));
67
+ const counts = countItems(nodes);
68
+ setTotalItemsCount(counts.total);
69
+ setTranslatableItemsCount(counts.translatable);
62
70
  setDiscoveryComplete(true);
63
71
  setTreeInitialized(true);
64
72
  return;
@@ -79,11 +87,11 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
79
87
  }, editContext.sessionId ?? undefined);
80
88
  if (!isMountedRef.current)
81
89
  return;
82
- const items = flattenSelectableItemsFromBackendTrees(resp.trees);
90
+ const pages = flattenPagesFromBackendTrees(resp.trees);
83
91
  const language = editContext?.item?.language ??
84
92
  editContext?.currentItemDescriptor?.language ??
85
93
  "en";
86
- const stubs = items.map(x => ({
94
+ const stubs = pages.map(x => ({
87
95
  id: x.id,
88
96
  name: x.name,
89
97
  path: x.path,
@@ -106,19 +114,26 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
106
114
  const nodes = convertBackendTreeToTreeNodes(resp.trees);
107
115
  setTreeNodes(nodes);
108
116
  setExpandedIds(new Set(nodes.map(n => n.key)));
117
+ // Count total items and translatable items in the tree
109
118
  const countItems = (nodeList) => {
110
119
  let total = 0;
120
+ let translatable = 0;
111
121
  const walk = (nodeList) => {
112
122
  nodeList.forEach(node => {
113
123
  total++;
124
+ if (node?.data?.isTranslatable === true) {
125
+ translatable++;
126
+ }
114
127
  if (node.children)
115
128
  walk(node.children);
116
129
  });
117
130
  };
118
131
  walk(nodeList);
119
- return total;
132
+ return { total, translatable };
120
133
  };
121
- setTotalItemsCount(countItems(nodes));
134
+ const counts = countItems(nodes);
135
+ setTotalItemsCount(counts.total);
136
+ setTranslatableItemsCount(counts.translatable);
122
137
  setDiscoveryComplete(true);
123
138
  setTreeInitialized(true);
124
139
  }
@@ -143,19 +158,19 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
143
158
  initialSelection = new Set(data.discoveredItems.map(item => item.descriptor.id));
144
159
  }
145
160
  else {
146
- // Default: select all selectable items in the tree
147
- const allSelectableIds = new Set();
161
+ // Default: select all translatable items in the tree
162
+ const allTranslatableIds = new Set();
148
163
  const walk = (nodes) => {
149
164
  nodes.forEach(node => {
150
- if (node?.data?.isFolder !== true) {
151
- allSelectableIds.add(node.key);
165
+ if (node?.data?.isTranslatable) {
166
+ allTranslatableIds.add(node.key);
152
167
  }
153
168
  if (node.children)
154
169
  walk(node.children);
155
170
  });
156
171
  };
157
172
  walk(treeNodes);
158
- initialSelection = allSelectableIds;
173
+ initialSelection = allTranslatableIds;
159
174
  }
160
175
  setSelectedItemIds(initialSelection);
161
176
  }, [isActive, discoveryComplete, treeNodes, data.discoveredItems, userHasInteracted]);
@@ -180,7 +195,7 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
180
195
  const buildNode = (item) => ({
181
196
  key: item.id,
182
197
  label: item.name || item.id,
183
- data: { id: item.id, name: item.name, path: item.path, isFolder: item.hasLayout !== true, hasChildren: item.hasChildren },
198
+ data: { id: item.id, name: item.name, path: item.path, hasChildren: item.hasChildren },
184
199
  children: (childrenMap.get(item.id) || []).map(buildNode)
185
200
  });
186
201
  const originalItemIds = new Set(data.items.map(item => item.descriptor.id));
@@ -190,6 +205,8 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
190
205
  // PerfectTree integrations
191
206
  const expandedKeys = useMemo(() => Array.from(expandedIds), [expandedIds]);
192
207
  const selectedKeys = useMemo(() => Array.from(selectedItemIds), [selectedItemIds]);
208
+ // Memoize base item IDs for checkbox rendering
209
+ const baseItemIdsForCheckbox = useMemo(() => new Set(data.items.map(item => item.descriptor.id)), [data.items]);
193
210
  const getDescendantIds = (nodeKey) => {
194
211
  const findNode = (nodes) => {
195
212
  for (const node of nodes) {
@@ -217,6 +234,35 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
217
234
  traverse(node.children);
218
235
  return descendants;
219
236
  };
237
+ const getDescendantTranslatableIds = (nodeKey) => {
238
+ const findNode = (nodes) => {
239
+ for (const node of nodes) {
240
+ if (node.key === nodeKey)
241
+ return node;
242
+ if (node.children) {
243
+ const found = findNode(node.children);
244
+ if (found)
245
+ return found;
246
+ }
247
+ }
248
+ return null;
249
+ };
250
+ const root = findNode(treeNodes);
251
+ if (!root?.children)
252
+ return [];
253
+ const out = [];
254
+ const walk = (children) => {
255
+ children.forEach(child => {
256
+ const isTranslatable = child?.data?.isTranslatable === true;
257
+ if (isTranslatable)
258
+ out.push(child.key);
259
+ if (child.children)
260
+ walk(child.children);
261
+ });
262
+ };
263
+ walk(root.children);
264
+ return out;
265
+ };
220
266
  const handleCancel = () => {
221
267
  // Simplified cancel - just stop discovery
222
268
  inFlightRef.current = false;
@@ -225,21 +271,24 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
225
271
  };
226
272
  return (_jsxs("div", { className: "flex min-h-0 flex-col gap-4 p-6", "data-testid": "subitem-discovery-step", children: [_jsxs("div", { className: "mb-2 shrink-0", children: [_jsx("h3", { className: "text-lg font-semibold text-[var(--color-dark)] mb-1", children: "Discover Subitems" }), _jsx("p", { className: "text-sm text-[var(--color-gray-2)]", children: "Scanning for subitems to include in translation..." })] }), _jsxs("div", { className: "flex w-full min-w-0 shrink-0 flex-col rounded-lg border border-[var(--color-gray-3)] bg-background p-4 shadow-sm", "data-testid": "discovery-status-container", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", 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...' :
227
273
  discoveryComplete ? 'Discovery Complete' :
228
- 'Ready to discover' })] }), isDiscovering && (_jsx(Button, { size: "sm", variant: "outline", onClick: handleCancel, "data-testid": "discovery-cancel-button", children: "Cancel" }))] }), _jsx("div", { className: "text-sm text-[var(--color-gray-2)] mb-3", "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: "mt-4 flex flex-col", children: !isDiscovering && discoveryComplete && treeInitialized && allDiscoveredItems.length > 0 && treeNodes.length > 0 ? (_jsxs("div", { className: "flex w-full min-w-0 flex-col", "data-testid": "item-selection-section", children: [_jsxs("div", { className: "mb-3 flex flex-col gap-2 border-t border-[var(--color-gray-3)] pt-4", 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: () => {
274
+ 'Ready to discover' })] }), isDiscovering && (_jsx(Button, { size: "sm", variant: "outline", onClick: handleCancel, "data-testid": "discovery-cancel-button", children: "Cancel" }))] }), _jsx("div", { className: "text-sm text-[var(--color-gray-2)] mb-3", "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"] }), totalItemsCount !== translatableItemsCount && (_jsxs(_Fragment, { children: [_jsx("br", {}), _jsxs("span", { className: "text-xs text-[var(--color-gray-2)]", children: ["Translatable items: ", translatableItemsCount, " | Non-translatable: ", totalItemsCount - translatableItemsCount] })] })), _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: "mt-4 flex flex-col", children: !isDiscovering && discoveryComplete && treeInitialized && allDiscoveredItems.length > 0 && treeNodes.length > 0 ? (_jsxs("div", { className: "flex w-full min-w-0 flex-col", "data-testid": "item-selection-section", children: [_jsxs("div", { className: "mb-3 flex flex-col gap-2 border-t border-[var(--color-gray-3)] pt-4", 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: () => {
229
275
  if (!isActive)
230
276
  return;
231
- const allIds = new Set();
277
+ // Only select translatable items
278
+ const translatableIds = new Set();
232
279
  const walk = (nodes) => {
233
280
  nodes.forEach(node => {
234
- allIds.add(node.key);
281
+ if (node?.data?.isTranslatable === true) {
282
+ translatableIds.add(node.key);
283
+ }
235
284
  if (node.children)
236
285
  walk(node.children);
237
286
  });
238
287
  };
239
288
  walk(treeNodes);
240
289
  setUserHasInteracted(true);
241
- setSelectedItemIds(allIds);
242
- onStepCompleted(allIds.size > 0);
290
+ setSelectedItemIds(translatableIds);
291
+ onStepCompleted(translatableIds.size > 0);
243
292
  }, "data-testid": "select-all-items-button", children: "Select All" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
244
293
  if (!isActive)
245
294
  return;
@@ -262,6 +311,7 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
262
311
  const targetNode = key;
263
312
  const shift = event?.shiftKey === true;
264
313
  const next = new Set(selectedItemIds);
314
+ // Only select translatable items
265
315
  const findNode = (nodes) => {
266
316
  for (const n of nodes) {
267
317
  if (n.key === targetNode)
@@ -275,37 +325,73 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
275
325
  return null;
276
326
  };
277
327
  const n = findNode(treeNodes);
278
- if (!n)
279
- return;
280
- const ids = shift ? [key, ...getDescendantIds(key)] : [key];
281
- if (!next.has(key))
282
- ids.forEach((id) => next.add(id));
283
- else
284
- ids.forEach((id) => next.delete(id));
328
+ const isTranslatable = n?.data?.isTranslatable === true;
329
+ if (isTranslatable) {
330
+ const ids = shift ? [key, ...getDescendantIds(key)] : [key];
331
+ if (!next.has(key))
332
+ ids.forEach((id) => next.add(id));
333
+ else
334
+ ids.forEach((id) => next.delete(id));
335
+ }
336
+ else {
337
+ // Folder row click toggles all descendant translatable items
338
+ const translatableIds = getDescendantTranslatableIds(key);
339
+ const allSelected = translatableIds.length > 0 && translatableIds.every((id) => next.has(id));
340
+ if (allSelected)
341
+ translatableIds.forEach((id) => next.delete(id));
342
+ else
343
+ translatableIds.forEach((id) => next.add(id));
344
+ }
285
345
  setSelectedItemIds(next);
286
346
  // Update completion when user changes selection
287
347
  onStepCompleted(next.size > 0);
288
348
  }, renderNode: (node) => {
289
- const isChecked = selectedItemIds.has(node.key);
349
+ const isTranslatable = node?.data?.isTranslatable === true;
350
+ const translatableIds = !isTranslatable ? getDescendantTranslatableIds(node.key) : [];
351
+ const allSelected = !isTranslatable ? (translatableIds.length > 0 && translatableIds.every((id) => selectedItemIds.has(id))) : false;
352
+ const someSelected = !isTranslatable ? (translatableIds.some((id) => selectedItemIds.has(id)) && !allSelected) : false;
353
+ // Simple checkbox logic: if it's translatable, check direct selection; otherwise check descendant translatable items
354
+ // Base items are treated like any other selected item - if selected, show checked
355
+ const isChecked = isTranslatable ? selectedItemIds.has(node.key) : allSelected;
290
356
  const icon = node?.data?.icon;
291
- 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) => {
357
+ return (_jsxs("div", { className: `flex items-center gap-2 ${!isTranslatable ? 'opacity-70' : ''}`, children: [icon && (_jsx("img", { src: icon, alt: "", className: `w-4 h-4 ${!isTranslatable ? 'opacity-50' : ''}` })), _jsx("input", { type: "checkbox", checked: isChecked, disabled: !isTranslatable, "data-testid": `tree-item-checkbox-${node.key}`, ref: (el) => { if (el)
358
+ el.indeterminate = someSelected; }, onMouseDown: (e) => {
359
+ if (!isTranslatable)
360
+ return; // Don't handle mouse down for untranslatable items
292
361
  shiftToggleRef.current = e.shiftKey;
293
362
  }, onChange: (e) => {
294
- if (!isActive)
295
- return;
363
+ if (!isActive || !isTranslatable)
364
+ return; // Don't handle changes when inactive or untranslatable
296
365
  setUserHasInteracted(true);
297
366
  const next = new Set(selectedItemIds);
298
- const withDesc = shiftToggleRef.current && (node.children?.length ?? 0) > 0;
299
- const ids = withDesc ? [node.key, ...getDescendantIds(node.key)] : [node.key];
300
- if (e.currentTarget.checked)
301
- ids.forEach((id) => next.add(id));
302
- else
303
- ids.forEach((id) => next.delete(id));
367
+ if (isTranslatable) {
368
+ const withDesc = shiftToggleRef.current && (node.children?.length ?? 0) > 0;
369
+ const ids = withDesc ? [node.key, ...getDescendantIds(node.key)] : [node.key];
370
+ if (e.currentTarget.checked)
371
+ ids.forEach((id) => next.add(id));
372
+ else
373
+ ids.forEach((id) => next.delete(id));
374
+ }
375
+ else {
376
+ const ids = getDescendantTranslatableIds(node.key);
377
+ const allSel = ids.length > 0 && ids.every((id) => next.has(id));
378
+ if (e.currentTarget.checked && !allSel)
379
+ ids.forEach((id) => next.add(id));
380
+ else if (!e.currentTarget.checked && allSel)
381
+ ids.forEach((id) => next.delete(id));
382
+ else {
383
+ // Toggle based on current
384
+ if (allSel)
385
+ ids.forEach((id) => next.delete(id));
386
+ else
387
+ ids.forEach((id) => next.add(id));
388
+ }
389
+ }
304
390
  setSelectedItemIds(next);
305
391
  shiftToggleRef.current = false;
306
392
  // Update completion when user changes selection
307
393
  onStepCompleted(next.size > 0);
308
- } }), _jsx("span", { children: node.label })] }));
394
+ } }), _jsx("span", { className: `${!isTranslatable ? 'opacity-70 italic' : ''}`, children: node.label })] }));
309
395
  } }) }))] })) : isDiscovering ? (
310
396
  // Loading skeleton for discovery results
311
397
  _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: "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: "border border-[var(--color-gray-3)] rounded-lg h-80 shrink-0 overflow-auto 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))) }) })] })] })) : (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parhelia/localization",
3
- "version": "0.1.12468",
3
+ "version": "0.1.12470",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"