-
Notifications
You must be signed in to change notification settings - Fork 0
fix: prevent two-pages-in-one layout bug on root and onboarding #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,40 +1,48 @@ | ||
| "use client"; | ||
|
|
||
| import { useEffect } from "react"; | ||
| import { useEffect, useState } from "react"; | ||
| import { useRouter } from "next/navigation"; | ||
| import { useGuestMode } from "@/hooks/useGuestMode"; | ||
| import { GuestOnboardingFlow } from "@/components/features/onboarding/GuestOnboardingFlow"; | ||
|
|
||
| const BG = "bg-[radial-gradient(circle_at_top_left,_rgba(124,92,255,0.16),_transparent_28rem),linear-gradient(180deg,_#fffefc,_#f7f2ff)]"; | ||
|
|
||
| function LoadingSpinner() { | ||
| return ( | ||
| <div className={`flex min-h-[100dvh] items-center justify-center ${BG}`}> | ||
| <div className="flex items-center gap-3 rounded-full border border-white/70 bg-white/80 px-4 py-3 shadow-xl backdrop-blur"> | ||
| <div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" /> | ||
| <span className="text-sm font-semibold text-foreground">Preparing your budget world</span> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export default function RootPage() { | ||
| const router = useRouter(); | ||
| const { isGuest, enableGuestMode, guestData } = useGuestMode(); | ||
| const [mounted, setMounted] = useState(false); | ||
|
|
||
| useEffect(() => { | ||
| setMounted(true); | ||
| }, []); | ||
|
|
||
| useEffect(() => { | ||
| if (!mounted) return; | ||
| if (!isGuest) { | ||
| enableGuestMode(); | ||
| } else if (guestData.onboarding?.questions_answered) { | ||
| router.replace("/dashboard"); | ||
| } | ||
| }, [isGuest, guestData.onboarding?.questions_answered, enableGuestMode, router]); | ||
|
|
||
| const handleOnboardingComplete = () => { | ||
| router.replace("/dashboard"); | ||
| }; | ||
|
|
||
| if (!isGuest) { | ||
| return ( | ||
| <div className="flex min-h-[100dvh] items-center justify-center bg-[radial-gradient(circle_at_top,_rgba(124,92,255,0.16),_transparent_32rem),linear-gradient(180deg,_#fffefc,_#f8f4ff)]"> | ||
| <div className="flex items-center gap-3 rounded-full border border-white/70 bg-white/80 px-4 py-3 shadow-xl backdrop-blur"> | ||
| <div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" /> | ||
| <span className="text-sm font-semibold text-foreground">Preparing your budget world</span> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }, [mounted, isGuest, guestData.onboarding?.questions_answered, enableGuestMode, router]); | ||
|
|
||
| if (!mounted || !isGuest) { | ||
| return <LoadingSpinner />; | ||
| } | ||
|
|
||
| if (guestData.onboarding?.questions_answered) { | ||
| return null; | ||
| return <LoadingSpinner />; | ||
| } | ||
|
|
||
| return <GuestOnboardingFlow onComplete={handleOnboardingComplete} />; | ||
| return <GuestOnboardingFlow onComplete={() => router.replace("/dashboard")} />; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -169,7 +169,7 @@ export function ConversationalOnboarding() { | |||||
|
|
||||||
| if (phase === "scan") { | ||||||
| return ( | ||||||
| <div className="flex min-h-screen flex-col items-center justify-center bg-background p-6 text-foreground"> | ||||||
| <div className="fixed inset-0 z-50 flex flex-col items-center justify-center overflow-y-auto bg-background p-6 text-foreground"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents
Suggested change
|
||||||
| <div className="w-full max-w-md space-y-6"> | ||||||
| <motion.div | ||||||
| initial={{ opacity: 0, y: 20 }} | ||||||
|
|
@@ -262,7 +262,7 @@ export function ConversationalOnboarding() { | |||||
| if (phase === "pet") { | ||||||
| const SPECIES_ORDER: PetSpecies[] = ["squirrel", "fox", "raccoon", "rabbit"]; | ||||||
| return ( | ||||||
| <div className="flex min-h-screen flex-col items-center justify-center bg-background p-6 text-foreground"> | ||||||
| <div className="fixed inset-0 z-50 flex flex-col items-center justify-center overflow-y-auto bg-background p-6 text-foreground"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the scan phase, combining
Suggested change
|
||||||
| <div className="w-full max-w-md space-y-6"> | ||||||
| <motion.div | ||||||
| initial={{ opacity: 0, y: 20 }} | ||||||
|
|
@@ -327,7 +327,7 @@ export function ConversationalOnboarding() { | |||||
|
|
||||||
| if (phase === "loading") { | ||||||
| return ( | ||||||
| <div className="min-h-screen flex flex-col items-center justify-center bg-background p-6 text-foreground"> | ||||||
| <div className="fixed inset-0 z-50 flex flex-col items-center justify-center bg-background p-6 text-foreground"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This container is missing
Suggested change
|
||||||
| <motion.div | ||||||
| animate={{ scale: [1, 1.1, 1] }} | ||||||
| transition={{ repeat: Infinity, duration: 1.5 }} | ||||||
|
|
@@ -354,7 +354,7 @@ export function ConversationalOnboarding() { | |||||
|
|
||||||
| if (phase === "result" && result) { | ||||||
| return ( | ||||||
| <div className="flex min-h-screen flex-col items-center bg-background p-6 text-foreground"> | ||||||
| <div className="fixed inset-0 z-50 flex flex-col items-center overflow-y-auto bg-background p-6 text-foreground"> | ||||||
| <div className="w-full max-w-md space-y-6 py-10"> | ||||||
| {/* Persona emoji */} | ||||||
| <motion.div | ||||||
|
|
@@ -445,7 +445,7 @@ export function ConversationalOnboarding() { | |||||
| } | ||||||
|
|
||||||
| return ( | ||||||
| <div className="flex min-h-screen flex-col items-center justify-center bg-background p-6 text-foreground"> | ||||||
| <div className="fixed inset-0 z-50 flex flex-col items-center justify-center bg-background p-6 text-foreground"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main questions phase is missing
Suggested change
|
||||||
| <div className="w-full max-w-md space-y-8"> | ||||||
| {/* Progress dots */} | ||||||
| <div className="flex justify-center gap-2"> | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -218,40 +218,42 @@ export function GuestOnboardingFlow({ onComplete }: GuestOnboardingFlowProps) { | |||||
| // ── Render ── | ||||||
|
|
||||||
| return ( | ||||||
| <AnimatePresence mode="wait"> | ||||||
| {step === "questions" && ( | ||||||
| <motion.div key="questions" initial={{ opacity: 0, x: 50 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -50 }} | ||||||
| className="fixed inset-0 z-50 bg-[radial-gradient(circle_at_top_left,_rgba(124,92,255,0.16),_transparent_28rem),linear-gradient(180deg,_#fffefc,_#f7f2ff)]"> | ||||||
| <OnboardingChat onComplete={handleQuestionsComplete} /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
| <div className="fixed inset-0 z-50 bg-[radial-gradient(circle_at_top_left,_rgba(124,92,255,0.16),_transparent_28rem),linear-gradient(180deg,_#fffefc,_#f7f2ff)]"> | ||||||
| <AnimatePresence mode="wait"> | ||||||
| {step === "questions" && ( | ||||||
| <motion.div key="questions" initial={{ x: 40, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: -40, opacity: 0 }} | ||||||
| className="absolute inset-0"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
| <OnboardingChat onComplete={handleQuestionsComplete} /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
|
|
||||||
| {step === "analyzing" && ( | ||||||
| <motion.div key="analyzing" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} | ||||||
| className="fixed inset-0 z-50 flex items-center justify-center bg-[radial-gradient(circle_at_top_left,_rgba(124,92,255,0.16),_transparent_28rem),linear-gradient(180deg,_#fffefc,_#f7f2ff)]"> | ||||||
| <AnalyzingAnimation /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
| {step === "analyzing" && ( | ||||||
| <motion.div key="analyzing" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} | ||||||
| className="absolute inset-0 flex items-center justify-center"> | ||||||
| <AnalyzingAnimation /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
|
|
||||||
| {step === "statement" && ( | ||||||
| <motion.div key="statement" initial={{ opacity: 0, x: 50 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -50 }} | ||||||
| className="fixed inset-0 z-50 bg-[radial-gradient(circle_at_top_left,_rgba(124,92,255,0.16),_transparent_28rem),linear-gradient(180deg,_#fffefc,_#f7f2ff)]"> | ||||||
| <BankStatementUpload onComplete={handleStatementComplete} onSkip={handleStatementSkip} /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
|
|
||||||
| {step === "roast" && persona && ( | ||||||
| <motion.div key="roast" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, y: 50 }} | ||||||
| className="fixed inset-0 z-50 bg-[radial-gradient(circle_at_top_left,_rgba(124,92,255,0.16),_transparent_28rem),linear-gradient(180deg,_#fffefc,_#f7f2ff)]"> | ||||||
| <PersonaSummary | ||||||
| persona={persona} | ||||||
| estimatedBudget={budget} | ||||||
| xpEarned={totalXP} | ||||||
| hasRealData={!!scannedTxns.length} | ||||||
| /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
| </AnimatePresence> | ||||||
| {step === "statement" && ( | ||||||
| <motion.div key="statement" initial={{ x: 40, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: -40, opacity: 0 }} | ||||||
| className="absolute inset-0"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| <BankStatementUpload onComplete={handleStatementComplete} onSkip={handleStatementSkip} /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
|
|
||||||
| {step === "roast" && persona && ( | ||||||
| <motion.div key="roast" initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, y: 50 }} | ||||||
| className="absolute inset-0"> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||
| <PersonaSummary | ||||||
| persona={persona} | ||||||
| estimatedBudget={budget} | ||||||
| xpEarned={totalXP} | ||||||
| hasRealData={!!scannedTxns.length} | ||||||
| /> | ||||||
| </motion.div> | ||||||
| )} | ||||||
| </AnimatePresence> | ||||||
| </div> | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
justify-centeron a container withoverflow-y-autocan cause the top of the content to be clipped and unreachable if the content height exceeds the viewport. It is safer to removejustify-centerand rely on padding or a wrapper to ensure the content remains accessible on smaller screens, similar to the implementation in theresultphase (line 357).