@parhelia/localization 0.1.12368 → 0.1.12390
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LocalizeItemDialog.d.ts.map +1 -1
- package/dist/LocalizeItemDialog.js +13 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -27
- package/dist/settings/TranslationServicesPanel.d.ts.map +1 -1
- package/dist/settings/TranslationServicesPanel.js +5 -6
- package/dist/steps/ServiceLanguageSelectionStep.d.ts.map +1 -1
- package/dist/steps/ServiceLanguageSelectionStep.js +18 -1
- package/dist/steps/SubitemDiscoveryStep.d.ts.map +1 -1
- package/dist/steps/SubitemDiscoveryStep.js +120 -120
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EAExB,MAAM,eAAe,CAAC;AAuBvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CA0btE"}
|
|
@@ -2,16 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, Button, cn, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@parhelia/core";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { AlertTriangle as LucideAlertTriangle } from "lucide-react";
|
|
5
|
-
import { ChevronRight as LucideChevronRight } from "lucide-react";
|
|
6
5
|
import { useTranslationWizard } from "./hooks/useTranslationWizard";
|
|
7
6
|
import { performDefaultTranslation, generateBatchId } from "./LocalizeItemUtils";
|
|
8
|
-
const ChevronRightIcon = LucideChevronRight;
|
|
9
7
|
const AlertTriangleIcon = LucideAlertTriangle;
|
|
10
8
|
// Threshold for warning about large batch translations
|
|
11
9
|
const LARGE_BATCH_WARNING_THRESHOLD = 100;
|
|
12
10
|
// Note: DialogButtons is an internal component that might need to be added to core exports
|
|
13
11
|
// For now, we'll implement it inline
|
|
14
|
-
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "flex justify-end gap-2
|
|
12
|
+
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "mt-auto flex flex-wrap items-stretch justify-end gap-2 gap-y-2 border-t border-[var(--color-gray-3)] bg-background px-6 pt-4 pb-6", ...props, children: children }));
|
|
15
13
|
export function LocalizeItemDialog(props) {
|
|
16
14
|
const editContext = props.editContext;
|
|
17
15
|
const configuration = editContext.configuration.translationWizard;
|
|
@@ -222,25 +220,23 @@ export function LocalizeItemDialog(props) {
|
|
|
222
220
|
if (!open) {
|
|
223
221
|
props.onClose?.(null);
|
|
224
222
|
}
|
|
225
|
-
}, children: [_jsxs(DialogContent, { className: "
|
|
226
|
-
width:
|
|
227
|
-
height:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
? "text-[#9650fb]"
|
|
237
|
-
: "text-[var(--color-gray-2)]"), "data-testid": `step-indicator-label-${step.id}`, children: step.name }), index < activeSteps.length - 1 && (_jsx(ChevronRightIcon, { className: "w-4 h-4 text-[var(--color-gray-3)] mx-2", strokeWidth: 1 }))] }, step.id))) }) }), _jsx("div", { className: "flex-1 overflow-y-auto min-h-0 bg-[var(--color-gray-5)]", "data-testid": "translation-wizard-step-content", children: _jsx("div", { className: "h-full relative", children: activeSteps.map((step, index) => {
|
|
223
|
+
}, children: [_jsxs(DialogContent, { className: "flex h-[85vh] max-h-[900px] min-h-0 w-[90vw] max-w-5xl flex-col overflow-hidden md:min-h-[700px]", "data-testid": "translation-wizard-dialog", onPointerDownOutside: (e) => e.preventDefault(), onEscapeKeyDown: (e) => e.preventDefault(), "aria-describedby": "translation-wizard-description", style: {
|
|
224
|
+
width: "min(90vw, 1280px)",
|
|
225
|
+
height: "min(85vh, 900px)",
|
|
226
|
+
}, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { "data-testid": "translation-wizard-title", children: "Translation Wizard" }), _jsx("div", { id: "translation-wizard-description", className: "sr-only", children: currentStep?.description || "Configure and start translation for your content." })] }), _jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [_jsx("div", { className: "border-b border-[var(--color-gray-3)] bg-background px-6", "data-testid": "translation-wizard-step-navigation", children: _jsx("div", { className: "flex min-w-0 flex-nowrap items-center gap-1 overflow-x-auto py-2.5 md:gap-1.5", children: activeSteps.map((step, index) => {
|
|
227
|
+
const isCurrent = currentStepIndex === index;
|
|
228
|
+
return (_jsxs("div", { className: "flex shrink-0 items-center gap-1", "data-testid": `step-indicator-${step.id}`, "aria-current": isCurrent ? "step" : undefined, children: [_jsx("div", { className: cn("flex h-8 w-8 shrink-0 items-center justify-center rounded-full border text-sm font-medium transition-colors", isCurrent && "border-[#9650fb] bg-[#f6eeff] text-[#9650fb]", currentStepIndex < index && "border-[var(--color-gray-3)] text-[var(--color-gray-2)]", currentStepIndex > index && "border-[#9650fb] bg-[#9650fb] text-white"), style: currentStepIndex > index ? { backgroundColor: "#9650fb", color: "#ffffff" } : undefined, "data-testid": `step-indicator-circle-${step.id}`, "aria-label": step.name, children: currentStepIndex > index ? "✓" : index + 1 }), _jsx("span", { className: cn("inline-block text-sm align-middle transition-[max-width,opacity] duration-200 ease-out md:max-w-none md:opacity-100", isCurrent
|
|
229
|
+
? "max-w-[min(calc(100vw-5rem),20rem)] overflow-visible text-[#9650fb] font-medium max-md:opacity-100 md:max-w-none"
|
|
230
|
+
: currentStepIndex > index
|
|
231
|
+
? "overflow-hidden text-[#9650fb] max-md:pointer-events-none max-md:max-w-0 max-md:opacity-0"
|
|
232
|
+
: "overflow-hidden text-[var(--color-gray-2)] max-md:pointer-events-none max-md:max-w-0 max-md:opacity-0"), "data-testid": `step-indicator-label-${step.id}`, children: _jsx("span", { className: cn(isCurrent ? "whitespace-normal break-words md:whitespace-nowrap" : "whitespace-nowrap"), children: step.name }) })] }, step.id));
|
|
233
|
+
}) }) }), _jsx("div", { className: "flex-1 overflow-y-auto min-h-0 bg-[var(--color-gray-5)]", "data-testid": "translation-wizard-step-content", children: _jsx("div", { className: "h-full relative", children: activeSteps.map((step, index) => {
|
|
238
234
|
const StepComponent = step.component;
|
|
239
235
|
const isActive = index === currentStepIndex;
|
|
240
236
|
if (!StepComponent)
|
|
241
237
|
return null;
|
|
242
238
|
return (_jsx("div", { className: "h-full", style: { display: isActive ? 'block' : 'none' }, "aria-hidden": !isActive, "data-testid": `step-content-${step.id}`, children: _jsx(StepComponent, { stepIndex: index, isActive: isActive, data: wizardData, setData: setWizardDataStable, editContext: editContext, onStepCompleted: (completed) => handleStepCompleted(completed, index), setBeforeNextCallback: isActive ? setBeforeNextCallbackStable : undefined, setFooterActions: isActive ? provideFooterActions : undefined, requestClose: isActive ? requestCloseCb : undefined }) }, step.id));
|
|
243
|
-
}) }) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [_jsx(Button, { onClick: () => props.onClose?.(null), variant: "outline", size: "default", "data-testid": "translation-wizard-cancel-button", children: "Cancel" }), currentStepIndex > 0 && (_jsx(Button, { onClick: handlePrevious, variant: "outline", size: "default", "data-testid": "translation-wizard-previous-button", children: "Previous" })), footerActions.map((a) => (_jsx(Button, { onClick: a.onClick, disabled: !!a.disabled, variant: "default", size: "default", "data-testid": `translation-wizard-footer-action-${a.key}`, children: a.label }, a.key))), _jsx(Button, { onClick: handleNext, disabled: !canProceed || isSubmitting, variant: "default", size: "default", "data-testid": "translation-wizard-next-button", children: isSubmitting ? "Starting..." : isLastStep ? "Start Translation" : "Next" })] })] })] }), _jsx(AlertDialog, { open: showLargeBatchWarning, onOpenChange: setShowLargeBatchWarning, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsxs(AlertDialogTitle, { className: "flex items-center gap-2", children: [_jsx(AlertTriangleIcon, { className: "h-5 w-5 text-amber-500" }), "Large Translation Batch"] }), _jsx(AlertDialogDescription, { children: (() => {
|
|
239
|
+
}) }) }), _jsxs(DialogButtons, { "data-testid": "translation-wizard-dialog-buttons", children: [_jsx(Button, { onClick: () => props.onClose?.(null), variant: "outline", size: "default", className: "max-md:hidden", "data-testid": "translation-wizard-cancel-button", children: "Cancel" }), _jsxs("div", { className: "flex min-h-10 min-w-0 flex-1 flex-wrap items-stretch justify-end gap-2 md:contents", children: [currentStepIndex > 0 && (_jsx(Button, { onClick: handlePrevious, variant: "outline", size: "default", className: "min-w-0 flex-1 md:min-w-[6.5rem] md:flex-initial", "data-testid": "translation-wizard-previous-button", children: "Previous" })), footerActions.map((a) => (_jsx(Button, { onClick: a.onClick, disabled: !!a.disabled, variant: "default", size: "default", className: "min-w-0 flex-1 md:flex-initial md:min-w-[6.5rem]", "data-testid": `translation-wizard-footer-action-${a.key}`, children: a.label }, a.key))), _jsx(Button, { onClick: handleNext, disabled: !canProceed || isSubmitting, variant: "default", size: "default", className: "min-w-0 flex-1 md:min-w-[6.5rem] md:flex-initial", "data-testid": "translation-wizard-next-button", children: isSubmitting ? "Starting..." : isLastStep ? "Start Translation" : "Next" })] })] })] })] }), _jsx(AlertDialog, { open: showLargeBatchWarning, onOpenChange: setShowLargeBatchWarning, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsxs(AlertDialogTitle, { className: "flex items-center gap-2", children: [_jsx(AlertTriangleIcon, { className: "h-5 w-5 text-amber-500" }), "Large Translation Batch"] }), _jsx(AlertDialogDescription, { children: (() => {
|
|
244
240
|
const { itemCount, languageCount, totalTranslations } = calculateTranslationCounts(wizardData);
|
|
245
241
|
return (_jsxs(_Fragment, { children: ["You are about to start ", _jsxs("strong", { children: [totalTranslations, " translations"] }), " (", itemCount, " items \u00D7 ", languageCount, " languages). Large batches may take a significant amount of time to process.", _jsx("br", {}), _jsx("br", {}), "Are you sure you want to continue?"] }));
|
|
246
242
|
})() })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isSubmitting, children: "Cancel" }), _jsx(AlertDialogAction, { onClick: handleLargeBatchConfirm, disabled: isSubmitting, children: isSubmitting ? "Starting..." : "Continue" })] })] }) })] }));
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
@@ -110,15 +110,6 @@ export * from "./steps/types";
|
|
|
110
110
|
export function configureLocalization(configuration, options) {
|
|
111
111
|
const enableMultiItem = options?.multiItem !== false; // Default to true
|
|
112
112
|
const enableTranslationManagement = options?.translationManagement !== false; // Default to true
|
|
113
|
-
// Add localization setup step
|
|
114
|
-
if (!configuration.setup) {
|
|
115
|
-
configuration.setup = { steps: [] };
|
|
116
|
-
}
|
|
117
|
-
// Check if the step is already added (to prevent duplicates)
|
|
118
|
-
const hasLocalizationSetup = configuration.setup.steps.some((step) => step === LocalizationSetupStep || step.name === "LocalizationSetupStep");
|
|
119
|
-
if (!hasLocalizationSetup) {
|
|
120
|
-
configuration.setup.steps.push(LocalizationSetupStep);
|
|
121
|
-
}
|
|
122
113
|
// Initialize the translation wizard configuration if it doesn't exist
|
|
123
114
|
if (!configuration.translationWizard) {
|
|
124
115
|
configuration.translationWizard = enableMultiItem
|
|
@@ -188,35 +179,40 @@ export function configureLocalization(configuration, options) {
|
|
|
188
179
|
icon: _jsx(LanguagesIcon, { strokeWidth: 1 }),
|
|
189
180
|
content: _jsx(TranslationServicesPanel, {}),
|
|
190
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
|
+
};
|
|
189
|
+
const ensureLocalizationPanels = (group) => {
|
|
190
|
+
if (!group.panels.some((p) => p.id === "translation-services")) {
|
|
191
|
+
group.panels.push(translationServicesPanel);
|
|
192
|
+
}
|
|
193
|
+
if (!group.panels.some((p) => p.id === "localization-setup")) {
|
|
194
|
+
group.panels.push(localizationSetupPanel);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
191
197
|
if (localizationGroupIndex >= 0) {
|
|
192
|
-
// Group exists, check if panel is already added
|
|
193
198
|
const group = configuration.settings.groups[localizationGroupIndex];
|
|
194
199
|
if (group) {
|
|
195
|
-
|
|
196
|
-
if (!hasPanel) {
|
|
197
|
-
group.panels.push(translationServicesPanel);
|
|
198
|
-
}
|
|
200
|
+
ensureLocalizationPanels(group);
|
|
199
201
|
}
|
|
200
202
|
}
|
|
201
203
|
else {
|
|
202
|
-
// Group doesn't exist, create it
|
|
203
204
|
const groups = configuration.settings.groups;
|
|
204
205
|
const aboutIndex = groups.findIndex((g) => g.title === "About");
|
|
206
|
+
const localizationGroup = {
|
|
207
|
+
title: "Localization",
|
|
208
|
+
icon: _jsx(LanguagesIcon, { strokeWidth: 1 }),
|
|
209
|
+
panels: [translationServicesPanel, localizationSetupPanel],
|
|
210
|
+
};
|
|
205
211
|
if (aboutIndex >= 0) {
|
|
206
|
-
|
|
207
|
-
groups.splice(aboutIndex, 0, {
|
|
208
|
-
title: "Localization",
|
|
209
|
-
icon: _jsx(LanguagesIcon, { strokeWidth: 1 }),
|
|
210
|
-
panels: [translationServicesPanel],
|
|
211
|
-
});
|
|
212
|
+
groups.splice(aboutIndex, 0, localizationGroup);
|
|
212
213
|
}
|
|
213
214
|
else {
|
|
214
|
-
|
|
215
|
-
groups.push({
|
|
216
|
-
title: "Localization",
|
|
217
|
-
icon: _jsx(LanguagesIcon, { strokeWidth: 1 }),
|
|
218
|
-
panels: [translationServicesPanel],
|
|
219
|
-
});
|
|
215
|
+
groups.push(localizationGroup);
|
|
220
216
|
}
|
|
221
217
|
}
|
|
222
218
|
return configuration;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranslationServicesPanel.d.ts","sourceRoot":"","sources":["../../src/settings/TranslationServicesPanel.tsx"],"names":[],"mappings":"AA6DA;;;GAGG;AACH,wBAAgB,wBAAwB,
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useState, useEffect, } from "react";
|
|
3
|
-
import { Button, useEditContext, Splitter, ItemConfigPanel,
|
|
3
|
+
import { Button, useEditContext, Splitter, ItemConfigPanel, } from "@parhelia/core";
|
|
4
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;
|
|
@@ -28,7 +28,6 @@ export function TranslationServicesPanel() {
|
|
|
28
28
|
const [structureState, setStructureState] = useState(null);
|
|
29
29
|
const [structureLoading, setStructureLoading] = useState(false);
|
|
30
30
|
const [ensuringStructure, setEnsuringStructure] = useState(false);
|
|
31
|
-
const isMobile = useMediaQuery("(max-width: 768px)");
|
|
32
31
|
const handleOpenConfig = useCallback((service) => {
|
|
33
32
|
if (service.isConfigured && service.settingsItemId) {
|
|
34
33
|
setSelectedConfigTarget({
|
|
@@ -218,14 +217,14 @@ export function TranslationServicesPanel() {
|
|
|
218
217
|
structureState?.translationFolder?.itemId && (_jsxs(Button, { size: "sm", variant: selectedConfigTarget?.key ===
|
|
219
218
|
`structure:${structureState.translationFolder.itemId}`
|
|
220
219
|
? "default"
|
|
221
|
-
: "ghost", onClick: () => handleOpenStructureConfig(structureState.translationFolder.itemId, "Configure: Translation folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), isMobile ? "" : "Configure"] })) })] }), _jsxs("div", { className: `flex items-center justify-between rounded border p-3 text-sm transition-shadow hover:shadow-sm ${selectedConfigTarget?.key ===
|
|
220
|
+
: "ghost", onClick: () => handleOpenStructureConfig(structureState.translationFolder.itemId, "Configure: Translation folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), editContext?.isMobile ? "" : "Configure"] })) })] }), _jsxs("div", { className: `flex items-center justify-between rounded border p-3 text-sm transition-shadow hover:shadow-sm ${selectedConfigTarget?.key ===
|
|
222
221
|
`structure:${structureState?.translationProvidersFolder?.itemId}`
|
|
223
222
|
? "border-blue-400 bg-blue-50"
|
|
224
223
|
: "border-gray-200 bg-white"}`, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0", children: [providersFolderExists ? (_jsx(CheckCircleIcon, { className: "h-4 w-4 text-green-600 shrink-0", strokeWidth: 1.5 })) : (_jsx(AlertCircleIcon, { className: "h-4 w-4 text-amber-500 shrink-0", strokeWidth: 1.5 })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-gray-900", children: "Translation providers folder" }), _jsx("div", { className: "text-xs text-gray-500 truncate", children: structureSettings.translationProvidersPath })] })] }), _jsx("div", { className: "flex items-center gap-2 ml-4 shrink-0", children: providersFolderExists &&
|
|
225
224
|
structureState?.translationProvidersFolder?.itemId && (_jsxs(Button, { size: "sm", variant: selectedConfigTarget?.key ===
|
|
226
225
|
`structure:${structureState.translationProvidersFolder.itemId}`
|
|
227
226
|
? "default"
|
|
228
|
-
: "ghost", onClick: () => handleOpenStructureConfig(structureState.translationProvidersFolder.itemId, "Configure: Translation Providers folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), isMobile ? "" : "Configure"] })) })] })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-gray-900", children: "Providers" }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [statusIcon(state), _jsx("span", { className: "text-sm text-gray-700", children: totalCount > 0
|
|
227
|
+
: "ghost", onClick: () => handleOpenStructureConfig(structureState.translationProvidersFolder.itemId, "Configure: Translation Providers folder"), className: "shrink-0", children: [_jsx(SettingsIcon, { className: "h-4 w-4", strokeWidth: 1.5 }), editContext?.isMobile ? "" : "Configure"] })) })] })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-gray-900", children: "Providers" }), _jsxs("div", { className: "mt-1 flex items-center gap-2", children: [statusIcon(state), _jsx("span", { className: "text-sm text-gray-700", children: totalCount > 0
|
|
229
228
|
? `${configuredCount} of ${totalCount} service${totalCount !== 1 ? "s" : ""} configured`
|
|
230
229
|
: "No services found" })] })] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: loadData, children: [_jsx(RefreshCwIcon, { strokeWidth: 1, className: "h-4 w-4" }), "Refresh"] })] }), totalCount === 0 && state === "success" && (_jsx("div", { className: "rounded border border-yellow-200 bg-yellow-50 p-3 text-sm text-yellow-800", children: "No translation services are registered in dependency injection. Please check the server configuration." })), services.length > 0 && (_jsx("div", { className: "space-y-1", children: services.map((service) => {
|
|
231
230
|
const isSelected = selectedConfigTarget?.key ===
|
|
@@ -235,7 +234,7 @@ export function TranslationServicesPanel() {
|
|
|
235
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 &&
|
|
236
235
|
service.displayName !== service.serviceName && (_jsxs("div", { className: "text-xs text-gray-500 truncate", children: ["Service: ", service.serviceName] })), service.isConfigured &&
|
|
237
236
|
service.supportedLanguages &&
|
|
238
|
-
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 }), 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));
|
|
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));
|
|
239
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 }))] }) }) }) }));
|
|
240
239
|
// Build splitter panels - only show config panel when a service is selected
|
|
241
240
|
const panels = selectedConfigTarget?.itemId
|
|
@@ -262,6 +261,6 @@ export function TranslationServicesPanel() {
|
|
|
262
261
|
},
|
|
263
262
|
];
|
|
264
263
|
const SplitterComponent = Splitter;
|
|
265
|
-
return (_jsx(SplitterComponent, { panels: panels, localStorageKey: "translation-services-panel-splitter", direction: isMobile ? "vertical" : "horizontal", className: "h-full w-full" }));
|
|
264
|
+
return (_jsx(SplitterComponent, { panels: panels, localStorageKey: "translation-services-panel-splitter", direction: editContext?.isMobile ? "vertical" : "horizontal", className: "h-full w-full" }));
|
|
266
265
|
}
|
|
267
266
|
export default TranslationServicesPanel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ServiceLanguageSelectionStep.d.ts","sourceRoot":"","sources":["../../src/steps/ServiceLanguageSelectionStep.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAyB,MAAM,SAAS,CAAC;AAkBtE,wBAAgB,4BAA4B,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,2CA8Z7K"}
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
// Version creation now offered via VersionOnlyTranslationProvider. No custom button here.
|
|
4
|
+
function getTranslationStatusBadgeClass(status) {
|
|
5
|
+
switch (status) {
|
|
6
|
+
case "Completed":
|
|
7
|
+
return "text-green-700 bg-green-100";
|
|
8
|
+
case "Error":
|
|
9
|
+
return "text-red-700 bg-red-100";
|
|
10
|
+
case "In Progress":
|
|
11
|
+
return "text-purple-800 bg-purple-100";
|
|
12
|
+
case "Not started":
|
|
13
|
+
return "text-[var(--color-gray-2)] bg-[var(--color-gray-4)]";
|
|
14
|
+
default:
|
|
15
|
+
return "text-[var(--color-gray-2)] bg-[var(--color-gray-4)]";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
4
18
|
export function ServiceLanguageSelectionStep({ stepIndex, isActive = true, data, setData, onStepCompleted, editContext, setFooterActions, requestClose }) {
|
|
5
19
|
const [languageSelection, setLanguageSelection] = useState({});
|
|
6
20
|
const dataRef = useRef(data);
|
|
@@ -220,5 +234,8 @@ export function ServiceLanguageSelectionStep({ stepIndex, isActive = true, data,
|
|
|
220
234
|
}
|
|
221
235
|
}, onChange: handleSelectAllLanguages, className: "h-4 w-4 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": "select-all-languages-checkbox" }), _jsx("span", { className: "ml-2 text-xs text-[var(--color-gray-2)]", children: "Select All" })] }))] }), _jsx("p", { className: "text-xs text-[var(--color-gray-2)] mb-3", children: "Select the languages you want to translate this content into." }), _jsx("div", { className: "border border-[var(--color-gray-3)] rounded-lg min-h-[200px] max-h-64 overflow-y-auto bg-[var(--color-gray-5)]", children: data.translationProviders.length === 0 || allLanguages.length === 0 ? (
|
|
222
236
|
// Loading skeleton
|
|
223
|
-
_jsx("div", { className: "p-3 space-y-2", children: [...Array(4)].map((_, i) => (_jsxs("div", { className: "flex items-center animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "ml-2 h-4 bg-[var(--color-gray-3)] rounded w-32" }), _jsx("div", { className: "ml-auto h-4 bg-[var(--color-gray-3)] rounded w-16" })] }, i))) })) : (_jsx("div", { className: "p-3 grid grid-cols-1 gap-2", children: allLanguages.map((lang) =>
|
|
237
|
+
_jsx("div", { className: "p-3 space-y-2", children: [...Array(4)].map((_, i) => (_jsxs("div", { className: "flex items-center animate-pulse", children: [_jsx("div", { className: "h-4 w-4 bg-[var(--color-gray-3)] rounded" }), _jsx("div", { className: "ml-2 h-4 bg-[var(--color-gray-3)] rounded w-32" }), _jsx("div", { className: "ml-auto h-4 bg-[var(--color-gray-3)] rounded w-16" })] }, i))) })) : (_jsx("div", { className: "p-3 grid grid-cols-1 gap-2", children: allLanguages.map((lang) => {
|
|
238
|
+
const statusLabel = lang.translationStatus?.status || "Not started";
|
|
239
|
+
return (_jsxs("label", { className: "flex items-start gap-2 py-1.5 px-2 rounded-md hover:bg-[var(--color-gray-4)] transition-colors cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: languageSelection[lang.code] || false, onChange: () => handleLanguageToggle(lang.code), className: "mt-0.5 h-4 w-4 shrink-0 text-[#9650fb] focus:ring-[#9650fb] border-[var(--color-gray-3)] rounded accent-[#9650fb]", "data-testid": `language-checkbox-${lang.code}` }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "text-sm text-[var(--color-dark)]", children: [lang.name, " (", lang.code, ")"] }), _jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-[var(--color-gray-2)]", children: [lang.sourceLanguage && _jsxs("span", { children: ["from: ", lang.sourceLanguage] }), !lang.hasVersions && (_jsx("span", { className: "text-2xs text-orange-600 bg-orange-100 px-2 py-1 rounded-full font-medium", children: "No versions" })), lang.hasVersions && (_jsx("span", { title: lang.translationStatus?.message, className: `text-2xs px-2 py-1 rounded-full font-medium ${getTranslationStatusBadgeClass(statusLabel)}`, "data-testid": `translation-status-badge-${lang.code}`, children: statusLabel }))] })] })] }, lang.code));
|
|
240
|
+
}) })) })] })] })] }));
|
|
224
241
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubitemDiscoveryStep.d.ts","sourceRoot":"","sources":["../../src/steps/SubitemDiscoveryStep.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAW/C,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,QAAe,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,YAAY,EAAE,EAAE,oBAAoB,
|
|
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"}
|
|
@@ -269,132 +269,132 @@ export function SubitemDiscoveryStep({ stepIndex, isActive = true, data, setData
|
|
|
269
269
|
setIsDiscovering(false);
|
|
270
270
|
setDiscoveryComplete(true);
|
|
271
271
|
};
|
|
272
|
-
return (_jsxs("div", { className: "flex flex-col gap-4 p-6
|
|
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...' :
|
|
273
273
|
discoveryComplete ? 'Discovery Complete' :
|
|
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
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
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: () => {
|
|
275
|
+
if (!isActive)
|
|
276
|
+
return;
|
|
277
|
+
// Only select translatable items
|
|
278
|
+
const translatableIds = new Set();
|
|
279
|
+
const walk = (nodes) => {
|
|
280
|
+
nodes.forEach(node => {
|
|
281
|
+
if (node?.data?.isTranslatable === true) {
|
|
282
|
+
translatableIds.add(node.key);
|
|
283
|
+
}
|
|
284
|
+
if (node.children)
|
|
285
|
+
walk(node.children);
|
|
286
|
+
});
|
|
287
|
+
};
|
|
288
|
+
walk(treeNodes);
|
|
289
|
+
setUserHasInteracted(true);
|
|
290
|
+
setSelectedItemIds(translatableIds);
|
|
291
|
+
onStepCompleted(translatableIds.size > 0);
|
|
292
|
+
}, "data-testid": "select-all-items-button", children: "Select All" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
|
|
293
|
+
if (!isActive)
|
|
294
|
+
return;
|
|
295
|
+
setUserHasInteracted(true);
|
|
296
|
+
setSelectedItemIds(new Set());
|
|
297
|
+
onStepCompleted(false);
|
|
298
|
+
}, "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: "h-80 w-full min-w-0 shrink-0 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) => {
|
|
299
|
+
setExpandedIds(prev => {
|
|
300
|
+
const next = new Set(prev);
|
|
301
|
+
if (next.has(key))
|
|
302
|
+
next.delete(key);
|
|
303
|
+
else
|
|
304
|
+
next.add(key);
|
|
305
|
+
return next;
|
|
306
|
+
});
|
|
307
|
+
}, onSelect: (key, event) => {
|
|
308
|
+
if (!isActive)
|
|
309
|
+
return; // Don't handle selection when inactive
|
|
310
|
+
setUserHasInteracted(true);
|
|
311
|
+
const targetNode = key;
|
|
312
|
+
const shift = event?.shiftKey === true;
|
|
313
|
+
const next = new Set(selectedItemIds);
|
|
314
|
+
// Only select translatable items
|
|
315
|
+
const findNode = (nodes) => {
|
|
316
|
+
for (const n of nodes) {
|
|
317
|
+
if (n.key === targetNode)
|
|
318
|
+
return n;
|
|
319
|
+
if (n.children) {
|
|
320
|
+
const f = findNode(n.children);
|
|
321
|
+
if (f)
|
|
322
|
+
return f;
|
|
324
323
|
}
|
|
325
|
-
return null;
|
|
326
|
-
};
|
|
327
|
-
const n = findNode(treeNodes);
|
|
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
324
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
325
|
+
return null;
|
|
326
|
+
};
|
|
327
|
+
const n = findNode(treeNodes);
|
|
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
|
+
}
|
|
345
|
+
setSelectedItemIds(next);
|
|
346
|
+
// Update completion when user changes selection
|
|
347
|
+
onStepCompleted(next.size > 0);
|
|
348
|
+
}, renderNode: (node) => {
|
|
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;
|
|
356
|
+
const icon = node?.data?.icon;
|
|
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
|
|
361
|
+
shiftToggleRef.current = e.shiftKey;
|
|
362
|
+
}, onChange: (e) => {
|
|
363
|
+
if (!isActive || !isTranslatable)
|
|
364
|
+
return; // Don't handle changes when inactive or untranslatable
|
|
365
|
+
setUserHasInteracted(true);
|
|
366
|
+
const next = new Set(selectedItemIds);
|
|
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));
|
|
375
382
|
else {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (e.currentTarget.checked && !allSel)
|
|
379
|
-
ids.forEach((id) => next.add(id));
|
|
380
|
-
else if (!e.currentTarget.checked && allSel)
|
|
383
|
+
// Toggle based on current
|
|
384
|
+
if (allSel)
|
|
381
385
|
ids.forEach((id) => next.delete(id));
|
|
382
|
-
else
|
|
383
|
-
|
|
384
|
-
if (allSel)
|
|
385
|
-
ids.forEach((id) => next.delete(id));
|
|
386
|
-
else
|
|
387
|
-
ids.forEach((id) => next.add(id));
|
|
388
|
-
}
|
|
386
|
+
else
|
|
387
|
+
ids.forEach((id) => next.add(id));
|
|
389
388
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
389
|
+
}
|
|
390
|
+
setSelectedItemIds(next);
|
|
391
|
+
shiftToggleRef.current = false;
|
|
392
|
+
// Update completion when user changes selection
|
|
393
|
+
onStepCompleted(next.size > 0);
|
|
394
|
+
} }), _jsx("span", { className: `${!isTranslatable ? 'opacity-70 italic' : ''}`, children: node.label })] }));
|
|
395
|
+
} }) }))] })) : isDiscovering ? (
|
|
396
396
|
// Loading skeleton for discovery results
|
|
397
|
-
_jsxs("div", { 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: "flex
|
|
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))) }) })] })] })) : (
|
|
398
398
|
// Empty state
|
|
399
|
-
_jsx("div", { className: "flex items-center justify-center
|
|
399
|
+
_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" })] }) })) })] })] }));
|
|
400
400
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parhelia/localization",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12390",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"@tailwindcss/postcss": "^4.1.18",
|
|
35
35
|
"@turbo/gen": "^2.4.4",
|
|
36
36
|
"@types/node": "^22.13.9",
|
|
37
|
-
"@types/react": "19.2.
|
|
38
|
-
"@types/react-dom": "19.2.
|
|
37
|
+
"@types/react": "19.2.14",
|
|
38
|
+
"@types/react-dom": "19.2.3",
|
|
39
39
|
"eslint": "^9.22.0",
|
|
40
40
|
"tailwindcss": "^4.1.18",
|
|
41
41
|
"typescript": "5.8.2"
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"lucide-react": "^0.486.0",
|
|
46
46
|
"next": "16.0.10",
|
|
47
47
|
"postcss": "^8.5.3",
|
|
48
|
-
"react": "19.2.
|
|
49
|
-
"react-dom": "19.2.
|
|
48
|
+
"react": "19.2.4",
|
|
49
|
+
"react-dom": "19.2.4"
|
|
50
50
|
},
|
|
51
51
|
"overrides": {
|
|
52
|
-
"@types/react": "19.2.
|
|
53
|
-
"@types/react-dom": "19.2.
|
|
52
|
+
"@types/react": "19.2.14",
|
|
53
|
+
"@types/react-dom": "19.2.3"
|
|
54
54
|
}
|
|
55
55
|
}
|