From 84004741ae076ed82d9b7d2a77c3ccabb46e5527 Mon Sep 17 00:00:00 2001 From: Aditya30ag Date: Wed, 9 Jul 2025 11:45:23 +0530 Subject: [PATCH 1/2] Fix: Add logout and navigation improvements to onboarding flow - Add logout button with confirmation dialog in onboarding header - Implement 'Skip for now' option to allow completing onboarding later - Add clickable step navigation to go back to previous steps - Implement auto-save functionality for onboarding progress - Add error handling and loading states for logout process - Enhance UI with better progress indicators and navigation - Fix missing exit options during onboarding flow Fixes #93 --- Frontend/src/components/Onboarding.tsx | 277 +++++++++++++++++++++++-- 1 file changed, 260 insertions(+), 17 deletions(-) diff --git a/Frontend/src/components/Onboarding.tsx b/Frontend/src/components/Onboarding.tsx index 950b09e..c04a0eb 100644 --- a/Frontend/src/components/Onboarding.tsx +++ b/Frontend/src/components/Onboarding.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; -import { Info } from "lucide-react"; +import { Info, LogOut, X, ArrowLeft, AlertCircle } from "lucide-react"; import { supabase } from "../utils/supabase"; const platforms = [ @@ -65,7 +65,7 @@ const brandInitialState: BrandData = { export default function Onboarding() { const navigate = useNavigate(); - const { user } = useAuth(); + const { user, logout } = useAuth(); const [step, setStep] = useState(0); const [role, setRole] = useState(""); const [personal, setPersonal] = useState({ name: "", email: "", age: "", gender: "", country: "", category: "", otherCategory: "" }); @@ -85,6 +85,12 @@ export default function Onboarding() { const [brandData, setBrandData] = useState(brandInitialState); const [brandLogoPreview, setBrandLogoPreview] = useState(null); const [brandError, setBrandError] = useState(""); + + // New state for navigation improvements + const [showExitDialog, setShowExitDialog] = useState(false); + const [exitAction, setExitAction] = useState<'logout' | 'skip' | null>(null); + const [exitLoading, setExitLoading] = useState(false); + const [exitError, setExitError] = useState(""); // Prefill name and email from Google user if available useEffect(() => { @@ -628,6 +634,101 @@ export default function Onboarding() { if (step > 0) setStep(step - 1); }; + // New handler functions for navigation improvements + const handleExitRequest = (action: 'logout' | 'skip') => { + setExitAction(action); + setShowExitDialog(true); + }; + + const handleExitConfirm = async () => { + setExitLoading(true); + setExitError(""); + + try { + + + if (exitAction === 'logout') { + await logout(); + // Note: logout() should handle navigation, so we don't close dialog here + // The AuthContext logout function should redirect to home page + } else if (exitAction === 'skip') { + console.log("Skipping onboarding..."); + // Save current progress to localStorage for later + const progressData = { + step, + role, + personal, + selectedPlatforms, + platformDetails, + pricing, + profilePic: null, // Can't save file to localStorage + brandStep, + brandData: { ...brandData, logo: null }, // Can't save file to localStorage + timestamp: Date.now() + }; + localStorage.setItem('onboarding_progress', JSON.stringify(progressData)); + + // Navigate to appropriate dashboard + if (role === "brand") { + navigate('/brand/dashboard'); + } else { + navigate('/dashboard'); + } + + // Close dialog after navigation + setShowExitDialog(false); + setExitAction(null); + } + } catch (error) { + console.error("Error in handleExitConfirm:", error); + setExitError(`Failed to ${exitAction}. Please try again.`); + } finally { + setExitLoading(false); + } + }; + + const handleExitCancel = () => { + setShowExitDialog(false); + setExitAction(null); + setExitLoading(false); + setExitError(""); + }; + + const handleStepClick = (targetStep: number) => { + // Only allow going back to previously completed steps + const currentStep = role === "brand" ? brandStep : step; + if (targetStep < currentStep) { + if (role === "brand") { + setBrandStep(targetStep); + } else { + setStep(targetStep); + } + } + }; + + // Load saved progress on component mount + useEffect(() => { + const savedProgress = localStorage.getItem('onboarding_progress'); + if (savedProgress) { + try { + const data = JSON.parse(savedProgress); + // Only load if it's recent (within 24 hours) + if (Date.now() - data.timestamp < 24 * 60 * 60 * 1000) { + setStep(data.step || 0); + setRole(data.role || ""); + setPersonal(data.personal || { name: "", email: "", age: "", gender: "", country: "", category: "", otherCategory: "" }); + setSelectedPlatforms(data.selectedPlatforms || []); + setPlatformDetails(data.platformDetails || {}); + setPricing(data.pricing || {}); + setBrandStep(data.brandStep || 0); + setBrandData(data.brandData || brandInitialState); + } + } catch (error) { + console.error("Error loading saved progress:", error); + } + } + }, []); + // Brand onboarding steps const brandSteps = [ "Brand Details", @@ -1065,17 +1166,90 @@ export default function Onboarding() { return (
-
- {/* Stepper UI */} +
+ {/* Header with Logout and Skip Options */} +
+
+

