@umituz/react-native-ai-generation-content 1.26.48 → 1.26.49
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/package.json +1 -1
- package/src/domains/generation/wizard/presentation/components/GenericWizardFlow.tsx +36 -248
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.tsx +158 -0
- package/src/domains/generation/wizard/presentation/components/index.ts +2 -0
- package/src/domains/generation/wizard/presentation/utilities/index.ts +1 -0
- package/src/domains/generation/wizard/presentation/utilities/validateScenario.ts +56 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.26.
|
|
3
|
+
"version": "1.26.49",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -17,17 +17,15 @@ import { View, StyleSheet } from "react-native";
|
|
|
17
17
|
import { useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
18
18
|
import { useFlow } from "../../../infrastructure/flow/useFlow";
|
|
19
19
|
import { StepType, type StepDefinition } from "../../../../../domain/entities/flow-config.types";
|
|
20
|
-
import type { WizardFeatureConfig
|
|
20
|
+
import type { WizardFeatureConfig } from "../../domain/entities/wizard-config.types";
|
|
21
21
|
import { buildFlowStepsFromWizard } from "../../infrastructure/builders/dynamic-step-builder";
|
|
22
22
|
import { useWizardGeneration, type WizardScenarioData } from "../hooks/useWizardGeneration";
|
|
23
23
|
import type { AlertMessages } from "../../../../../presentation/hooks/generation/types";
|
|
24
24
|
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
25
|
-
import { GenericPhotoUploadScreen } from "../screens/GenericPhotoUploadScreen";
|
|
26
|
-
import { GeneratingScreen } from "../screens/GeneratingScreen";
|
|
27
|
-
import { ScenarioPreviewScreen } from "../../../../scenarios/presentation/screens/ScenarioPreviewScreen";
|
|
28
|
-
import { ResultPreviewScreen } from "../../../../result-preview/presentation/components/ResultPreviewScreen";
|
|
29
|
-
import { useResultActions } from "../../../../result-preview/presentation/hooks/useResultActions";
|
|
30
25
|
import type { Creation } from "../../../../creations/domain/entities/Creation";
|
|
26
|
+
import { useResultActions } from "../../../../result-preview/presentation/hooks/useResultActions";
|
|
27
|
+
import { validateScenario } from "../utilities/validateScenario";
|
|
28
|
+
import { WizardStepRenderer } from "./WizardStepRenderer";
|
|
31
29
|
|
|
32
30
|
export interface GenericWizardFlowProps {
|
|
33
31
|
readonly featureConfig: WizardFeatureConfig;
|
|
@@ -68,6 +66,7 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
68
66
|
}) => {
|
|
69
67
|
const tokens = useAppDesignTokens();
|
|
70
68
|
const [currentCreation, setCurrentCreation] = useState<Creation | null>(null);
|
|
69
|
+
const prevStepIdRef = useRef<string | undefined>(undefined);
|
|
71
70
|
|
|
72
71
|
const flowSteps = useMemo<StepDefinition[]>(() => {
|
|
73
72
|
return buildFlowStepsFromWizard(featureConfig, {
|
|
@@ -76,13 +75,7 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
76
75
|
});
|
|
77
76
|
}, [featureConfig]);
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
const flow = useFlow({
|
|
81
|
-
steps: flowSteps,
|
|
82
|
-
initialStepIndex: 0,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Destructure flow to get stable references for useCallback dependencies
|
|
78
|
+
const flow = useFlow({ steps: flowSteps, initialStepIndex: 0 });
|
|
86
79
|
const {
|
|
87
80
|
currentStep,
|
|
88
81
|
currentStepIndex,
|
|
@@ -101,72 +94,22 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
101
94
|
imageUrl: resultImageUrl,
|
|
102
95
|
});
|
|
103
96
|
|
|
104
|
-
const
|
|
105
|
-
(progress: number) => {
|
|
106
|
-
updateProgress(progress);
|
|
107
|
-
},
|
|
108
|
-
[updateProgress],
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const handleGenerationComplete = useCallback(
|
|
112
|
-
(result: unknown) => {
|
|
113
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
114
|
-
console.log("[GenericWizardFlow] Generation completed, saving result and advancing to result preview");
|
|
115
|
-
}
|
|
116
|
-
setResult(result);
|
|
117
|
-
setCurrentCreation(result as Creation);
|
|
118
|
-
nextStep();
|
|
119
|
-
onGenerationComplete?.(result);
|
|
120
|
-
},
|
|
121
|
-
[setResult, nextStep, onGenerationComplete],
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Validate scenario - NO FALLBACK, aiPrompt is REQUIRED
|
|
125
|
-
const validatedScenario = useMemo(() => {
|
|
126
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
127
|
-
console.log("[GenericWizardFlow] Validating scenario", {
|
|
128
|
-
hasScenario: !!scenario,
|
|
129
|
-
scenarioId: scenario?.id,
|
|
130
|
-
hasAiPrompt: !!scenario?.aiPrompt,
|
|
131
|
-
aiPromptValue: scenario?.aiPrompt,
|
|
132
|
-
aiPromptLength: scenario?.aiPrompt?.length,
|
|
133
|
-
hasModel: !!scenario?.model,
|
|
134
|
-
scenarioModel: scenario?.model,
|
|
135
|
-
outputType: scenario?.outputType,
|
|
136
|
-
fullScenario: JSON.stringify(scenario, null, 2),
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (!scenario || !scenario.id) {
|
|
141
|
-
throw new Error("[GenericWizardFlow] Scenario is required");
|
|
142
|
-
}
|
|
97
|
+
const validatedScenario = useMemo(() => validateScenario(scenario), [scenario]);
|
|
143
98
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
scenarioId: scenario.id,
|
|
148
|
-
aiPrompt: scenario.aiPrompt,
|
|
149
|
-
fullScenario: scenario,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
throw new Error(`[GenericWizardFlow] Scenario "${scenario.id}" must have aiPrompt field`);
|
|
153
|
-
}
|
|
99
|
+
const handleProgressChange = useCallback((progress: number) => {
|
|
100
|
+
updateProgress(progress);
|
|
101
|
+
}, [updateProgress]);
|
|
154
102
|
|
|
103
|
+
const handleGenerationComplete = useCallback((result: unknown) => {
|
|
155
104
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
156
|
-
console.log("[GenericWizardFlow]
|
|
157
|
-
scenarioId: scenario.id,
|
|
158
|
-
model: scenario.model,
|
|
159
|
-
outputType: scenario.outputType,
|
|
160
|
-
promptLength: scenario.aiPrompt.length,
|
|
161
|
-
promptPreview: scenario.aiPrompt.substring(0, 100),
|
|
162
|
-
});
|
|
105
|
+
console.log("[GenericWizardFlow] Generation completed");
|
|
163
106
|
}
|
|
107
|
+
setResult(result);
|
|
108
|
+
setCurrentCreation(result as Creation);
|
|
109
|
+
nextStep();
|
|
110
|
+
onGenerationComplete?.(result);
|
|
111
|
+
}, [setResult, nextStep, onGenerationComplete]);
|
|
164
112
|
|
|
165
|
-
return scenario;
|
|
166
|
-
}, [scenario]);
|
|
167
|
-
|
|
168
|
-
// Generation hook - handles AI generation automatically
|
|
169
|
-
// Note: Hook is used for its side effects (automatic generation)
|
|
170
113
|
useWizardGeneration({
|
|
171
114
|
scenario: validatedScenario,
|
|
172
115
|
wizardData: customData,
|
|
@@ -179,40 +122,16 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
179
122
|
onCreditsExhausted,
|
|
180
123
|
});
|
|
181
124
|
|
|
182
|
-
// Track previous step ID to prevent infinite loops
|
|
183
|
-
const prevStepIdRef = useRef<string | undefined>(undefined);
|
|
184
|
-
|
|
185
|
-
// DEBUG logging
|
|
186
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
187
|
-
console.log("[GenericWizardFlow] Render", {
|
|
188
|
-
featureId: featureConfig.id,
|
|
189
|
-
currentStepId: currentStep?.id,
|
|
190
|
-
currentStepType: currentStep?.type,
|
|
191
|
-
stepIndex: currentStepIndex,
|
|
192
|
-
totalSteps: flowSteps.length,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Notify parent when step changes
|
|
197
|
-
// Only call onStepChange when step ID actually changes (not on every object reference change)
|
|
198
125
|
useEffect(() => {
|
|
199
126
|
if (currentStep && onStepChange) {
|
|
200
127
|
const currentStepId = currentStep.id;
|
|
201
|
-
// Only notify if step ID changed
|
|
202
128
|
if (prevStepIdRef.current !== currentStepId) {
|
|
203
129
|
prevStepIdRef.current = currentStepId;
|
|
204
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
205
|
-
console.log("[GenericWizardFlow] Step changed", {
|
|
206
|
-
stepId: currentStep.id,
|
|
207
|
-
stepType: currentStep.type,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
130
|
onStepChange(currentStep.id, currentStep.type);
|
|
211
131
|
}
|
|
212
132
|
}
|
|
213
133
|
}, [currentStep, currentStepIndex, onStepChange]);
|
|
214
134
|
|
|
215
|
-
// Handle back
|
|
216
135
|
const handleBack = useCallback(() => {
|
|
217
136
|
if (currentStepIndex === 0) {
|
|
218
137
|
onBack?.();
|
|
@@ -221,18 +140,12 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
221
140
|
}
|
|
222
141
|
}, [currentStepIndex, previousStep, onBack]);
|
|
223
142
|
|
|
224
|
-
// Handle photo continue - saves photo and moves to next step
|
|
225
143
|
const handlePhotoContinue = useCallback((stepId: string, image: UploadedImage) => {
|
|
226
144
|
setCustomData(stepId, image);
|
|
227
145
|
|
|
228
|
-
// Check if this is the last step before generating
|
|
229
146
|
if (currentStepIndex === flowSteps.length - 2) {
|
|
230
|
-
// Next step is GENERATING - call onGenerationStart
|
|
231
147
|
if (onGenerationStart) {
|
|
232
148
|
onGenerationStart({ ...customData, [stepId]: image }, () => {
|
|
233
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
234
|
-
console.log("[GenericWizardFlow] Proceeding to GENERATING step");
|
|
235
|
-
}
|
|
236
149
|
nextStep();
|
|
237
150
|
});
|
|
238
151
|
}
|
|
@@ -242,152 +155,27 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
242
155
|
nextStep();
|
|
243
156
|
}, [currentStepIndex, flowSteps.length, customData, setCustomData, nextStep, onGenerationStart]);
|
|
244
157
|
|
|
245
|
-
// Render current step
|
|
246
|
-
const renderCurrentStep = useCallback(() => {
|
|
247
|
-
const step = currentStep;
|
|
248
|
-
if (!step) {
|
|
249
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
250
|
-
console.warn("[GenericWizardFlow] No current step!");
|
|
251
|
-
}
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
256
|
-
console.log("[GenericWizardFlow] Rendering step", {
|
|
257
|
-
stepId: step.id,
|
|
258
|
-
stepType: step.type,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
switch (step.type) {
|
|
263
|
-
case StepType.SCENARIO_PREVIEW: {
|
|
264
|
-
if (renderPreview) {
|
|
265
|
-
return renderPreview(nextStep);
|
|
266
|
-
}
|
|
267
|
-
return (
|
|
268
|
-
<ScenarioPreviewScreen
|
|
269
|
-
scenario={scenario}
|
|
270
|
-
translations={{
|
|
271
|
-
continueButton: t("common.continue"),
|
|
272
|
-
whatToExpect: t("scenarioPreview.whatToExpect"),
|
|
273
|
-
}}
|
|
274
|
-
onContinue={nextStep}
|
|
275
|
-
onBack={handleBack}
|
|
276
|
-
t={t}
|
|
277
|
-
/>
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
case StepType.GENERATING: {
|
|
282
|
-
if (renderGenerating) {
|
|
283
|
-
return renderGenerating(generationProgress);
|
|
284
|
-
}
|
|
285
|
-
return (
|
|
286
|
-
<GeneratingScreen
|
|
287
|
-
progress={generationProgress}
|
|
288
|
-
scenario={scenario}
|
|
289
|
-
t={t}
|
|
290
|
-
/>
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
case StepType.RESULT_PREVIEW: {
|
|
295
|
-
if (renderResult) {
|
|
296
|
-
return renderResult(generationResult);
|
|
297
|
-
}
|
|
298
|
-
const creation = generationResult as Creation;
|
|
299
|
-
const imageUrl = creation?.output?.imageUrl || creation?.uri || "";
|
|
300
|
-
if (!imageUrl) return null;
|
|
301
|
-
return (
|
|
302
|
-
<ResultPreviewScreen
|
|
303
|
-
imageUrl={imageUrl}
|
|
304
|
-
isSaving={isSaving}
|
|
305
|
-
isSharing={isSharing}
|
|
306
|
-
onDownload={handleDownload}
|
|
307
|
-
onShare={handleShare}
|
|
308
|
-
onTryAgain={onTryAgain || onBack || (() => {})}
|
|
309
|
-
onNavigateBack={onTryAgain || onBack || (() => {})}
|
|
310
|
-
translations={{
|
|
311
|
-
title: t("generation.result.title"),
|
|
312
|
-
yourResult: t("generation.result.yourResult"),
|
|
313
|
-
saveButton: t("generation.result.save"),
|
|
314
|
-
saving: t("generation.result.saving"),
|
|
315
|
-
shareButton: t("generation.result.share"),
|
|
316
|
-
sharing: t("generation.result.sharing"),
|
|
317
|
-
tryAnother: t("generation.result.tryAnother"),
|
|
318
|
-
}}
|
|
319
|
-
/>
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
case StepType.PARTNER_UPLOAD: {
|
|
324
|
-
// Get wizard step config
|
|
325
|
-
const wizardConfig = step.config as WizardStepConfig;
|
|
326
|
-
|
|
327
|
-
// Use titleKey from config, fallback to step-specific translation key
|
|
328
|
-
const titleKey = wizardConfig?.titleKey || `wizard.steps.${step.id}.title`;
|
|
329
|
-
const title = t(titleKey);
|
|
330
|
-
|
|
331
|
-
// Subtitle from config
|
|
332
|
-
const subtitleKey = wizardConfig?.subtitleKey || `wizard.steps.${step.id}.subtitle`;
|
|
333
|
-
const subtitle = t(subtitleKey);
|
|
334
|
-
|
|
335
|
-
// Get existing photo for this step from customData
|
|
336
|
-
const existingPhoto = customData[step.id] as UploadedImage | undefined;
|
|
337
|
-
|
|
338
|
-
return (
|
|
339
|
-
<GenericPhotoUploadScreen
|
|
340
|
-
translations={{
|
|
341
|
-
title,
|
|
342
|
-
subtitle,
|
|
343
|
-
continue: t("common.continue"),
|
|
344
|
-
tapToUpload: t("photoUpload.tapToUpload"),
|
|
345
|
-
selectPhoto: t("photoUpload.selectPhoto"),
|
|
346
|
-
change: t("common.change"),
|
|
347
|
-
fileTooLarge: t("common.errors.file_too_large"),
|
|
348
|
-
maxFileSize: t("common.errors.max_file_size"),
|
|
349
|
-
error: t("common.error"),
|
|
350
|
-
uploadFailed: t("common.errors.upload_failed"),
|
|
351
|
-
}}
|
|
352
|
-
t={t}
|
|
353
|
-
onBack={handleBack}
|
|
354
|
-
onContinue={(image) => handlePhotoContinue(step.id, image)}
|
|
355
|
-
existingImage={existingPhoto}
|
|
356
|
-
/>
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
default:
|
|
361
|
-
// Other step types should be handled by custom render props
|
|
362
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
363
|
-
console.warn("[GenericWizardFlow] Unhandled step type", { stepType: step.type });
|
|
364
|
-
}
|
|
365
|
-
return null;
|
|
366
|
-
}
|
|
367
|
-
}, [
|
|
368
|
-
currentStep,
|
|
369
|
-
customData,
|
|
370
|
-
generationProgress,
|
|
371
|
-
generationResult,
|
|
372
|
-
nextStep,
|
|
373
|
-
renderPreview,
|
|
374
|
-
renderGenerating,
|
|
375
|
-
renderResult,
|
|
376
|
-
handlePhotoContinue,
|
|
377
|
-
handleBack,
|
|
378
|
-
isSaving,
|
|
379
|
-
isSharing,
|
|
380
|
-
handleDownload,
|
|
381
|
-
handleShare,
|
|
382
|
-
onTryAgain,
|
|
383
|
-
onBack,
|
|
384
|
-
scenario,
|
|
385
|
-
t,
|
|
386
|
-
]);
|
|
387
|
-
|
|
388
158
|
return (
|
|
389
159
|
<View style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
|
|
390
|
-
|
|
160
|
+
<WizardStepRenderer
|
|
161
|
+
step={currentStep}
|
|
162
|
+
scenario={scenario}
|
|
163
|
+
customData={customData}
|
|
164
|
+
generationProgress={generationProgress}
|
|
165
|
+
generationResult={generationResult}
|
|
166
|
+
isSaving={isSaving}
|
|
167
|
+
isSharing={isSharing}
|
|
168
|
+
onNext={nextStep}
|
|
169
|
+
onBack={handleBack}
|
|
170
|
+
onPhotoContinue={handlePhotoContinue}
|
|
171
|
+
onDownload={handleDownload}
|
|
172
|
+
onShare={handleShare}
|
|
173
|
+
onTryAgain={onTryAgain}
|
|
174
|
+
t={t}
|
|
175
|
+
renderPreview={renderPreview}
|
|
176
|
+
renderGenerating={renderGenerating}
|
|
177
|
+
renderResult={renderResult}
|
|
178
|
+
/>
|
|
391
179
|
</View>
|
|
392
180
|
);
|
|
393
181
|
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard Step Renderer Component
|
|
3
|
+
* Renders the appropriate screen based on current step type
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { StepType, type StepDefinition } from "../../../../../domain/entities/flow-config.types";
|
|
8
|
+
import type { WizardStepConfig } from "../../domain/entities/wizard-config.types";
|
|
9
|
+
import type { WizardScenarioData } from "../hooks/useWizardGeneration";
|
|
10
|
+
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
11
|
+
import type { Creation } from "../../../../creations/domain/entities/Creation";
|
|
12
|
+
import { GenericPhotoUploadScreen } from "../screens/GenericPhotoUploadScreen";
|
|
13
|
+
import { GeneratingScreen } from "../screens/GeneratingScreen";
|
|
14
|
+
import { ScenarioPreviewScreen } from "../../../../scenarios/presentation/screens/ScenarioPreviewScreen";
|
|
15
|
+
import { ResultPreviewScreen } from "../../../../result-preview/presentation/components/ResultPreviewScreen";
|
|
16
|
+
|
|
17
|
+
export interface WizardStepRendererProps {
|
|
18
|
+
readonly step: StepDefinition | undefined;
|
|
19
|
+
readonly scenario?: WizardScenarioData;
|
|
20
|
+
readonly customData: Record<string, unknown>;
|
|
21
|
+
readonly generationProgress: number;
|
|
22
|
+
readonly generationResult: unknown;
|
|
23
|
+
readonly isSaving: boolean;
|
|
24
|
+
readonly isSharing: boolean;
|
|
25
|
+
readonly onNext: () => void;
|
|
26
|
+
readonly onBack: () => void;
|
|
27
|
+
readonly onPhotoContinue: (stepId: string, image: UploadedImage) => void;
|
|
28
|
+
readonly onDownload: () => void;
|
|
29
|
+
readonly onShare: () => void;
|
|
30
|
+
readonly onTryAgain?: () => void;
|
|
31
|
+
readonly t: (key: string) => string;
|
|
32
|
+
readonly renderPreview?: (onContinue: () => void) => React.ReactElement | null;
|
|
33
|
+
readonly renderGenerating?: (progress: number) => React.ReactElement | null;
|
|
34
|
+
readonly renderResult?: (result: unknown) => React.ReactElement | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
38
|
+
step,
|
|
39
|
+
scenario,
|
|
40
|
+
customData,
|
|
41
|
+
generationProgress,
|
|
42
|
+
generationResult,
|
|
43
|
+
isSaving,
|
|
44
|
+
isSharing,
|
|
45
|
+
onNext,
|
|
46
|
+
onBack,
|
|
47
|
+
onPhotoContinue,
|
|
48
|
+
onDownload,
|
|
49
|
+
onShare,
|
|
50
|
+
onTryAgain,
|
|
51
|
+
t,
|
|
52
|
+
renderPreview,
|
|
53
|
+
renderGenerating,
|
|
54
|
+
renderResult,
|
|
55
|
+
}) => {
|
|
56
|
+
if (!step) {
|
|
57
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
58
|
+
console.warn("[WizardStepRenderer] No current step!");
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
switch (step.type) {
|
|
64
|
+
case StepType.SCENARIO_PREVIEW: {
|
|
65
|
+
if (renderPreview) {
|
|
66
|
+
return renderPreview(onNext);
|
|
67
|
+
}
|
|
68
|
+
return (
|
|
69
|
+
<ScenarioPreviewScreen
|
|
70
|
+
scenario={scenario}
|
|
71
|
+
translations={{
|
|
72
|
+
continueButton: t("common.continue"),
|
|
73
|
+
whatToExpect: t("scenarioPreview.whatToExpect"),
|
|
74
|
+
}}
|
|
75
|
+
onContinue={onNext}
|
|
76
|
+
onBack={onBack}
|
|
77
|
+
t={t}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case StepType.GENERATING: {
|
|
83
|
+
if (renderGenerating) {
|
|
84
|
+
return renderGenerating(generationProgress);
|
|
85
|
+
}
|
|
86
|
+
return (
|
|
87
|
+
<GeneratingScreen
|
|
88
|
+
progress={generationProgress}
|
|
89
|
+
scenario={scenario}
|
|
90
|
+
t={t}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
case StepType.RESULT_PREVIEW: {
|
|
96
|
+
if (renderResult) {
|
|
97
|
+
return renderResult(generationResult);
|
|
98
|
+
}
|
|
99
|
+
const creation = generationResult as Creation;
|
|
100
|
+
const imageUrl = creation?.output?.imageUrl || creation?.uri || "";
|
|
101
|
+
if (!imageUrl) return null;
|
|
102
|
+
return (
|
|
103
|
+
<ResultPreviewScreen
|
|
104
|
+
imageUrl={imageUrl}
|
|
105
|
+
isSaving={isSaving}
|
|
106
|
+
isSharing={isSharing}
|
|
107
|
+
onDownload={onDownload}
|
|
108
|
+
onShare={onShare}
|
|
109
|
+
onTryAgain={onTryAgain || onBack}
|
|
110
|
+
onNavigateBack={onTryAgain || onBack}
|
|
111
|
+
translations={{
|
|
112
|
+
title: t("generation.result.title"),
|
|
113
|
+
yourResult: t("generation.result.yourResult"),
|
|
114
|
+
saveButton: t("generation.result.save"),
|
|
115
|
+
saving: t("generation.result.saving"),
|
|
116
|
+
shareButton: t("generation.result.share"),
|
|
117
|
+
sharing: t("generation.result.sharing"),
|
|
118
|
+
tryAnother: t("generation.result.tryAnother"),
|
|
119
|
+
}}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case StepType.PARTNER_UPLOAD: {
|
|
125
|
+
const wizardConfig = step.config as WizardStepConfig;
|
|
126
|
+
const titleKey = wizardConfig?.titleKey || `wizard.steps.${step.id}.title`;
|
|
127
|
+
const subtitleKey = wizardConfig?.subtitleKey || `wizard.steps.${step.id}.subtitle`;
|
|
128
|
+
const existingPhoto = customData[step.id] as UploadedImage | undefined;
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<GenericPhotoUploadScreen
|
|
132
|
+
translations={{
|
|
133
|
+
title: t(titleKey),
|
|
134
|
+
subtitle: t(subtitleKey),
|
|
135
|
+
continue: t("common.continue"),
|
|
136
|
+
tapToUpload: t("photoUpload.tapToUpload"),
|
|
137
|
+
selectPhoto: t("photoUpload.selectPhoto"),
|
|
138
|
+
change: t("common.change"),
|
|
139
|
+
fileTooLarge: t("common.errors.file_too_large"),
|
|
140
|
+
maxFileSize: t("common.errors.max_file_size"),
|
|
141
|
+
error: t("common.error"),
|
|
142
|
+
uploadFailed: t("common.errors.upload_failed"),
|
|
143
|
+
}}
|
|
144
|
+
t={t}
|
|
145
|
+
onBack={onBack}
|
|
146
|
+
onContinue={(image) => onPhotoContinue(step.id, image)}
|
|
147
|
+
existingImage={existingPhoto}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
default:
|
|
153
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
154
|
+
console.warn("[WizardStepRenderer] Unhandled step type", { stepType: step.type });
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validateScenario, type ScenarioValidationResult } from "./validateScenario";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario validation utility
|
|
3
|
+
* Validates that scenario has required fields for wizard generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { WizardScenarioData } from "../hooks/useWizardGeneration";
|
|
7
|
+
|
|
8
|
+
export interface ScenarioValidationResult {
|
|
9
|
+
isValid: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
scenario?: WizardScenarioData;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validates scenario data for wizard generation
|
|
16
|
+
* @throws Error if scenario is invalid
|
|
17
|
+
*/
|
|
18
|
+
export const validateScenario = (
|
|
19
|
+
scenario: WizardScenarioData | undefined,
|
|
20
|
+
): WizardScenarioData => {
|
|
21
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
22
|
+
console.log("[validateScenario] Validating", {
|
|
23
|
+
hasScenario: !!scenario,
|
|
24
|
+
scenarioId: scenario?.id,
|
|
25
|
+
hasAiPrompt: !!scenario?.aiPrompt,
|
|
26
|
+
aiPromptLength: scenario?.aiPrompt?.length,
|
|
27
|
+
hasModel: !!scenario?.model,
|
|
28
|
+
outputType: scenario?.outputType,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!scenario || !scenario.id) {
|
|
33
|
+
throw new Error("[validateScenario] Scenario is required");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!scenario.aiPrompt || scenario.aiPrompt.trim() === "") {
|
|
37
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
38
|
+
console.error("[validateScenario] CRITICAL: Scenario missing aiPrompt!", {
|
|
39
|
+
scenarioId: scenario.id,
|
|
40
|
+
aiPrompt: scenario.aiPrompt,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`[validateScenario] Scenario "${scenario.id}" must have aiPrompt field`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
47
|
+
console.log("[validateScenario] Validation passed", {
|
|
48
|
+
scenarioId: scenario.id,
|
|
49
|
+
model: scenario.model,
|
|
50
|
+
outputType: scenario.outputType,
|
|
51
|
+
promptLength: scenario.aiPrompt.length,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return scenario;
|
|
56
|
+
};
|