@alpaca-editor/core 1.0.0
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/.prettierrc +3 -0
- package/eslint.config.mjs +4 -0
- package/images/bg-shape-black.webp +0 -0
- package/package.json +52 -0
- package/src/client-components/api.ts +6 -0
- package/src/client-components/index.ts +19 -0
- package/src/components/ActionButton.tsx +43 -0
- package/src/components/Error.tsx +57 -0
- package/src/config/config.tsx +737 -0
- package/src/config/types.ts +263 -0
- package/src/editor/ComponentInfo.tsx +77 -0
- package/src/editor/ConfirmationDialog.tsx +103 -0
- package/src/editor/ContentTree.tsx +654 -0
- package/src/editor/ContextMenu.tsx +155 -0
- package/src/editor/Editor.tsx +91 -0
- package/src/editor/EditorWarning.tsx +34 -0
- package/src/editor/EditorWarnings.tsx +33 -0
- package/src/editor/FieldEditorPopup.tsx +65 -0
- package/src/editor/FieldHistory.tsx +74 -0
- package/src/editor/FieldList.tsx +190 -0
- package/src/editor/FieldListField.tsx +387 -0
- package/src/editor/FieldListFieldWithFallbacks.tsx +211 -0
- package/src/editor/FloatingToolbar.tsx +163 -0
- package/src/editor/ImageEditor.tsx +129 -0
- package/src/editor/InsertMenu.tsx +332 -0
- package/src/editor/ItemInfo.tsx +90 -0
- package/src/editor/LinkEditorDialog.tsx +192 -0
- package/src/editor/MainLayout.tsx +94 -0
- package/src/editor/NewEditorClient.tsx +11 -0
- package/src/editor/PictureCropper.tsx +505 -0
- package/src/editor/PictureEditor.tsx +206 -0
- package/src/editor/PictureEditorDialog.tsx +381 -0
- package/src/editor/PublishDialog.ignore +74 -0
- package/src/editor/ScrollingContentTree.tsx +47 -0
- package/src/editor/Terminal.tsx +215 -0
- package/src/editor/Titlebar.tsx +23 -0
- package/src/editor/ai/AiPopup.tsx +59 -0
- package/src/editor/ai/AiResponseMessage.tsx +82 -0
- package/src/editor/ai/AiTerminal.tsx +450 -0
- package/src/editor/ai/AiToolCall.tsx +46 -0
- package/src/editor/ai/EditorAiTerminal.tsx +20 -0
- package/src/editor/ai/editorAiContext.ts +18 -0
- package/src/editor/client/DialogContext.tsx +49 -0
- package/src/editor/client/EditorClient.tsx +1831 -0
- package/src/editor/client/GenericDialog.tsx +50 -0
- package/src/editor/client/editContext.ts +330 -0
- package/src/editor/client/helpers.ts +44 -0
- package/src/editor/client/itemsRepository.ts +391 -0
- package/src/editor/client/operations.ts +610 -0
- package/src/editor/client/pageModelBuilder.ts +182 -0
- package/src/editor/commands/commands.ts +23 -0
- package/src/editor/commands/componentCommands.tsx +408 -0
- package/src/editor/commands/createVersionCommand.ts +33 -0
- package/src/editor/commands/deleteVersionCommand.ts +71 -0
- package/src/editor/commands/itemCommands.tsx +186 -0
- package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +201 -0
- package/src/editor/commands/undo.ts +39 -0
- package/src/editor/component-designer/ComponentDesigner.tsx +70 -0
- package/src/editor/component-designer/ComponentDesignerAiTerminal.tsx +11 -0
- package/src/editor/component-designer/ComponentDesignerMenu.tsx +91 -0
- package/src/editor/component-designer/ComponentEditor.tsx +97 -0
- package/src/editor/component-designer/ComponentRenderingCodeEditor.tsx +31 -0
- package/src/editor/component-designer/ComponentRenderingEditor.tsx +104 -0
- package/src/editor/component-designer/ComponentsDropdown.tsx +39 -0
- package/src/editor/component-designer/PlaceholdersEditor.tsx +183 -0
- package/src/editor/component-designer/RenderingsDropdown.tsx +36 -0
- package/src/editor/component-designer/TemplateEditor.tsx +236 -0
- package/src/editor/component-designer/aiContext.ts +23 -0
- package/src/editor/componentTreeHelper.tsx +114 -0
- package/src/editor/control-center/ControlCenterMenu.tsx +71 -0
- package/src/editor/control-center/IndexOverview.tsx +50 -0
- package/src/editor/control-center/IndexSettings.tsx +266 -0
- package/src/editor/control-center/Status.tsx +7 -0
- package/src/editor/editor-warnings/ItemLocked.tsx +63 -0
- package/src/editor/editor-warnings/NoLanguageWriteAccess.tsx +22 -0
- package/src/editor/editor-warnings/NoWorkflowWriteAccess.tsx +23 -0
- package/src/editor/editor-warnings/NoWriteAccess.tsx +15 -0
- package/src/editor/editor-warnings/ValidationErrors.tsx +54 -0
- package/src/editor/field-types/AttachmentEditor.tsx +9 -0
- package/src/editor/field-types/CheckboxEditor.tsx +47 -0
- package/src/editor/field-types/DropLinkEditor.tsx +75 -0
- package/src/editor/field-types/DropListEditor.tsx +84 -0
- package/src/editor/field-types/ImageFieldEditor.tsx +65 -0
- package/src/editor/field-types/InternalLinkFieldEditor.tsx +112 -0
- package/src/editor/field-types/LinkFieldEditor.tsx +85 -0
- package/src/editor/field-types/MultiLineText.tsx +63 -0
- package/src/editor/field-types/PictureFieldEditor.tsx +121 -0
- package/src/editor/field-types/RawEditor.tsx +53 -0
- package/src/editor/field-types/ReactQuill.tsx +580 -0
- package/src/editor/field-types/RichTextEditor.tsx +22 -0
- package/src/editor/field-types/RichTextEditorComponent.tsx +108 -0
- package/src/editor/field-types/SingleLineText.tsx +150 -0
- package/src/editor/field-types/TreeListEditor.tsx +261 -0
- package/src/editor/fieldTypes.ts +140 -0
- package/src/editor/media-selector/AiImageSearch.tsx +186 -0
- package/src/editor/media-selector/AiImageSearchPrompt.tsx +95 -0
- package/src/editor/media-selector/MediaSelector.tsx +42 -0
- package/src/editor/media-selector/Preview.tsx +14 -0
- package/src/editor/media-selector/Thumbnails.tsx +48 -0
- package/src/editor/media-selector/TreeSelector.tsx +292 -0
- package/src/editor/media-selector/UploadZone.tsx +137 -0
- package/src/editor/menubar/ActionsMenu.tsx +47 -0
- package/src/editor/menubar/ActiveUsers.tsx +17 -0
- package/src/editor/menubar/ApproveAndPublish.tsx +18 -0
- package/src/editor/menubar/BrowseHistory.tsx +37 -0
- package/src/editor/menubar/ItemLanguageVersion.tsx +52 -0
- package/src/editor/menubar/LanguageSelector.tsx +152 -0
- package/src/editor/menubar/Menu.tsx +83 -0
- package/src/editor/menubar/NavButtons.tsx +74 -0
- package/src/editor/menubar/PageSelector.tsx +139 -0
- package/src/editor/menubar/PageViewerControls.tsx +99 -0
- package/src/editor/menubar/Separator.tsx +12 -0
- package/src/editor/menubar/SiteInfo.tsx +53 -0
- package/src/editor/menubar/User.tsx +27 -0
- package/src/editor/menubar/VersionSelector.tsx +143 -0
- package/src/editor/page-editor-chrome/CommentHighlighting.tsx +287 -0
- package/src/editor/page-editor-chrome/CommentHighlightings.tsx +35 -0
- package/src/editor/page-editor-chrome/FieldActionIndicator.tsx +44 -0
- package/src/editor/page-editor-chrome/FieldActionIndicators.tsx +23 -0
- package/src/editor/page-editor-chrome/FieldEditedIndicator.tsx +64 -0
- package/src/editor/page-editor-chrome/FieldEditedIndicators.tsx +35 -0
- package/src/editor/page-editor-chrome/FrameMenu.tsx +263 -0
- package/src/editor/page-editor-chrome/FrameMenus.tsx +48 -0
- package/src/editor/page-editor-chrome/InlineEditor.tsx +147 -0
- package/src/editor/page-editor-chrome/LockedFieldIndicator.tsx +61 -0
- package/src/editor/page-editor-chrome/NoLayout.tsx +36 -0
- package/src/editor/page-editor-chrome/PageEditorChrome.tsx +119 -0
- package/src/editor/page-editor-chrome/PictureEditorOverlay.tsx +154 -0
- package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +171 -0
- package/src/editor/page-editor-chrome/PlaceholderDropZones.tsx +233 -0
- package/src/editor/page-viewer/DeviceToolbar.tsx +70 -0
- package/src/editor/page-viewer/EditorForm.tsx +247 -0
- package/src/editor/page-viewer/MiniMap.tsx +351 -0
- package/src/editor/page-viewer/PageViewer.tsx +127 -0
- package/src/editor/page-viewer/PageViewerFrame.tsx +1030 -0
- package/src/editor/page-viewer/pageViewContext.ts +186 -0
- package/src/editor/pageModel.ts +191 -0
- package/src/editor/picture-shared.tsx +53 -0
- package/src/editor/reviews/Comment.tsx +265 -0
- package/src/editor/reviews/Comments.tsx +50 -0
- package/src/editor/reviews/PreviewInfo.tsx +35 -0
- package/src/editor/reviews/Reviews.tsx +280 -0
- package/src/editor/reviews/reviewCommands.tsx +47 -0
- package/src/editor/reviews/useReviews.tsx +70 -0
- package/src/editor/services/aiService.ts +155 -0
- package/src/editor/services/componentDesignerService.ts +151 -0
- package/src/editor/services/contentService.ts +159 -0
- package/src/editor/services/editService.ts +462 -0
- package/src/editor/services/indexService.ts +24 -0
- package/src/editor/services/reviewsService.ts +45 -0
- package/src/editor/services/serviceHelper.ts +95 -0
- package/src/editor/services/systemService.ts +5 -0
- package/src/editor/services/translationService.ts +21 -0
- package/src/editor/services-server/api.ts +150 -0
- package/src/editor/services-server/graphQL.ts +106 -0
- package/src/editor/sidebar/ComponentPalette.tsx +146 -0
- package/src/editor/sidebar/ComponentTree.tsx +512 -0
- package/src/editor/sidebar/ComponentTree2.tsxx +490 -0
- package/src/editor/sidebar/Debug.tsx +105 -0
- package/src/editor/sidebar/DictionaryEditor.tsx +261 -0
- package/src/editor/sidebar/EditHistory.tsx +134 -0
- package/src/editor/sidebar/GraphQL.tsx +164 -0
- package/src/editor/sidebar/Insert.tsx +35 -0
- package/src/editor/sidebar/MainContentTree.tsx +95 -0
- package/src/editor/sidebar/Performance.tsx +53 -0
- package/src/editor/sidebar/Sessions.tsx +35 -0
- package/src/editor/sidebar/Sidebar.tsx +20 -0
- package/src/editor/sidebar/SidebarView.tsx +150 -0
- package/src/editor/sidebar/Translations.tsx +276 -0
- package/src/editor/sidebar/Validation.tsx +102 -0
- package/src/editor/sidebar/ViewSelector.tsx +49 -0
- package/src/editor/sidebar/Workbox.tsx +209 -0
- package/src/editor/ui/CenteredMessage.tsx +7 -0
- package/src/editor/ui/CopyToClipboardButton.tsx +23 -0
- package/src/editor/ui/DialogButtons.tsx +11 -0
- package/src/editor/ui/Icons.tsx +585 -0
- package/src/editor/ui/ItemNameDialog.tsx +94 -0
- package/src/editor/ui/ItemNameDialogNew.tsx +118 -0
- package/src/editor/ui/ItemSearch.tsx +173 -0
- package/src/editor/ui/PerfectTree.tsx +550 -0
- package/src/editor/ui/Section.tsx +35 -0
- package/src/editor/ui/SimpleIconButton.tsx +43 -0
- package/src/editor/ui/SimpleMenu.tsx +48 -0
- package/src/editor/ui/SimpleTable.tsx +63 -0
- package/src/editor/ui/SimpleTabs.tsx +55 -0
- package/src/editor/ui/SimpleToolbar.tsx +7 -0
- package/src/editor/ui/Spinner.tsx +7 -0
- package/src/editor/ui/Splitter.tsx +247 -0
- package/src/editor/ui/StackedPanels.tsx +134 -0
- package/src/editor/ui/Toolbar.tsx +7 -0
- package/src/editor/utils/id-helper.ts +3 -0
- package/src/editor/utils/insertOptions.ts +69 -0
- package/src/editor/utils/itemutils.ts +29 -0
- package/src/editor/utils/useMemoDebug.ts +28 -0
- package/src/editor/utils.ts +435 -0
- package/src/editor/views/CompareView.tsx +256 -0
- package/src/editor/views/EditView.tsx +27 -0
- package/src/editor/views/ItemEditor.tsx +58 -0
- package/src/editor/views/SingleEditView.tsx +44 -0
- package/src/fonts/Geist-Black.woff2 +0 -0
- package/src/fonts/Geist-Bold.woff2 +0 -0
- package/src/fonts/Geist-ExtraBold.woff2 +0 -0
- package/src/fonts/Geist-ExtraLight.woff2 +0 -0
- package/src/fonts/Geist-Light.woff2 +0 -0
- package/src/fonts/Geist-Medium.woff2 +0 -0
- package/src/fonts/Geist-Regular.woff2 +0 -0
- package/src/fonts/Geist-SemiBold.woff2 +0 -0
- package/src/fonts/Geist-Thin.woff2 +0 -0
- package/src/fonts/Geist[wght].woff2 +0 -0
- package/src/index.ts +7 -0
- package/src/page-wizard/PageWizard.tsx +163 -0
- package/src/page-wizard/SelectWizard.tsx +109 -0
- package/src/page-wizard/WizardSteps.tsx +207 -0
- package/src/page-wizard/service.ts +35 -0
- package/src/page-wizard/startPageWizardCommand.ts +27 -0
- package/src/page-wizard/steps/BuildPageStep.tsx +266 -0
- package/src/page-wizard/steps/CollectStep.tsx +233 -0
- package/src/page-wizard/steps/ComponentTypesSelector.tsx +443 -0
- package/src/page-wizard/steps/Components.tsx +193 -0
- package/src/page-wizard/steps/CreatePage.tsx +285 -0
- package/src/page-wizard/steps/CreatePageAndLayoutStep.tsx +384 -0
- package/src/page-wizard/steps/EditButton.tsx +34 -0
- package/src/page-wizard/steps/FieldEditor.tsx +102 -0
- package/src/page-wizard/steps/Generate.tsx +32 -0
- package/src/page-wizard/steps/ImagesStep.tsx +318 -0
- package/src/page-wizard/steps/LayoutStep.tsx +228 -0
- package/src/page-wizard/steps/SelectStep.tsx +256 -0
- package/src/page-wizard/steps/schema.ts +180 -0
- package/src/page-wizard/steps/usePageCreator.ts +279 -0
- package/src/splash-screen/NewPage.tsx +232 -0
- package/src/splash-screen/SectionHeadline.tsx +21 -0
- package/src/splash-screen/SplashScreen.tsx +156 -0
- package/src/tour/Tour.tsx +558 -0
- package/src/tour/default-tour.tsx +300 -0
- package/src/tour/preview-tour.tsx +127 -0
- package/src/types.ts +302 -0
- package/styles.css +476 -0
- package/tsconfig.build.json +21 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { WizardData } from "../PageWizard";
|
|
3
|
+
import { useEditContext } from "../../editor/client/editContext";
|
|
4
|
+
import { executePrompt } from "../../editor/services/aiService";
|
|
5
|
+
import { createWizardAiContext } from "../service";
|
|
6
|
+
import { classNames } from "primereact/utils";
|
|
7
|
+
import { Splitter, SplitterPanel } from "../../editor/ui/Splitter";
|
|
8
|
+
import { InputTextarea } from "primereact/inputtextarea";
|
|
9
|
+
import { StepComponentProps } from "../../config/types";
|
|
10
|
+
import { ActionButton } from "../../components/ActionButton";
|
|
11
|
+
import Generate from "./Generate";
|
|
12
|
+
type Option = {
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
id: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type Response = {
|
|
19
|
+
options: Option[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function SelectStep({
|
|
23
|
+
step,
|
|
24
|
+
data,
|
|
25
|
+
setData,
|
|
26
|
+
setStepCompleted,
|
|
27
|
+
internalState,
|
|
28
|
+
setInternalState,
|
|
29
|
+
}: StepComponentProps) {
|
|
30
|
+
const editContext = useEditContext();
|
|
31
|
+
const [options, setOptions] = useState<Option[]>([]);
|
|
32
|
+
const [loading, setLoading] = useState(false);
|
|
33
|
+
const [error, setError] = useState<string | null>(null);
|
|
34
|
+
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
|
|
35
|
+
const [instructions, setInstructions] = useState(step.instructions);
|
|
36
|
+
|
|
37
|
+
// Get the property name from the step or use "choice" as default
|
|
38
|
+
const propertyName = step.propertyName || "selectedOptions";
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (data[propertyName]?.length) {
|
|
42
|
+
setStepCompleted(true);
|
|
43
|
+
}
|
|
44
|
+
}, [data]);
|
|
45
|
+
|
|
46
|
+
// Initialize the property in data
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!data[propertyName]) {
|
|
49
|
+
setData((prevData: WizardData) => ({
|
|
50
|
+
...prevData,
|
|
51
|
+
[propertyName]: [],
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
}, [propertyName, setData]);
|
|
55
|
+
|
|
56
|
+
const generateOptions = async () => {
|
|
57
|
+
if (!editContext) return;
|
|
58
|
+
|
|
59
|
+
setLoading(true);
|
|
60
|
+
setError(null);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Create a prompt based on step instructions and data
|
|
64
|
+
const prompt = `${instructions}\n\nCurrent data: ${JSON.stringify(
|
|
65
|
+
data,
|
|
66
|
+
null,
|
|
67
|
+
2
|
|
68
|
+
)}`;
|
|
69
|
+
|
|
70
|
+
// Call the executePrompt function with the prompt
|
|
71
|
+
const result = await executePrompt(
|
|
72
|
+
[
|
|
73
|
+
{
|
|
74
|
+
content: `You are a helpful assistant that generates options for a wizard select step.
|
|
75
|
+
Generate a JSON array of options based on the instructions and current data.
|
|
76
|
+
ONLY respond with a valid JSON array without any explanation or additional text.
|
|
77
|
+
JSON format: {options: {title: string, description: string}[] }`,
|
|
78
|
+
name: "system",
|
|
79
|
+
role: "system",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
content: prompt,
|
|
83
|
+
name: "user",
|
|
84
|
+
role: "user",
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
editContext,
|
|
88
|
+
createWizardAiContext,
|
|
89
|
+
[""],
|
|
90
|
+
true,
|
|
91
|
+
undefined,
|
|
92
|
+
step.aiModel || "o3-mini-low"
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Parse the result and set options
|
|
96
|
+
if (result && result.content) {
|
|
97
|
+
try {
|
|
98
|
+
const generatedOptions = JSON.parse(result.content) as Response;
|
|
99
|
+
for (let i = 0; i < generatedOptions.options.length; i++) {
|
|
100
|
+
generatedOptions.options[i]!.id = i.toString();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setOptions(generatedOptions.options);
|
|
104
|
+
setInternalState((state: any) => ({
|
|
105
|
+
...state,
|
|
106
|
+
[step.id + "options"]: generatedOptions.options,
|
|
107
|
+
}));
|
|
108
|
+
} catch (parseError) {
|
|
109
|
+
console.error("Error parsing options:", parseError);
|
|
110
|
+
setError(
|
|
111
|
+
"Failed to parse options. The response was not in the expected format."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
setError("No options were generated. Please try again.");
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
setError("Failed to generate options. Please try again.");
|
|
119
|
+
console.error("Error generating options:", err);
|
|
120
|
+
} finally {
|
|
121
|
+
setLoading(false);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Generate options on component mount
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (internalState[step.id + "options"]?.length > 0) {
|
|
128
|
+
setOptions(internalState[step.id + "options"]);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
generateOptions();
|
|
132
|
+
}, [editContext, step.instructions, internalState]);
|
|
133
|
+
|
|
134
|
+
// Sync data[propertyName] with selectedOptions state
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
const currentSelections = (data[propertyName] as Array<Option>) || [];
|
|
137
|
+
setSelectedOptions(currentSelections.map((option: Option) => option.id));
|
|
138
|
+
}, [data, propertyName]);
|
|
139
|
+
|
|
140
|
+
// Handle option selection
|
|
141
|
+
const handleOptionSelect = (option: Option) => {
|
|
142
|
+
setData((prevData: WizardData) => {
|
|
143
|
+
const currentData = [
|
|
144
|
+
...((prevData[propertyName] as Array<Option>) || []),
|
|
145
|
+
];
|
|
146
|
+
const isSelected = currentData.some((o: Option) => o.id === option.id);
|
|
147
|
+
|
|
148
|
+
let updatedData;
|
|
149
|
+
if (isSelected) {
|
|
150
|
+
// Remove option if already selected
|
|
151
|
+
updatedData = currentData.filter((o: Option) => o.id !== option.id);
|
|
152
|
+
} else {
|
|
153
|
+
// Add option if not selected
|
|
154
|
+
updatedData = [...currentData, option];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
...prevData,
|
|
159
|
+
[propertyName]: updatedData,
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
if (error) {
|
|
165
|
+
return (
|
|
166
|
+
<div className="text-red-500 p-4">
|
|
167
|
+
{error}
|
|
168
|
+
<button
|
|
169
|
+
className="ml-4 px-2 py-1 bg-blue-500 text-white rounded"
|
|
170
|
+
onClick={() => window.location.reload()}
|
|
171
|
+
>
|
|
172
|
+
Retry
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const optionsPanel: SplitterPanel = {
|
|
179
|
+
name: "options",
|
|
180
|
+
defaultSize: "auto",
|
|
181
|
+
content: (
|
|
182
|
+
<div className="p-4 h-full">
|
|
183
|
+
{loading && (
|
|
184
|
+
<div className="flex items-center justify-center h-full">
|
|
185
|
+
<Generate title="Generating options..." />
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
188
|
+
{!loading && (
|
|
189
|
+
<div>
|
|
190
|
+
{options.length === 0 ? (
|
|
191
|
+
<div className="text-gray-500 p-4">
|
|
192
|
+
No options available. Try refreshing the page.
|
|
193
|
+
</div>
|
|
194
|
+
) : (
|
|
195
|
+
options.map((option, index) => {
|
|
196
|
+
const isSelected = selectedOptions.includes(option.id);
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<div
|
|
200
|
+
key={option.id}
|
|
201
|
+
className={classNames(
|
|
202
|
+
"mb-3 flex items-center p-3 rounded bg-white border-2",
|
|
203
|
+
index % 2 === 0
|
|
204
|
+
? "animate-fadeRight"
|
|
205
|
+
: "animate-fadeLeft",
|
|
206
|
+
isSelected
|
|
207
|
+
? "text-canvas-pink border-canvas-pink"
|
|
208
|
+
: "text-gray-600 border-gray-300"
|
|
209
|
+
)}
|
|
210
|
+
onClick={() => handleOptionSelect(option)}
|
|
211
|
+
>
|
|
212
|
+
<div className="flex flex-col flex-1 cursor-pointer">
|
|
213
|
+
<div className="font-medium">{option.title}</div>
|
|
214
|
+
<div className="text-xs text-gray-600">
|
|
215
|
+
{option.description}
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
{isSelected && (
|
|
219
|
+
<div className="text-canvas-pink ml-2">✓</div>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
})
|
|
224
|
+
)}
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
),
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const settingsPanel: SplitterPanel = {
|
|
232
|
+
name: "settings",
|
|
233
|
+
defaultSize: 400,
|
|
234
|
+
content: (
|
|
235
|
+
<div className="flex flex-col gap-2 pr-6">
|
|
236
|
+
<h3 className="text-sm font-bold">Instructions</h3>
|
|
237
|
+
<InputTextarea
|
|
238
|
+
className="h-48 text-sm"
|
|
239
|
+
value={instructions}
|
|
240
|
+
onChange={(e) => setInstructions(e.target.value)}
|
|
241
|
+
placeholder="Enter instructions for the options"
|
|
242
|
+
/>
|
|
243
|
+
<ActionButton
|
|
244
|
+
onClick={generateOptions}
|
|
245
|
+
isLoading={loading}
|
|
246
|
+
disabled={loading}
|
|
247
|
+
loadingText="Thinking..."
|
|
248
|
+
>
|
|
249
|
+
Regenerate Options
|
|
250
|
+
</ActionButton>
|
|
251
|
+
</div>
|
|
252
|
+
),
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return <Splitter panels={[settingsPanel, optionsPanel]} />;
|
|
256
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WizardSchemaComponent,
|
|
3
|
+
PageSchema,
|
|
4
|
+
SchemaComponent,
|
|
5
|
+
WizardSchemaField,
|
|
6
|
+
} from "../PageWizard";
|
|
7
|
+
type WizardSchemaComponentAccumulator = {
|
|
8
|
+
name: string;
|
|
9
|
+
type: string;
|
|
10
|
+
fields: WizardSchemaField[];
|
|
11
|
+
validParentPlaceholders: Set<string>;
|
|
12
|
+
validChildComponentTypes: Set<string>;
|
|
13
|
+
allowedOnRoot: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function convertPageSchemaToWizardComponents(
|
|
17
|
+
pageSchema: PageSchema,
|
|
18
|
+
whitelist: string[]
|
|
19
|
+
): WizardSchemaComponent[] {
|
|
20
|
+
const componentMap = new Map<string, WizardSchemaComponentAccumulator>();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Recursively processes a SchemaComponent.
|
|
24
|
+
*
|
|
25
|
+
* @param component - The current SchemaComponent to process.
|
|
26
|
+
* @param parentPlaceholder - The name of the placeholder where the component appears.
|
|
27
|
+
* @param isRoot - True if the component is at the root level.
|
|
28
|
+
* @param parentAccumulator - The accumulator of the parent component (if the parent is allowed).
|
|
29
|
+
*/
|
|
30
|
+
function processComponent(
|
|
31
|
+
component: SchemaComponent,
|
|
32
|
+
parentPlaceholder: string,
|
|
33
|
+
isRoot: boolean,
|
|
34
|
+
parentAccumulator?: WizardSchemaComponentAccumulator
|
|
35
|
+
): void {
|
|
36
|
+
const isAllowed = whitelist.includes(component.type);
|
|
37
|
+
let currentAccumulator: WizardSchemaComponentAccumulator | undefined =
|
|
38
|
+
undefined;
|
|
39
|
+
|
|
40
|
+
if (isAllowed) {
|
|
41
|
+
// If a parent accumulator exists (and is allowed), record the child's type as a valid child type.
|
|
42
|
+
if (parentAccumulator) {
|
|
43
|
+
parentAccumulator.validChildComponentTypes.add(component.type);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (componentMap.has(component.typeId)) {
|
|
47
|
+
currentAccumulator = componentMap.get(component.typeId)!;
|
|
48
|
+
currentAccumulator.validParentPlaceholders.add(parentPlaceholder);
|
|
49
|
+
// Mark as allowed on root if encountered at the root level in any instance.
|
|
50
|
+
if (isRoot) {
|
|
51
|
+
currentAccumulator.allowedOnRoot = true;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
currentAccumulator = {
|
|
55
|
+
name: component.typeId, // using typeId as the wizard component name
|
|
56
|
+
type: component.type,
|
|
57
|
+
fields: component.fields.map((field) => ({
|
|
58
|
+
name: field.name,
|
|
59
|
+
type: field.type,
|
|
60
|
+
})),
|
|
61
|
+
validParentPlaceholders: new Set([parentPlaceholder]),
|
|
62
|
+
validChildComponentTypes: new Set<string>(),
|
|
63
|
+
allowedOnRoot: isRoot,
|
|
64
|
+
};
|
|
65
|
+
componentMap.set(component.typeId, currentAccumulator);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Process child components regardless of whether the current component is allowed.
|
|
69
|
+
for (const placeholder of component.placeholders) {
|
|
70
|
+
for (const child of placeholder.components) {
|
|
71
|
+
processComponent(child, placeholder.name, false, currentAccumulator);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Process each top-level component in the PageSchema.
|
|
77
|
+
for (const placeholder of pageSchema) {
|
|
78
|
+
for (const component of placeholder.components) {
|
|
79
|
+
processComponent(component, placeholder.name, true);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Convert accumulators to final WizardSchemaComponent objects.
|
|
84
|
+
const wizardComponents: WizardSchemaComponent[] = [];
|
|
85
|
+
for (const acc of componentMap.values()) {
|
|
86
|
+
const wizardComponent: WizardSchemaComponent = {
|
|
87
|
+
type: acc.type,
|
|
88
|
+
fields: acc.fields,
|
|
89
|
+
allowedChildrenComponentTypes: Array.from(acc.validChildComponentTypes),
|
|
90
|
+
allowedOnRoot: acc.allowedOnRoot,
|
|
91
|
+
};
|
|
92
|
+
// Only include validParentPlaceholders if there is more than one.
|
|
93
|
+
if (acc.validParentPlaceholders.size > 1) {
|
|
94
|
+
wizardComponent.availableParentPlaceholders = Array.from(
|
|
95
|
+
acc.validParentPlaceholders
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
wizardComponents.push(wizardComponent);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return wizardComponents;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Finds a placeholder (by its name) where a child component of type `childComponentType`
|
|
106
|
+
* can be placed inside a parent of type `parentComponentType`. For a parentComponentType
|
|
107
|
+
* equal to "root" (case‑insensitive), the function searches among top‑level placeholders.
|
|
108
|
+
*
|
|
109
|
+
* @param schema - The PageSchema (an array of SchemaPlaceholders).
|
|
110
|
+
* @param parentComponentType - The type of the parent component, or "root" to indicate top‑level.
|
|
111
|
+
* @param childComponentType - The type of the child component to place.
|
|
112
|
+
* @returns The name of a valid placeholder or undefined if none is found.
|
|
113
|
+
*/
|
|
114
|
+
export function getPlaceholder(
|
|
115
|
+
schema: PageSchema,
|
|
116
|
+
parentComponentType: string,
|
|
117
|
+
childComponentType: string
|
|
118
|
+
): string | undefined {
|
|
119
|
+
// Check for the special case where we want to place at the root level.
|
|
120
|
+
if (parentComponentType.toLowerCase() === "root") {
|
|
121
|
+
// First, look for a top-level placeholder that already contains a component
|
|
122
|
+
// of the given childComponentType.
|
|
123
|
+
for (const topPlaceholder of schema) {
|
|
124
|
+
if (
|
|
125
|
+
topPlaceholder.components.some((c) => c.type === childComponentType)
|
|
126
|
+
) {
|
|
127
|
+
return topPlaceholder.name;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// If not found, return the first top-level placeholder that is empty.
|
|
131
|
+
for (const topPlaceholder of schema) {
|
|
132
|
+
if (topPlaceholder.components.length === 0) {
|
|
133
|
+
return topPlaceholder.name;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Recursive helper to search through nested SchemaComponents.
|
|
140
|
+
function searchComponents(components: SchemaComponent[]): string | undefined {
|
|
141
|
+
for (const component of components) {
|
|
142
|
+
// If this component matches the desired parent type, check its placeholders.
|
|
143
|
+
if (component.type === parentComponentType) {
|
|
144
|
+
// First, try to find a placeholder that already has a child of the desired type.
|
|
145
|
+
for (const placeholder of component.placeholders) {
|
|
146
|
+
if (
|
|
147
|
+
placeholder.components.some(
|
|
148
|
+
(child) => child.type === childComponentType
|
|
149
|
+
)
|
|
150
|
+
) {
|
|
151
|
+
return placeholder.name;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Otherwise, pick an empty placeholder.
|
|
155
|
+
for (const placeholder of component.placeholders) {
|
|
156
|
+
if (placeholder.components.length === 0) {
|
|
157
|
+
return placeholder.name;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Recurse into any placeholders in the current component.
|
|
162
|
+
for (const placeholder of component.placeholders) {
|
|
163
|
+
const result = searchComponents(placeholder.components);
|
|
164
|
+
if (result) {
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Search through all top-level placeholders' components.
|
|
173
|
+
for (const topPlaceholder of schema) {
|
|
174
|
+
const result = searchComponents(topPlaceholder.components);
|
|
175
|
+
if (result) {
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction, useMemo } from "react";
|
|
2
|
+
import {
|
|
3
|
+
SchemaComponent,
|
|
4
|
+
Wizard,
|
|
5
|
+
WizardPageComponent,
|
|
6
|
+
WizardPageModel,
|
|
7
|
+
WizardField,
|
|
8
|
+
} from "../PageWizard";
|
|
9
|
+
import { useEditContextRef } from "../../editor/client/editContext";
|
|
10
|
+
import { useEditContext } from "../../editor/client/editContext";
|
|
11
|
+
import { getComponentById } from "../../editor/componentTreeHelper";
|
|
12
|
+
import { getPlaceholder } from "./schema";
|
|
13
|
+
import { normalizeGuid } from "../../editor/utils";
|
|
14
|
+
import { ItemDescriptor } from "../../editor/pageModel";
|
|
15
|
+
|
|
16
|
+
export function usePageCreator(
|
|
17
|
+
pageItem: ItemDescriptor | undefined,
|
|
18
|
+
wizard: Wizard,
|
|
19
|
+
setPageModel: Dispatch<SetStateAction<WizardPageModel>>
|
|
20
|
+
) {
|
|
21
|
+
const editContext = useEditContext();
|
|
22
|
+
const editContextRef = useEditContextRef();
|
|
23
|
+
|
|
24
|
+
// Function to build a map of all components from wizard schema
|
|
25
|
+
const buildComponentTypeMap = () => {
|
|
26
|
+
const componentMap = new Map<string, string>();
|
|
27
|
+
const fieldIdsMap = new Map<string, string>();
|
|
28
|
+
const placeholderMap = new Map<string, string>();
|
|
29
|
+
|
|
30
|
+
const processComponentsRecursively = (components: SchemaComponent[]) => {
|
|
31
|
+
for (const component of components) {
|
|
32
|
+
// Store the mapping from component type to typeId
|
|
33
|
+
componentMap.set(component.type, component.typeId);
|
|
34
|
+
placeholderMap.set(component.typeId, component.type);
|
|
35
|
+
|
|
36
|
+
// Process nested components in placeholders
|
|
37
|
+
if (component.placeholders && component.placeholders.length > 0) {
|
|
38
|
+
for (const placeholder of component.placeholders) {
|
|
39
|
+
if (placeholder.components && placeholder.components.length > 0) {
|
|
40
|
+
processComponentsRecursively(placeholder.components);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (component.fields && component.fields.length > 0) {
|
|
45
|
+
for (const field of component.fields) {
|
|
46
|
+
fieldIdsMap.set(component.typeId + "_" + field.name, field.id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Start processing from the root level of the schema
|
|
53
|
+
if (wizard.schema && wizard.schema.length > 0) {
|
|
54
|
+
for (const placeholder of wizard.schema) {
|
|
55
|
+
if (placeholder.components && placeholder.components.length > 0) {
|
|
56
|
+
processComponentsRecursively(placeholder.components);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { componentMap, fieldIdsMap };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Build the component type to typeId map
|
|
65
|
+
const mappings = useMemo(() => buildComponentTypeMap(), [wizard.schema]);
|
|
66
|
+
|
|
67
|
+
const createNewComponent = async (
|
|
68
|
+
component: WizardPageComponent,
|
|
69
|
+
parentComponentType: string,
|
|
70
|
+
index: number,
|
|
71
|
+
parentComponentId?: string
|
|
72
|
+
): Promise<ItemDescriptor | undefined> => {
|
|
73
|
+
if (!editContext) return undefined;
|
|
74
|
+
const { componentMap } = mappings;
|
|
75
|
+
// Get the typeId from the map using the component type
|
|
76
|
+
const typeId = componentMap.get(component.type) || component.type;
|
|
77
|
+
|
|
78
|
+
// 1. Add the component to the placeholder
|
|
79
|
+
console.log(
|
|
80
|
+
`Adding component ${component.type} (typeId: ${typeId}) to placeholder ${parentComponentId} at index ${index}`
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const placeholderKey =
|
|
84
|
+
component.placeholder ||
|
|
85
|
+
getPlaceholder(wizard.schema, parentComponentType, component.type);
|
|
86
|
+
|
|
87
|
+
const placeholderId = parentComponentId
|
|
88
|
+
? placeholderKey + "_" + normalizeGuid(parentComponentId)
|
|
89
|
+
: placeholderKey;
|
|
90
|
+
|
|
91
|
+
if (!placeholderId) {
|
|
92
|
+
console.error("Placeholder ID not found", component);
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const addComponentOp = await editContext.operations.addComponent(
|
|
97
|
+
typeId, // Use typeId instead of just type
|
|
98
|
+
placeholderId,
|
|
99
|
+
index
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (!addComponentOp?.componentId) {
|
|
103
|
+
console.error(
|
|
104
|
+
"Failed to add component",
|
|
105
|
+
component,
|
|
106
|
+
"Result Op:",
|
|
107
|
+
addComponentOp
|
|
108
|
+
);
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Wait for the component to be created
|
|
113
|
+
const componentId = addComponentOp.componentId;
|
|
114
|
+
console.log("Waiting for component to be created", componentId);
|
|
115
|
+
|
|
116
|
+
let createdComponent;
|
|
117
|
+
let attempts = 0;
|
|
118
|
+
const maxAttempts = 40;
|
|
119
|
+
|
|
120
|
+
while (!createdComponent && attempts < maxAttempts) {
|
|
121
|
+
if (editContextRef.current?.page) {
|
|
122
|
+
createdComponent = getComponentById(
|
|
123
|
+
componentId,
|
|
124
|
+
editContextRef.current?.page
|
|
125
|
+
);
|
|
126
|
+
if (createdComponent) {
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (createdComponent) break;
|
|
131
|
+
|
|
132
|
+
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 100ms between attempts
|
|
133
|
+
attempts++;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!createdComponent) {
|
|
137
|
+
console.error(
|
|
138
|
+
"Timeout waiting for component to appear in page model",
|
|
139
|
+
componentId
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const componentDescriptor = {
|
|
144
|
+
...addComponentOp.mainItem,
|
|
145
|
+
id: addComponentOp.componentId,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
console.log("Component created:", componentDescriptor);
|
|
149
|
+
|
|
150
|
+
return componentDescriptor;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const findComponent = (components: WizardPageComponent[], path: number[]) => {
|
|
154
|
+
const child = components[path[0]!];
|
|
155
|
+
if (!child) return null;
|
|
156
|
+
if (path.length === 1) return child;
|
|
157
|
+
return findComponent(child.children || [], path.slice(1));
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Function to recursively create components from the page model
|
|
161
|
+
const createComponentsRecursively = async (
|
|
162
|
+
components: WizardPageComponent[],
|
|
163
|
+
parentComponentType: string,
|
|
164
|
+
parentComponentId?: string,
|
|
165
|
+
parentPath?: number[]
|
|
166
|
+
) => {
|
|
167
|
+
if (!editContext || !pageItem) return;
|
|
168
|
+
|
|
169
|
+
const { fieldIdsMap, componentMap } = mappings;
|
|
170
|
+
|
|
171
|
+
// Process each component in the array
|
|
172
|
+
for (let i = 0; i < components.length; i++) {
|
|
173
|
+
const component = components[i];
|
|
174
|
+
const componentPath = [...(parentPath || []), i];
|
|
175
|
+
|
|
176
|
+
if (!component?.id) {
|
|
177
|
+
if (!component?.type || !component?.fields) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const descriptor = await createNewComponent(
|
|
182
|
+
component,
|
|
183
|
+
parentComponentType,
|
|
184
|
+
i,
|
|
185
|
+
parentComponentId
|
|
186
|
+
);
|
|
187
|
+
if (!descriptor) continue;
|
|
188
|
+
component.id = descriptor.id;
|
|
189
|
+
setPageModel((prev) => {
|
|
190
|
+
const component = findComponent(prev.components, componentPath);
|
|
191
|
+
if (component) {
|
|
192
|
+
component.id = descriptor.id;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
...prev,
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (component.fields && component.fields.length > 0) {
|
|
201
|
+
const typeId = componentMap.get(component.type) || component.type;
|
|
202
|
+
|
|
203
|
+
// 2. Edit fields of the component
|
|
204
|
+
for (const field of component.fields) {
|
|
205
|
+
if (
|
|
206
|
+
!field.name ||
|
|
207
|
+
!field.value ||
|
|
208
|
+
field.value + field.type === field.writtenValue
|
|
209
|
+
) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const fieldId = fieldIdsMap.get(typeId + "_" + field.name);
|
|
214
|
+
|
|
215
|
+
if (!fieldId) {
|
|
216
|
+
console.error("Field not found", field);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const fieldDescriptor = {
|
|
221
|
+
fieldId: fieldId.toLowerCase().replace(/[{}]/g, ""),
|
|
222
|
+
item: {
|
|
223
|
+
id: component.id,
|
|
224
|
+
language: pageItem.language,
|
|
225
|
+
version: pageItem.version,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
console.log(
|
|
230
|
+
`Editing field ${field.name} of component ${component.id} with value ${field.value}`
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
let rawValue = field.value;
|
|
234
|
+
let value = field.value as any;
|
|
235
|
+
|
|
236
|
+
if (field.type === "Picture") {
|
|
237
|
+
value = {
|
|
238
|
+
Variants: [{ Name: "Landscape", MediaId: field.value }],
|
|
239
|
+
};
|
|
240
|
+
rawValue = JSON.stringify(value);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
await editContext.operations.editField({
|
|
244
|
+
field: fieldDescriptor,
|
|
245
|
+
value: value,
|
|
246
|
+
rawValue: rawValue,
|
|
247
|
+
refresh: "none",
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
setPageModel((prev) => {
|
|
251
|
+
const component = findComponent(prev.components, componentPath);
|
|
252
|
+
if (component) {
|
|
253
|
+
const prevField: WizardField | undefined = component.fields.find(
|
|
254
|
+
(f: WizardField) => f.name === field.name
|
|
255
|
+
);
|
|
256
|
+
if (prevField) {
|
|
257
|
+
prevField.writtenValue = field.value + field.type;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
...prev,
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 3. Recursively process placeholders
|
|
268
|
+
if (component.children && component.children.length > 0) {
|
|
269
|
+
await createComponentsRecursively(
|
|
270
|
+
component.children,
|
|
271
|
+
component.type,
|
|
272
|
+
component.id,
|
|
273
|
+
componentPath
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
return { createComponentsRecursively };
|
|
279
|
+
}
|