Cheyenne’s Note:
App.tsx
import React, { useState, useEffect } from ‘react’;
import { AppStep, UserData } from ‘./types’;
import { QUESTIONS } from ‘./constants’;
import { generateWealthVision } from ‘./services/geminiService’;
// — Components —
const Header: React.FC = () => (
);
const ProgressBar: React.FC<{ current: number; total: number }> = ({ current, total }) => (
);
const Button: React.FC<{ onClick?: () => void;
disabled?: boolean;
variant?: ‘primary’ | ‘secondary’;
children: React.ReactNode;
className?: string;
type?: “button” | “submit” | “reset”;
}> = ({ onClick, disabled, variant = ‘primary’, children, className = ”, type = ‘button’ }) => {
const base = “px-6 py-3 rounded-full font-semibold transition-all duration-200 flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed”;
const variants = {
primary: “bg-mfcblue-600 text-white hover:bg-mfcblue-700 shadow-md hover:shadow-lg active:scale-95”,
secondary: “bg-white text-mfcblue-600 border border-mfcblue-100 hover:bg-mfcblue-50”
};
return (
);
};
// — Main App —
export default function App() {
const [step, setStep] = useState
const [isEmbedded, setIsEmbedded] = useState(false);
const [userData, setUserData] = useState
image: null,
answers: {},
name: ”,
email: ”
});
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
const [visionUrl, setVisionUrl] = useState
const [error, setError] = useState
// Check for embed mode in URL
useEffect(() => {
const params = new URLSearchParams(window.location.search);
if (params.get(’embed’) === ‘true’) {
setIsEmbedded(true);
}
}, []);
const handleImageUpload = (e: React.ChangeEvent
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setUserData(prev => ({ …prev, image: reader.result as string }));
};
reader.readAsDataURL(file);
}
};
const handleAnswer = (questionId: string, value: string) => {
setUserData(prev => ({
…prev,
answers: { …prev.answers, [questionId]: value }
}));
if (currentQuestionIndex < QUESTIONS.length - 1) { setCurrentQuestionIndex(prev => prev + 1);
} else {
setStep(AppStep.CONTACT);
}
};
const handleSubmitContact = async (e: React.FormEvent) => {
e.preventDefault();
setStep(AppStep.PROCESSING);
try {
// Simulate backend lead sync
console.log(“Syncing lead to CRM:”, { name: userData.name, email: userData.email });
const resultImageUrl = await generateWealthVision(userData);
setVisionUrl(resultImageUrl);
setStep(AppStep.RESULT);
} catch (err) {
console.error(err);
setError(“We encountered an issue generating your vision. Please try again.”);
setStep(AppStep.CONTACT);
}
};
const reset = () => {
setStep(AppStep.WELCOME);
setUserData({ image: null, answers: {}, name: ”, email: ” });
setCurrentQuestionIndex(0);
setVisionUrl(null);
setError(null);
};
const renderStep = () => {
switch (step) {
case AppStep.WELCOME:
return (
Welcome to Wealth-O-Vision. Through a combination of your current life data and advanced AI,
we’ll help you visualize the prosperous future you’re working towards.


);
case AppStep.UPLOAD:
return (
Please upload a clear picture of yourself (and your spouse/family if applicable).
) : (
Click to upload photo
PNG, JPG or JPEG (Max 10MB)
)}
);
case AppStep.QUESTIONS:
const currentQ = QUESTIONS[currentQuestionIndex];
return (
);
case AppStep.CONTACT:
return (
);
case AppStep.PROCESSING:
return (
“Developing the details of your {userData.answers.retirement_age} milestone…”
);
case AppStep.RESULT:
return (
);
}
};
return (
{!isEmbedded && (
)}
);
}