InPact Onboarding

+
+
+ {role && ( + + )} + +
+
+ + {/* Enhanced Stepper UI with clickable progress */}
{role === "brand" - ? brandSteps.map((label, idx) => ( -
{label}
- )) - : steps.map((label, idx) => ( -
{label}
- ))} + ? brandSteps.map((label, idx) => { + const isActive = idx === brandStep; + const isCompleted = idx < brandStep; + const isClickable = idx < brandStep; + return ( +
isClickable && handleStepClick(idx)} + > +
+ {isCompleted ? '✓' : idx + 1} +
+
{label}
+
+ ); + }) + : steps.map((label, idx) => { + const isActive = idx === step; + const isCompleted = idx < step; + const isClickable = idx < step; + return ( +
isClickable && handleStepClick(idx)} + > +
+ {isCompleted ? '✓' : idx + 1} +
+
{label}
+
+ ); + })}
+ {/* Step Content */}
{role === "brand" ? ( @@ -1099,22 +1273,27 @@ export default function Onboarding() { )}
- {/* Navigation */} -
+ + {/* Enhanced Navigation */} +
{role === "brand" ? ( <> +
+ Step {brandStep + 1} of {brandSteps.length} +
{brandStep < brandSteps.length - 1 ? ( @@ -1125,10 +1304,14 @@ export default function Onboarding() { +
+ Step {step + 1} of {steps.length} +
{step < steps.length - 1 ? ( @@ -1148,7 +1331,7 @@ export default function Onboarding() { @@ -1158,6 +1341,66 @@ export default function Onboarding() {
{brandError &&
{brandError}
}
+ + {/* Exit Confirmation Dialog */} + {showExitDialog && ( +
+
+
+ +

+ {exitAction === 'logout' ? 'Logout Confirmation' : 'Skip Onboarding'} +

+
+

+ {exitAction === 'logout' + ? 'Are you sure you want to logout? Your onboarding progress will be saved and you can continue later.' + : 'Are you sure you want to skip the onboarding? You can complete it later from your dashboard, but some features may be limited until then.'} +

+ + {/* Error message */} + {exitError && ( +
+ {exitError} +
+ )} + + {/* Loading indicator */} + {exitLoading && ( +
+
+ + {exitAction === 'logout' ? 'Logging out...' : 'Saving progress...'} + +
+ )} + +
+ + +
+
+
+ )}
); } From 6df3a3804e01514cce160379b3af824b124c4381 Mon Sep 17 00:00:00 2001 From: Aditya30ag Date: Wed, 16 Jul 2025 15:16:58 +0530 Subject: [PATCH 2/2] smart skip functionality that prevents users from bypassing --- Frontend/src/components/Onboarding.tsx | 77 +++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/Frontend/src/components/Onboarding.tsx b/Frontend/src/components/Onboarding.tsx index c04a0eb..a676173 100644 --- a/Frontend/src/components/Onboarding.tsx +++ b/Frontend/src/components/Onboarding.tsx @@ -21,6 +21,12 @@ const steps = [ "Review & Submit", ]; +// Define which steps are essential for matching +const essentialSteps = { + creator: [0, 1, 2, 3], // Role, Personal Details, Platform Selection, Platform Details + brand: [0, 1, 2] // Brand Details, Contact Information, Platforms +}; + // const YOUTUBE_API_KEY = import.meta.env.VITE_YOUTUBE_API_KEY; // No longer needed in frontend type BrandData = { @@ -645,14 +651,52 @@ export default function Onboarding() { setExitError(""); try { - - if (exitAction === 'logout') { await logout(); // Note: logout() should handle navigation, so we don't close dialog here // The AuthContext logout function should redirect to home page } else if (exitAction === 'skip') { - console.log("Skipping onboarding..."); + console.log("Skipping non-essential onboarding fields..."); + + // Only allow skipping if essential fields are completed + if (role === "creator") { + // For creators, essential fields are: role, personal details, platform selection, platform details, pricing + const essentialFieldsComplete = role && + personal.name && personal.email && personal.age && personal.gender && + personal.category && personal.country && + selectedPlatforms.length > 0; + + if (!essentialFieldsComplete) { + setExitError("Please complete the essential fields (Personal Details, Platform Selection, and Platform Details) before skipping. These are required for creator matching."); + return; + } + + // Allow skipping pricing and profile picture (non-essential for basic matching) + if (step < 4) { // Before pricing step + setExitError("Please complete platform details before skipping. This information is essential for creator matching."); + return; + } + + } else if (role === "brand") { + // For brands, essential fields are: brand details, contact info, platforms + const essentialFieldsComplete = brandData.brand_name && + brandData.website_url && brandData.industry && brandData.company_size && + brandData.location && brandData.description && + brandData.contact_person && brandData.contact_email && + brandData.platforms.length > 0; + + if (!essentialFieldsComplete) { + setExitError("Please complete the essential fields (Brand Details, Contact Information, and Platform Selection) before skipping. These are required for brand matching."); + return; + } + + // Allow skipping social links and collaboration preferences (non-essential for basic matching) + if (brandStep < 2) { // Before social links step + setExitError("Please complete platform selection before skipping. This information is essential for brand matching."); + return; + } + } + // Save current progress to localStorage for later const progressData = { step, @@ -664,7 +708,8 @@ export default function Onboarding() { profilePic: null, // Can't save file to localStorage brandStep, brandData: { ...brandData, logo: null }, // Can't save file to localStorage - timestamp: Date.now() + timestamp: Date.now(), + skipped: true // Mark as skipped for later completion }; localStorage.setItem('onboarding_progress', JSON.stringify(progressData)); @@ -675,7 +720,7 @@ export default function Onboarding() { navigate('/dashboard'); } - // Close dialog after navigation + // Close dialog setShowExitDialog(false); setExitAction(null); } @@ -1177,8 +1222,9 @@ export default function Onboarding() { )}
+ }`}> + {label} + {isEssential && *} +
); }) @@ -1225,6 +1275,7 @@ export default function Onboarding() { const isActive = idx === step; const isCompleted = idx < step; const isClickable = idx < step; + const isEssential = essentialSteps.creator.includes(idx); return (
{label}
+ }`}> + {label} + {isEssential && *} + ); })} + + {/* Legend for essential fields */} +
+ * Essential fields required for matching +
{/* Step Content */}
@@ -1355,7 +1414,7 @@ export default function Onboarding() {

{exitAction === 'logout' ? 'Are you sure you want to logout? Your onboarding progress will be saved and you can continue later.' - : 'Are you sure you want to skip the onboarding? You can complete it later from your dashboard, but some features may be limited until then.'} + : 'Are you sure you want to skip the remaining non-essential fields? Essential fields for matching must be completed first. You can complete the remaining fields later from your dashboard.'}

{/* Error message */}