@alpaca-editor/core 1.0.4033 → 1.0.4038
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/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/{editor/menubar → components/ui}/LanguageSelector.d.ts +1 -1
- package/dist/{editor/menubar → components/ui}/LanguageSelector.js +8 -8
- package/dist/components/ui/LanguageSelector.js.map +1 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/dropdown-menu.d.ts +1 -1
- package/dist/components/ui/dropdown-menu.js +2 -2
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/sonner.js +3 -1
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/config/config.js +5 -5
- package/dist/config/config.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +2 -1
- package/dist/editor/ContentTree.js +33 -9
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/PictureEditor.js +2 -2
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/ScrollingContentTree.d.ts +2 -1
- package/dist/editor/ScrollingContentTree.js +2 -2
- package/dist/editor/ScrollingContentTree.js.map +1 -1
- package/dist/editor/Terminal.d.ts +2 -0
- package/dist/editor/Terminal.js +2 -2
- package/dist/editor/Terminal.js.map +1 -1
- package/dist/editor/ai/AgentHistory.d.ts +11 -0
- package/dist/editor/ai/AgentHistory.js +12 -0
- package/dist/editor/ai/AgentHistory.js.map +1 -0
- package/dist/editor/ai/Agents.js +187 -24
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +2 -1
- package/dist/editor/ai/AiResponseMessage.js +6 -6
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/AiTerminal.d.ts +1 -0
- package/dist/editor/ai/AiTerminal.js +330 -43
- package/dist/editor/ai/AiTerminal.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +19 -7
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js +48 -1
- package/dist/editor/field-types/InternalLinkFieldEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.js +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js +1 -1
- package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js +2 -2
- package/dist/editor/page-editor-chrome/FieldEditedIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +2 -2
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +9 -9
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js +2 -2
- package/dist/editor/page-editor-chrome/LockedFieldIndicator.js.map +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.js +1 -1
- package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js +1 -1
- package/dist/editor/page-editor-chrome/PictureEditorOverlay.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +2 -2
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +12 -12
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +1 -1
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.js +10 -11
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.js +2 -2
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +4 -4
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.d.ts +2 -2
- package/dist/editor/page-viewer/pageViewContext.js +11 -14
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +21 -1
- package/dist/editor/services/agentService.js +101 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -1
- package/dist/editor/services/aiService.js +1 -2
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/sidebar/GraphQL.js +5 -6
- package/dist/editor/sidebar/GraphQL.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.d.ts +2 -1
- package/dist/editor/sidebar/MainContentTree.js +9 -5
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/ui/Icons.d.ts +5 -0
- package/dist/editor/ui/Icons.js +3 -0
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.js +26 -14
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/PerfectTree.js +54 -2
- package/dist/editor/ui/PerfectTree.js.map +1 -1
- package/dist/editor/views/CompareView.js +3 -3
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/editor/views/EditView.js +1 -1
- package/dist/editor/views/EditView.js.map +1 -1
- package/dist/editor/views/ItemEditor.js +1 -1
- package/dist/editor/views/ItemEditor.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/page-wizard/steps/ContentStep.js +5 -5
- package/dist/page-wizard/steps/ContentStep.js.map +1 -1
- package/dist/page-wizard/steps/FindItemsStep.js +1 -1
- package/dist/page-wizard/steps/ImagesStep.js +1 -1
- package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
- package/dist/page-wizard/steps/LayoutStep.js +1 -1
- package/dist/page-wizard/steps/LayoutStep.js.map +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js +1 -1
- package/dist/page-wizard/steps/MetaDataStep.js.map +1 -1
- package/dist/page-wizard/steps/SelectStep.js +1 -1
- package/dist/page-wizard/steps/StructureStep.js +28 -11
- package/dist/page-wizard/steps/StructureStep.js.map +1 -1
- package/dist/page-wizard/steps/TranslateStep.d.ts +1 -0
- package/dist/page-wizard/steps/TranslateStep.js +67 -73
- package/dist/page-wizard/steps/TranslateStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/splash-screen/NewPage.js +1 -1
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/OpenPage.js +1 -1
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/styles.css +71 -8
- package/package.json +1 -1
- package/src/components/index.ts +1 -0
- package/src/{editor/menubar → components/ui}/LanguageSelector.tsx +12 -12
- package/src/components/ui/dropdown-menu.tsx +3 -1
- package/src/components/ui/sonner.tsx +5 -1
- package/src/config/config.tsx +4 -3
- package/src/editor/ContentTree.tsx +41 -12
- package/src/editor/PictureEditor.tsx +2 -2
- package/src/editor/ScrollingContentTree.tsx +3 -0
- package/src/editor/Terminal.tsx +16 -7
- package/src/editor/ai/AgentHistory.tsx +85 -0
- package/src/editor/ai/Agents.tsx +256 -88
- package/src/editor/ai/AiResponseMessage.tsx +25 -11
- package/src/editor/ai/AiTerminal.tsx +571 -73
- package/src/editor/client/itemsRepository.ts +29 -12
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +52 -1
- package/src/editor/field-types/richtext/components/SimpleRichTextEditor.css +64 -0
- package/src/editor/field-types/richtext/contextMenuFactory.tsx +7 -8
- package/src/editor/menubar/ItemLanguageVersion.tsx +1 -1
- package/src/editor/menubar/PageSelector.tsx +1 -0
- package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +1 -1
- package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +3 -3
- package/src/editor/page-editor-chrome/FrameMenu.tsx +2 -2
- package/src/editor/page-editor-chrome/InlineEditor.tsx +9 -12
- package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +3 -3
- package/src/editor/page-editor-chrome/PageEditorChrome.tsx +3 -3
- package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +1 -1
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +1 -1
- package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +2 -2
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +12 -18
- package/src/editor/page-viewer/EditorForm.tsx +1 -1
- package/src/editor/page-viewer/MiniMap.tsx +10 -11
- package/src/editor/page-viewer/PageViewer.tsx +8 -3
- package/src/editor/page-viewer/PageViewerFrame.tsx +4 -4
- package/src/editor/page-viewer/pageViewContext.ts +71 -66
- package/src/editor/services/agentService.ts +129 -1
- package/src/editor/services/aiService.ts +2 -4
- package/src/editor/sidebar/GraphQL.tsx +16 -15
- package/src/editor/sidebar/MainContentTree.tsx +12 -4
- package/src/editor/ui/Icons.tsx +35 -0
- package/src/editor/ui/ItemNameDialogNew.tsx +29 -13
- package/src/editor/ui/PerfectTree.tsx +70 -4
- package/src/editor/views/CompareView.tsx +3 -3
- package/src/editor/views/EditView.tsx +1 -1
- package/src/editor/views/ItemEditor.tsx +1 -1
- package/src/index.ts +10 -4
- package/src/page-wizard/steps/ContentStep.tsx +5 -5
- package/src/page-wizard/steps/FindItemsStep.tsx +1 -1
- package/src/page-wizard/steps/ImagesStep.tsx +1 -1
- package/src/page-wizard/steps/LayoutStep.tsx +1 -1
- package/src/page-wizard/steps/MetaDataStep.tsx +1 -1
- package/src/page-wizard/steps/SelectStep.tsx +1 -1
- package/src/page-wizard/steps/StructureStep.tsx +41 -17
- package/src/page-wizard/steps/TranslateStep.tsx +326 -222
- package/src/revision.ts +2 -2
- package/src/splash-screen/NewPage.tsx +1 -0
- package/src/splash-screen/OpenPage.tsx +1 -0
- package/dist/components/SimpleLanguageSelector.d.ts +0 -10
- package/dist/components/SimpleLanguageSelector.js +0 -59
- package/dist/components/SimpleLanguageSelector.js.map +0 -1
- package/dist/editor/menubar/LanguageSelector.js.map +0 -1
- package/src/components/SimpleLanguageSelector.tsx +0 -113
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { StepComponentProps } from "../../config/types";
|
|
2
2
|
import { useEffect, useState, useCallback } from "react";
|
|
3
3
|
import { useEditContext } from "../../editor/client/editContext";
|
|
4
|
-
import {
|
|
4
|
+
import { Language } from "../../editor/pageModel";
|
|
5
5
|
import { ActionButton } from "../../components/ActionButton";
|
|
6
|
-
import { Input } from "../../components/ui/input";
|
|
7
6
|
import { InputTextarea } from "primereact/inputtextarea";
|
|
8
7
|
import { executePrompt } from "../../editor/services/aiService";
|
|
9
8
|
import { createWizardAiContext } from "../service";
|
|
10
|
-
import {
|
|
11
|
-
|
|
9
|
+
import { LanguageSelector } from "../../components/ui/LanguageSelector";
|
|
10
|
+
|
|
12
11
|
import {
|
|
13
12
|
Languages,
|
|
14
13
|
Upload,
|
|
@@ -17,13 +16,19 @@ import {
|
|
|
17
16
|
AlertCircle,
|
|
18
17
|
Sparkles,
|
|
19
18
|
RefreshCw,
|
|
19
|
+
Edit,
|
|
20
|
+
Save,
|
|
21
|
+
X,
|
|
20
22
|
} from "lucide-react";
|
|
21
23
|
import { WizardBox } from "../WizardBox";
|
|
24
|
+
import { SimpleRichTextEditor } from "../../editor/field-types/richtext/components/SimpleRichTextEditor";
|
|
25
|
+
import "../../editor/field-types/richtext/components/SimpleRichTextEditor.css";
|
|
22
26
|
|
|
23
27
|
interface TranslatableField {
|
|
24
28
|
fieldId: string;
|
|
25
29
|
fieldName: string;
|
|
26
30
|
value: string;
|
|
31
|
+
fieldType: string;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
interface TranslatableFieldsResponse {
|
|
@@ -68,12 +73,11 @@ export function TranslateStep({
|
|
|
68
73
|
const [detectedLanguageName, setDetectedLanguageName] = useState<string>(
|
|
69
74
|
data.detectedLanguageName || "",
|
|
70
75
|
);
|
|
71
|
-
|
|
72
|
-
Language[]
|
|
73
|
-
>([]);
|
|
76
|
+
|
|
74
77
|
const [componentNames, setComponentNames] = useState<Record<string, string>>(
|
|
75
78
|
{},
|
|
76
79
|
);
|
|
80
|
+
const [editingFields, setEditingFields] = useState<Set<string>>(new Set());
|
|
77
81
|
|
|
78
82
|
// Get the current item from data (set by previous wizard steps)
|
|
79
83
|
const currentItem = data.currentItem || parentItem;
|
|
@@ -86,41 +90,12 @@ export function TranslateStep({
|
|
|
86
90
|
// Get document content from previous wizard step
|
|
87
91
|
const documentContent = data.document;
|
|
88
92
|
|
|
89
|
-
const [
|
|
90
|
-
const [
|
|
93
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
94
|
+
const [saveResults, setSaveResults] = useState<{
|
|
91
95
|
success: string[];
|
|
92
96
|
errors: string[];
|
|
93
97
|
}>({ success: [], errors: [] });
|
|
94
98
|
|
|
95
|
-
const fetchAvailableLanguages = useCallback(async () => {
|
|
96
|
-
if (!currentItem || !editContext) return;
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const response = await getLanguagesAndVersions({
|
|
100
|
-
id: currentItem.id,
|
|
101
|
-
language: currentItem.language,
|
|
102
|
-
version: currentItem.version,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
if (response.data?.languages) {
|
|
106
|
-
// Filter to only languages that have versions (versions > 0)
|
|
107
|
-
const languagesWithVersions = response.data.languages
|
|
108
|
-
.filter((lang) => lang.versions > 0)
|
|
109
|
-
.map((lang) => ({
|
|
110
|
-
languageCode: lang.languageCode,
|
|
111
|
-
name: lang.name,
|
|
112
|
-
icon: lang.icon,
|
|
113
|
-
versions: lang.versions,
|
|
114
|
-
}));
|
|
115
|
-
setAvailableSourceLanguages(languagesWithVersions);
|
|
116
|
-
}
|
|
117
|
-
} catch (error) {
|
|
118
|
-
console.error("Error fetching available languages:", error);
|
|
119
|
-
// Fallback to all languages if there's an error
|
|
120
|
-
setAvailableSourceLanguages(editContext?.itemLanguages || []);
|
|
121
|
-
}
|
|
122
|
-
}, [currentItem, editContext]);
|
|
123
|
-
|
|
124
99
|
const fetchComponentNames = useCallback(
|
|
125
100
|
async (itemIds: string[], sourceLanguageParam: string) => {
|
|
126
101
|
if (!editContext || itemIds.length === 0) return;
|
|
@@ -213,10 +188,6 @@ export function TranslateStep({
|
|
|
213
188
|
const [lastFetchedSourceLanguage, setLastFetchedSourceLanguage] =
|
|
214
189
|
useState<string>("");
|
|
215
190
|
|
|
216
|
-
useEffect(() => {
|
|
217
|
-
fetchAvailableLanguages();
|
|
218
|
-
}, [fetchAvailableLanguages]);
|
|
219
|
-
|
|
220
191
|
// Reset the last fetched source language when the current item changes
|
|
221
192
|
useEffect(() => {
|
|
222
193
|
setLastFetchedSourceLanguage("");
|
|
@@ -226,12 +197,12 @@ export function TranslateStep({
|
|
|
226
197
|
// Only fetch translatable fields if:
|
|
227
198
|
// 1. We have a source language
|
|
228
199
|
// 2. It's different from the last one we fetched for
|
|
229
|
-
// 3. We're not currently
|
|
200
|
+
// 3. We're not currently saving (to avoid conflicts)
|
|
230
201
|
// 4. We're not currently loading or translating
|
|
231
202
|
if (
|
|
232
203
|
sourceLanguage &&
|
|
233
204
|
sourceLanguage !== lastFetchedSourceLanguage &&
|
|
234
|
-
!
|
|
205
|
+
!isSaving &&
|
|
235
206
|
!isLoading &&
|
|
236
207
|
!isTranslating
|
|
237
208
|
) {
|
|
@@ -242,7 +213,7 @@ export function TranslateStep({
|
|
|
242
213
|
fetchTranslatableFields,
|
|
243
214
|
sourceLanguage,
|
|
244
215
|
lastFetchedSourceLanguage,
|
|
245
|
-
|
|
216
|
+
isSaving,
|
|
246
217
|
isLoading,
|
|
247
218
|
isTranslating,
|
|
248
219
|
]);
|
|
@@ -272,7 +243,7 @@ ${documentContent.substring(0, 1000)}`;
|
|
|
272
243
|
},
|
|
273
244
|
],
|
|
274
245
|
{ editContext, createAiContext: createWizardAiContext },
|
|
275
|
-
{ model:
|
|
246
|
+
{ model: step.fields.aiModel || undefined },
|
|
276
247
|
{ signal: abortController.signal },
|
|
277
248
|
(response: any) => {
|
|
278
249
|
try {
|
|
@@ -403,7 +374,7 @@ Return your response as a JSON object in this exact format:
|
|
|
403
374
|
},
|
|
404
375
|
],
|
|
405
376
|
{ editContext, createAiContext: createWizardAiContext },
|
|
406
|
-
{ model:
|
|
377
|
+
{ model: step.fields.aiModel || undefined, stream: false },
|
|
407
378
|
{ signal: abortController.signal },
|
|
408
379
|
);
|
|
409
380
|
|
|
@@ -519,7 +490,7 @@ Return your response as a JSON object in this exact format:
|
|
|
519
490
|
},
|
|
520
491
|
],
|
|
521
492
|
{ editContext, createAiContext: createWizardAiContext },
|
|
522
|
-
{ model:
|
|
493
|
+
{ model: step.fields.aiModel || undefined, stream: false },
|
|
523
494
|
{ signal: abortController.signal },
|
|
524
495
|
);
|
|
525
496
|
|
|
@@ -620,7 +591,7 @@ Return your response as a JSON object in this exact format:
|
|
|
620
591
|
},
|
|
621
592
|
],
|
|
622
593
|
{ editContext, createAiContext: createWizardAiContext },
|
|
623
|
-
{ model:
|
|
594
|
+
{ model: step.fields.aiModel || undefined, stream: false },
|
|
624
595
|
{ signal: abortController.signal },
|
|
625
596
|
);
|
|
626
597
|
|
|
@@ -683,6 +654,26 @@ Return your response as a JSON object in this exact format:
|
|
|
683
654
|
}
|
|
684
655
|
};
|
|
685
656
|
|
|
657
|
+
const isRichTextField = (fieldType: string): boolean => {
|
|
658
|
+
return (
|
|
659
|
+
fieldType?.toLowerCase() === "rich text" ||
|
|
660
|
+
fieldType?.toLowerCase() === "html"
|
|
661
|
+
);
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const toggleFieldEdit = (itemId: string, fieldId: string) => {
|
|
665
|
+
const fieldKey = `${itemId}-${fieldId}`;
|
|
666
|
+
setEditingFields((prev) => {
|
|
667
|
+
const newSet = new Set(prev);
|
|
668
|
+
if (newSet.has(fieldKey)) {
|
|
669
|
+
newSet.delete(fieldKey);
|
|
670
|
+
} else {
|
|
671
|
+
newSet.add(fieldKey);
|
|
672
|
+
}
|
|
673
|
+
return newSet;
|
|
674
|
+
});
|
|
675
|
+
};
|
|
676
|
+
|
|
686
677
|
const handleFieldChange = (
|
|
687
678
|
itemId: string,
|
|
688
679
|
fieldId: string,
|
|
@@ -704,14 +695,14 @@ Return your response as a JSON object in this exact format:
|
|
|
704
695
|
}));
|
|
705
696
|
};
|
|
706
697
|
|
|
707
|
-
const
|
|
698
|
+
const handleSaveTranslations = async () => {
|
|
708
699
|
if (!editContext || !currentItem || !targetLanguage?.languageCode) {
|
|
709
|
-
console.error("Missing required data for
|
|
700
|
+
console.error("Missing required data for saving translations");
|
|
710
701
|
return;
|
|
711
702
|
}
|
|
712
703
|
|
|
713
|
-
|
|
714
|
-
|
|
704
|
+
setIsSaving(true);
|
|
705
|
+
setSaveResults({ success: [], errors: [] });
|
|
715
706
|
|
|
716
707
|
try {
|
|
717
708
|
// Get all items that have translations
|
|
@@ -721,7 +712,7 @@ Return your response as a JSON object in this exact format:
|
|
|
721
712
|
);
|
|
722
713
|
|
|
723
714
|
console.log(
|
|
724
|
-
`
|
|
715
|
+
`Saving translations for ${itemsToTranslate.length} items to language: ${targetLanguage.languageCode}`,
|
|
725
716
|
);
|
|
726
717
|
|
|
727
718
|
for (const [itemId, translatedFieldsList] of itemsToTranslate) {
|
|
@@ -765,7 +756,7 @@ Return your response as a JSON object in this exact format:
|
|
|
765
756
|
});
|
|
766
757
|
}
|
|
767
758
|
|
|
768
|
-
|
|
759
|
+
setSaveResults((prev) => ({
|
|
769
760
|
...prev,
|
|
770
761
|
success: [
|
|
771
762
|
...prev.success,
|
|
@@ -774,7 +765,7 @@ Return your response as a JSON object in this exact format:
|
|
|
774
765
|
}));
|
|
775
766
|
} catch (error) {
|
|
776
767
|
console.error(`Error translating item ${itemId}:`, error);
|
|
777
|
-
|
|
768
|
+
setSaveResults((prev) => ({
|
|
778
769
|
...prev,
|
|
779
770
|
errors: [
|
|
780
771
|
...prev.errors,
|
|
@@ -784,10 +775,10 @@ Return your response as a JSON object in this exact format:
|
|
|
784
775
|
}
|
|
785
776
|
}
|
|
786
777
|
|
|
787
|
-
console.log("Translation
|
|
778
|
+
console.log("Translation saving completed");
|
|
788
779
|
} catch (error) {
|
|
789
|
-
console.error("Error during translation
|
|
790
|
-
|
|
780
|
+
console.error("Error during translation saving:", error);
|
|
781
|
+
setSaveResults((prev) => ({
|
|
791
782
|
...prev,
|
|
792
783
|
errors: [
|
|
793
784
|
...prev.errors,
|
|
@@ -795,34 +786,34 @@ Return your response as a JSON object in this exact format:
|
|
|
795
786
|
],
|
|
796
787
|
}));
|
|
797
788
|
} finally {
|
|
798
|
-
|
|
789
|
+
setIsSaving(false);
|
|
799
790
|
}
|
|
800
791
|
};
|
|
801
792
|
|
|
802
|
-
// Check if step is completed (has translations and they've been
|
|
793
|
+
// Check if step is completed (has translations and they've been saved successfully)
|
|
803
794
|
useEffect(() => {
|
|
804
795
|
const hasTranslations = Object.values(translatedFields).some((fields) =>
|
|
805
796
|
fields.some((field) => field.translatedValue.trim() !== ""),
|
|
806
797
|
);
|
|
807
|
-
const
|
|
798
|
+
const hasSuccessfulSave = saveResults.success.length > 0;
|
|
808
799
|
|
|
809
|
-
// Step is completed if we have translations and at least some have been
|
|
810
|
-
setStepCompleted(hasTranslations &&
|
|
811
|
-
}, [translatedFields,
|
|
800
|
+
// Step is completed if we have translations and at least some have been saved successfully
|
|
801
|
+
setStepCompleted(hasTranslations && hasSuccessfulSave);
|
|
802
|
+
}, [translatedFields, saveResults, setStepCompleted]);
|
|
812
803
|
|
|
813
|
-
// Save translated fields and
|
|
804
|
+
// Save translated fields and save results to wizard data
|
|
814
805
|
useEffect(() => {
|
|
815
806
|
setData((prev) => ({
|
|
816
807
|
...prev,
|
|
817
808
|
translatedFields,
|
|
818
|
-
|
|
809
|
+
saveResults,
|
|
819
810
|
targetLanguage,
|
|
820
811
|
detectedLanguageName,
|
|
821
812
|
sourceLanguage,
|
|
822
813
|
}));
|
|
823
814
|
}, [
|
|
824
815
|
translatedFields,
|
|
825
|
-
|
|
816
|
+
saveResults,
|
|
826
817
|
targetLanguage,
|
|
827
818
|
detectedLanguageName,
|
|
828
819
|
sourceLanguage,
|
|
@@ -852,13 +843,14 @@ Return your response as a JSON object in this exact format:
|
|
|
852
843
|
<label className="mb-2 block text-sm font-medium">
|
|
853
844
|
Source Language
|
|
854
845
|
</label>
|
|
855
|
-
<
|
|
846
|
+
<LanguageSelector
|
|
856
847
|
selectedLanguage={sourceLanguage}
|
|
857
848
|
onLanguageSelected={(language: Language) =>
|
|
858
849
|
setSourceLanguage(language.languageCode)
|
|
859
850
|
}
|
|
860
851
|
disabled={false}
|
|
861
|
-
|
|
852
|
+
showAllLanguagesSwitch={false}
|
|
853
|
+
showAllLanguages={true}
|
|
862
854
|
/>
|
|
863
855
|
<p className="mt-1 text-xs text-gray-600">
|
|
864
856
|
Select the language of the content to translate from
|
|
@@ -883,15 +875,14 @@ Return your response as a JSON object in this exact format:
|
|
|
883
875
|
)}
|
|
884
876
|
</div>
|
|
885
877
|
<div className="relative">
|
|
886
|
-
<
|
|
878
|
+
<LanguageSelector
|
|
887
879
|
selectedLanguage={targetLanguage?.languageCode || ""}
|
|
888
880
|
onLanguageSelected={(language: Language) =>
|
|
889
881
|
setTargetLanguage(language)
|
|
890
882
|
}
|
|
891
883
|
disabled={false}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
884
|
+
showAllLanguagesSwitch={false}
|
|
885
|
+
showAllLanguages={true}
|
|
895
886
|
/>
|
|
896
887
|
{detectedLanguageName && (
|
|
897
888
|
<button
|
|
@@ -944,13 +935,43 @@ Return your response as a JSON object in this exact format:
|
|
|
944
935
|
Translating fields...
|
|
945
936
|
</div>
|
|
946
937
|
</div>
|
|
947
|
-
) :
|
|
938
|
+
) : isSaving ? (
|
|
948
939
|
<div className="py-8 text-center">
|
|
949
940
|
<div className="flex items-center justify-center gap-2">
|
|
950
941
|
<RefreshCw className="h-4 w-4 animate-spin" />
|
|
951
|
-
|
|
942
|
+
Saving translations...
|
|
952
943
|
</div>
|
|
953
944
|
</div>
|
|
945
|
+
) : saveResults.success.length > 0 || saveResults.errors.length > 0 ? (
|
|
946
|
+
<div className="space-y-4">
|
|
947
|
+
{saveResults.success.length > 0 && (
|
|
948
|
+
<div className="rounded-lg border border-green-200 bg-green-50 p-4">
|
|
949
|
+
<div className="mb-2 flex items-center gap-2 font-medium text-green-800">
|
|
950
|
+
<CheckCircle className="h-4 w-4" />
|
|
951
|
+
Successfully Saved ({saveResults.success.length} items)
|
|
952
|
+
</div>
|
|
953
|
+
<ul className="list-inside list-disc text-sm text-green-700">
|
|
954
|
+
{saveResults.success.map((item, index) => (
|
|
955
|
+
<li key={index}>{item}</li>
|
|
956
|
+
))}
|
|
957
|
+
</ul>
|
|
958
|
+
</div>
|
|
959
|
+
)}
|
|
960
|
+
|
|
961
|
+
{saveResults.errors.length > 0 && (
|
|
962
|
+
<div className="rounded-lg border border-red-200 bg-red-50 p-4">
|
|
963
|
+
<div className="mb-2 flex items-center gap-2 font-medium text-red-800">
|
|
964
|
+
<AlertCircle className="h-4 w-4" />
|
|
965
|
+
Errors ({saveResults.errors.length})
|
|
966
|
+
</div>
|
|
967
|
+
<ul className="list-inside list-disc text-sm text-red-700">
|
|
968
|
+
{saveResults.errors.map((error, index) => (
|
|
969
|
+
<li key={index}>{error}</li>
|
|
970
|
+
))}
|
|
971
|
+
</ul>
|
|
972
|
+
</div>
|
|
973
|
+
)}
|
|
974
|
+
</div>
|
|
954
975
|
) : translatedCount > 0 ? (
|
|
955
976
|
<div className="space-y-6">
|
|
956
977
|
{Object.entries(translatableFields).map(([itemId, fields]) => (
|
|
@@ -964,20 +985,18 @@ Return your response as a JSON object in this exact format:
|
|
|
964
985
|
(f) => f.fieldId === field.fieldId,
|
|
965
986
|
);
|
|
966
987
|
return (
|
|
967
|
-
<div
|
|
968
|
-
|
|
969
|
-
className="grid grid-cols-2 gap-4"
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
<div className="min-h-[100px] flex-1 rounded border bg-gray-50 p-3 text-xs">
|
|
976
|
-
{field.value}
|
|
988
|
+
<div key={field.fieldId}>
|
|
989
|
+
{/* Headers Row */}
|
|
990
|
+
<div className="grid grid-cols-2 gap-4">
|
|
991
|
+
{/* Source Field Header */}
|
|
992
|
+
<div>
|
|
993
|
+
<label className="block text-xs font-medium">
|
|
994
|
+
{field.fieldName} ({field.fieldType})
|
|
995
|
+
</label>
|
|
977
996
|
</div>
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
<div className="
|
|
997
|
+
|
|
998
|
+
{/* Translation Field Header */}
|
|
999
|
+
<div className="flex items-center justify-between">
|
|
981
1000
|
<div className="flex items-center gap-2">
|
|
982
1001
|
<label className="block text-xs font-medium">
|
|
983
1002
|
{field.fieldName}
|
|
@@ -1003,91 +1022,205 @@ Return your response as a JSON object in this exact format:
|
|
|
1003
1022
|
)}
|
|
1004
1023
|
</div>
|
|
1005
1024
|
<div className="flex items-center gap-1">
|
|
1006
|
-
{
|
|
1025
|
+
{!editingFields.has(
|
|
1026
|
+
`${itemId}-${field.fieldId}`,
|
|
1027
|
+
) &&
|
|
1028
|
+
translatedField?.translatedValue && (
|
|
1029
|
+
<button
|
|
1030
|
+
type="button"
|
|
1031
|
+
onClick={() =>
|
|
1032
|
+
checkTranslationCompleteness(
|
|
1033
|
+
itemId,
|
|
1034
|
+
field.fieldId,
|
|
1035
|
+
field.value,
|
|
1036
|
+
translatedField.translatedValue,
|
|
1037
|
+
field.fieldName,
|
|
1038
|
+
)
|
|
1039
|
+
}
|
|
1040
|
+
disabled={
|
|
1041
|
+
checkingFields.has(
|
|
1042
|
+
`${itemId}-${field.fieldId}`,
|
|
1043
|
+
) ||
|
|
1044
|
+
!translatedField.translatedValue.trim()
|
|
1045
|
+
}
|
|
1046
|
+
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-gray-600 hover:bg-gray-50 hover:text-gray-800 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-transparent"
|
|
1047
|
+
title="Check translation completeness"
|
|
1048
|
+
>
|
|
1049
|
+
{checkingFields.has(
|
|
1050
|
+
`${itemId}-${field.fieldId}`,
|
|
1051
|
+
) ? (
|
|
1052
|
+
<RefreshCw className="h-3 w-3 animate-spin" />
|
|
1053
|
+
) : (
|
|
1054
|
+
<CheckCircle className="h-3 w-3" />
|
|
1055
|
+
)}
|
|
1056
|
+
Check
|
|
1057
|
+
</button>
|
|
1058
|
+
)}
|
|
1059
|
+
{!editingFields.has(
|
|
1060
|
+
`${itemId}-${field.fieldId}`,
|
|
1061
|
+
) && (
|
|
1007
1062
|
<button
|
|
1008
1063
|
type="button"
|
|
1009
1064
|
onClick={() =>
|
|
1010
|
-
|
|
1065
|
+
handleSingleFieldTranslation(
|
|
1011
1066
|
itemId,
|
|
1012
1067
|
field.fieldId,
|
|
1013
|
-
field
|
|
1014
|
-
translatedField.translatedValue,
|
|
1015
|
-
field.fieldName,
|
|
1068
|
+
field,
|
|
1016
1069
|
)
|
|
1017
1070
|
}
|
|
1018
1071
|
disabled={
|
|
1019
|
-
|
|
1072
|
+
!targetLanguage?.languageCode ||
|
|
1073
|
+
translatingFields.has(
|
|
1020
1074
|
`${itemId}-${field.fieldId}`,
|
|
1021
|
-
) ||
|
|
1075
|
+
) ||
|
|
1076
|
+
isTranslating
|
|
1022
1077
|
}
|
|
1023
|
-
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-
|
|
1024
|
-
title="
|
|
1078
|
+
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-blue-600 hover:bg-blue-50 hover:text-blue-800 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-transparent"
|
|
1079
|
+
title="Retranslate this field"
|
|
1025
1080
|
>
|
|
1026
|
-
{
|
|
1081
|
+
{translatingFields.has(
|
|
1027
1082
|
`${itemId}-${field.fieldId}`,
|
|
1028
1083
|
) ? (
|
|
1029
1084
|
<RefreshCw className="h-3 w-3 animate-spin" />
|
|
1030
1085
|
) : (
|
|
1031
|
-
<
|
|
1086
|
+
<Wand2 className="h-3 w-3" />
|
|
1032
1087
|
)}
|
|
1033
|
-
|
|
1088
|
+
Retranslate
|
|
1089
|
+
</button>
|
|
1090
|
+
)}
|
|
1091
|
+
{editingFields.has(
|
|
1092
|
+
`${itemId}-${field.fieldId}`,
|
|
1093
|
+
) ? (
|
|
1094
|
+
<>
|
|
1095
|
+
<button
|
|
1096
|
+
type="button"
|
|
1097
|
+
onClick={() =>
|
|
1098
|
+
toggleFieldEdit(itemId, field.fieldId)
|
|
1099
|
+
}
|
|
1100
|
+
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-green-600 hover:bg-green-50 hover:text-green-800"
|
|
1101
|
+
title="Save changes"
|
|
1102
|
+
>
|
|
1103
|
+
<Save className="h-3 w-3" strokeWidth={1} />
|
|
1104
|
+
Save
|
|
1105
|
+
</button>
|
|
1106
|
+
<button
|
|
1107
|
+
type="button"
|
|
1108
|
+
onClick={() =>
|
|
1109
|
+
toggleFieldEdit(itemId, field.fieldId)
|
|
1110
|
+
}
|
|
1111
|
+
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-gray-600 hover:bg-gray-50 hover:text-gray-800"
|
|
1112
|
+
title="Cancel edit"
|
|
1113
|
+
>
|
|
1114
|
+
<X className="h-3 w-3" strokeWidth={1} />
|
|
1115
|
+
Cancel
|
|
1116
|
+
</button>
|
|
1117
|
+
</>
|
|
1118
|
+
) : (
|
|
1119
|
+
<button
|
|
1120
|
+
type="button"
|
|
1121
|
+
onClick={() =>
|
|
1122
|
+
toggleFieldEdit(itemId, field.fieldId)
|
|
1123
|
+
}
|
|
1124
|
+
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-gray-600 hover:bg-gray-50 hover:text-gray-800"
|
|
1125
|
+
title="Edit translation"
|
|
1126
|
+
>
|
|
1127
|
+
<Edit className="h-3 w-3" strokeWidth={1} />
|
|
1128
|
+
Edit
|
|
1034
1129
|
</button>
|
|
1035
1130
|
)}
|
|
1036
|
-
<button
|
|
1037
|
-
type="button"
|
|
1038
|
-
onClick={() =>
|
|
1039
|
-
handleSingleFieldTranslation(
|
|
1040
|
-
itemId,
|
|
1041
|
-
field.fieldId,
|
|
1042
|
-
field,
|
|
1043
|
-
)
|
|
1044
|
-
}
|
|
1045
|
-
disabled={
|
|
1046
|
-
!targetLanguage?.languageCode ||
|
|
1047
|
-
translatingFields.has(
|
|
1048
|
-
`${itemId}-${field.fieldId}`,
|
|
1049
|
-
) ||
|
|
1050
|
-
isTranslating
|
|
1051
|
-
}
|
|
1052
|
-
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-blue-600 hover:bg-blue-50 hover:text-blue-800 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-transparent"
|
|
1053
|
-
title="Retranslate this field"
|
|
1054
|
-
>
|
|
1055
|
-
{translatingFields.has(
|
|
1056
|
-
`${itemId}-${field.fieldId}`,
|
|
1057
|
-
) ? (
|
|
1058
|
-
<RefreshCw className="h-3 w-3 animate-spin" />
|
|
1059
|
-
) : (
|
|
1060
|
-
<Wand2 className="h-3 w-3" />
|
|
1061
|
-
)}
|
|
1062
|
-
Retranslate
|
|
1063
|
-
</button>
|
|
1064
1131
|
</div>
|
|
1065
1132
|
</div>
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1133
|
+
</div>
|
|
1134
|
+
|
|
1135
|
+
{/* Content Row */}
|
|
1136
|
+
<div className="grid grid-cols-2 gap-4">
|
|
1137
|
+
{/* Source Field Content */}
|
|
1138
|
+
<div className="rounded border bg-gray-50 p-1 text-xs">
|
|
1139
|
+
{isRichTextField(field.fieldType) ? (
|
|
1140
|
+
<div
|
|
1141
|
+
className="rich-text-content"
|
|
1142
|
+
dangerouslySetInnerHTML={{
|
|
1143
|
+
__html: field.value,
|
|
1144
|
+
}}
|
|
1145
|
+
/>
|
|
1146
|
+
) : (
|
|
1147
|
+
field.value
|
|
1148
|
+
)}
|
|
1149
|
+
</div>
|
|
1150
|
+
|
|
1151
|
+
{/* Translation Field Content */}
|
|
1152
|
+
<div className="space-y-2">
|
|
1153
|
+
{translatedField?.isComplete === false &&
|
|
1154
|
+
translatedField?.missingSuggestions && (
|
|
1155
|
+
<div className="rounded border border-orange-200 bg-orange-50 p-2">
|
|
1156
|
+
<div className="mb-1 flex items-center gap-1 text-xs font-medium text-orange-800">
|
|
1157
|
+
<Sparkles className="h-3 w-3" />
|
|
1158
|
+
AI Suggestions for Missing Content:
|
|
1159
|
+
</div>
|
|
1160
|
+
<p className="text-xs text-orange-700 select-text">
|
|
1161
|
+
{translatedField.missingSuggestions}
|
|
1162
|
+
</p>
|
|
1072
1163
|
</div>
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1164
|
+
)}
|
|
1165
|
+
|
|
1166
|
+
{/* Conditionally render editor or readonly view */}
|
|
1167
|
+
{editingFields.has(`${itemId}-${field.fieldId}`) ? (
|
|
1168
|
+
<div>
|
|
1169
|
+
{isRichTextField(field.fieldType) ? (
|
|
1170
|
+
<SimpleRichTextEditor
|
|
1171
|
+
value={
|
|
1172
|
+
translatedField?.translatedValue || ""
|
|
1173
|
+
}
|
|
1174
|
+
onChange={(value: string) =>
|
|
1175
|
+
handleFieldChange(
|
|
1176
|
+
itemId,
|
|
1177
|
+
field.fieldId,
|
|
1178
|
+
value,
|
|
1179
|
+
)
|
|
1180
|
+
}
|
|
1181
|
+
readOnly={false}
|
|
1182
|
+
placeholder="Enter translation here..."
|
|
1183
|
+
className="min-h-[70px] text-xs"
|
|
1184
|
+
/>
|
|
1185
|
+
) : (
|
|
1186
|
+
<InputTextarea
|
|
1187
|
+
value={
|
|
1188
|
+
translatedField?.translatedValue || ""
|
|
1189
|
+
}
|
|
1190
|
+
onChange={(e) =>
|
|
1191
|
+
handleFieldChange(
|
|
1192
|
+
itemId,
|
|
1193
|
+
field.fieldId,
|
|
1194
|
+
e.target.value,
|
|
1195
|
+
)
|
|
1196
|
+
}
|
|
1197
|
+
placeholder="Enter translation here..."
|
|
1198
|
+
rows={4}
|
|
1199
|
+
className="h-full min-h-[70px] w-full text-xs"
|
|
1200
|
+
/>
|
|
1201
|
+
)}
|
|
1202
|
+
</div>
|
|
1203
|
+
) : (
|
|
1204
|
+
<div className="h-full min-h-[70px] rounded border bg-gray-50 p-1 text-xs">
|
|
1205
|
+
{translatedField?.translatedValue ? (
|
|
1206
|
+
isRichTextField(field.fieldType) ? (
|
|
1207
|
+
<div
|
|
1208
|
+
className="rich-text-content"
|
|
1209
|
+
dangerouslySetInnerHTML={{
|
|
1210
|
+
__html: translatedField.translatedValue,
|
|
1211
|
+
}}
|
|
1212
|
+
/>
|
|
1213
|
+
) : (
|
|
1214
|
+
translatedField.translatedValue
|
|
1215
|
+
)
|
|
1216
|
+
) : (
|
|
1217
|
+
<span className="text-gray-400">
|
|
1218
|
+
Translation will appear here...
|
|
1219
|
+
</span>
|
|
1220
|
+
)}
|
|
1076
1221
|
</div>
|
|
1077
1222
|
)}
|
|
1078
|
-
|
|
1079
|
-
value={translatedField?.translatedValue || ""}
|
|
1080
|
-
onChange={(e) =>
|
|
1081
|
-
handleFieldChange(
|
|
1082
|
-
itemId,
|
|
1083
|
-
field.fieldId,
|
|
1084
|
-
e.target.value,
|
|
1085
|
-
)
|
|
1086
|
-
}
|
|
1087
|
-
placeholder="Translation will appear here..."
|
|
1088
|
-
rows={4}
|
|
1089
|
-
className="min-h-[100px] w-full flex-1 text-xs"
|
|
1090
|
-
/>
|
|
1223
|
+
</div>
|
|
1091
1224
|
</div>
|
|
1092
1225
|
</div>
|
|
1093
1226
|
);
|
|
@@ -1105,79 +1238,50 @@ Return your response as a JSON object in this exact format:
|
|
|
1105
1238
|
)}
|
|
1106
1239
|
</WizardBox>
|
|
1107
1240
|
|
|
1108
|
-
{translatedCount > 0 &&
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
<
|
|
1122
|
-
|
|
1123
|
-
targetLanguage?.name
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
{(publishResults.success.length > 0 ||
|
|
1134
|
-
publishResults.errors.length > 0) && (
|
|
1135
|
-
<div className="space-y-2">
|
|
1136
|
-
{publishResults.success.length > 0 && (
|
|
1137
|
-
<div className="rounded-lg border border-green-200 bg-green-50 p-3">
|
|
1138
|
-
<div className="mb-1 flex items-center gap-2 font-medium text-green-800">
|
|
1139
|
-
<CheckCircle className="h-4 w-4" />
|
|
1140
|
-
Successfully Published ({publishResults.success.length})
|
|
1141
|
-
</div>
|
|
1142
|
-
<ul className="list-inside list-disc text-sm text-green-700">
|
|
1143
|
-
{publishResults.success.map((item, index) => (
|
|
1144
|
-
<li key={index}>{item}</li>
|
|
1145
|
-
))}
|
|
1146
|
-
</ul>
|
|
1147
|
-
</div>
|
|
1148
|
-
)}
|
|
1149
|
-
|
|
1150
|
-
{publishResults.errors.length > 0 && (
|
|
1151
|
-
<div className="rounded-lg border border-red-200 bg-red-50 p-3">
|
|
1152
|
-
<div className="mb-1 flex items-center gap-2 font-medium text-red-800">
|
|
1153
|
-
<AlertCircle className="h-4 w-4" />
|
|
1154
|
-
Errors ({publishResults.errors.length})
|
|
1155
|
-
</div>
|
|
1156
|
-
<ul className="list-inside list-disc text-sm text-red-700">
|
|
1157
|
-
{publishResults.errors.map((error, index) => (
|
|
1158
|
-
<li key={index}>{error}</li>
|
|
1159
|
-
))}
|
|
1160
|
-
</ul>
|
|
1161
|
-
</div>
|
|
1162
|
-
)}
|
|
1241
|
+
{translatedCount > 0 &&
|
|
1242
|
+
saveResults.success.length === 0 &&
|
|
1243
|
+
saveResults.errors.length === 0 && (
|
|
1244
|
+
<WizardBox
|
|
1245
|
+
title="Save Translations"
|
|
1246
|
+
description="Create language versions and apply translations"
|
|
1247
|
+
icon={<CheckCircle className="h-5 w-5" />}
|
|
1248
|
+
>
|
|
1249
|
+
<div className="space-y-4">
|
|
1250
|
+
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4">
|
|
1251
|
+
<h4 className="mb-2 font-medium text-blue-900">
|
|
1252
|
+
Ready to Save
|
|
1253
|
+
</h4>
|
|
1254
|
+
<p className="text-sm text-blue-800">
|
|
1255
|
+
{translatedCount} fields ready to be saved to{" "}
|
|
1256
|
+
<strong>{targetLanguage?.name}</strong> language versions
|
|
1257
|
+
{detectedLanguageName &&
|
|
1258
|
+
targetLanguage?.name
|
|
1259
|
+
?.toLowerCase()
|
|
1260
|
+
.includes(detectedLanguageName.toLowerCase()) && (
|
|
1261
|
+
<span className="text-green-700"> (auto-detected)</span>
|
|
1262
|
+
)}
|
|
1263
|
+
. This will create new language versions for all translated
|
|
1264
|
+
items and update their field values.
|
|
1265
|
+
</p>
|
|
1163
1266
|
</div>
|
|
1164
|
-
)}
|
|
1165
1267
|
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1268
|
+
<ActionButton
|
|
1269
|
+
onClick={handleSaveTranslations}
|
|
1270
|
+
isLoading={isSaving}
|
|
1271
|
+
disabled={
|
|
1272
|
+
translatedCount === 0 || !targetLanguage?.languageCode
|
|
1273
|
+
}
|
|
1274
|
+
className="w-full"
|
|
1275
|
+
variant="outline"
|
|
1276
|
+
>
|
|
1277
|
+
<CheckCircle className="mr-2 h-4 w-4" />
|
|
1278
|
+
{isSaving
|
|
1279
|
+
? "Saving Translations..."
|
|
1280
|
+
: `Save ${translatedCount} Translations to ${targetLanguage?.name || "Target Language"}`}
|
|
1281
|
+
</ActionButton>
|
|
1282
|
+
</div>
|
|
1283
|
+
</WizardBox>
|
|
1284
|
+
)}
|
|
1181
1285
|
</div>
|
|
1182
1286
|
);
|
|
1183
1287
|
}
|