diff --git a/ai-trip-creator/src/components/auth/auth.css b/ai-trip-creator/src/components/auth/auth.css index 49e335c9..df158dd6 100644 --- a/ai-trip-creator/src/components/auth/auth.css +++ b/ai-trip-creator/src/components/auth/auth.css @@ -1,86 +1,108 @@ /* auth.css */ form { - display: flex; - flex-direction: column; - gap: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +form div { + width: 100%; + margin-bottom: 1rem; +} + +label { + display: block; + margin-bottom: 0.5rem; + color: #000; +} + +input { + width: 100%; + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; +} + +button { + background-color: #0070f3; + color: white; + border: none; + padding: 0.75rem; + border-radius: 4px; + transition: background-color 0.3s; +} + +button:hover { + background-color: #005bb5; +} + +p { + padding: 25px; + margin-top: 0; + color: #000; +} + +p a { + padding: 5px; + color: #0070f3; +} + +.splash-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100vh; + background-color: #f0f2f5; +} + +.splash-container h1 { + font-size: 2.5rem; + margin-bottom: 2rem; +} + +.modal { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1000; + background-color: white; + padding: 2rem; + border-radius: 16px; + width: 100%; + max-width: 400px; + animation: modalFadeIn 0.3s ease-in-out; +} + +@keyframes modalFadeIn { + from { + opacity: 0; + transform: translate(-50%, -48%); } - - form div { - width: 100%; - margin-bottom: 1rem; - } - - label { - display: block; - margin-bottom: 0.5rem; - color: #000; - } - - input { - width: 100%; - padding: 0.5rem; - border: 1px solid #ccc; - border-radius: 4px; - } - - button { - background-color: #0070f3; - color: white; - border: none; - padding: 0.75rem; - border-radius: 4px; - transition: background-color 0.3s; - } - - button:hover { - background-color: #005bb5; - } - - p { - padding: 25px; - margin-top: 0; - color: #000; - } - - p a { - padding: 5px; - color: #0070f3; - } - - .splash-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100vh; - background-color: #f0f2f5; - } - - .splash-container h1 { - font-size: 2.5rem; - margin-bottom: 2rem; - } - - .modal { - position: absolute; - top: 50%; - left: 50%; + to { + opacity: 1; transform: translate(-50%, -50%); - z-index: 1000; - background-color: white; - padding: 2rem; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - border-radius: 8px; - width: 20rem; } - - .overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - z-index: 500; +} + +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(4px); + z-index: 500; + animation: overlayFadeIn 0.3s ease-in-out; +} + +@keyframes overlayFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; } - \ No newline at end of file +} diff --git a/ai-trip-creator/src/components/auth/login.js b/ai-trip-creator/src/components/auth/login.js index 40dc2887..71aba1f1 100644 --- a/ai-trip-creator/src/components/auth/login.js +++ b/ai-trip-creator/src/components/auth/login.js @@ -86,17 +86,26 @@ const Login = ({ setIsLoggedIn, closeLogin, openSignup }) => { - {error && {error}} - {resetError && {resetError}} - {resetSuccess && {resetSuccess}} + + Welcome Back + + + {error && {error}} + {resetError && {resetError}} + {resetSuccess && {resetSuccess}} { fullWidth required autoComplete="email" + sx={{ '& .MuiOutlinedInput-root': { borderRadius: '8px' } }} /> { fullWidth required autoComplete="current-password" + sx={{ '& .MuiOutlinedInput-root': { borderRadius: '8px' } }} /> - - - + - - Don't have an account?{" "} - { - closeLogin(); - openSignup(); - }} - > - Signup - -
- Forgot your password?{" "} - - Reset Password - -
+ + + Don't have an account?{' '} + { + closeLogin(); + openSignup(); + }} + sx={{ + fontWeight: 600, + color: '#2563eb', + textDecoration: 'none', + '&:hover': { textDecoration: 'underline' } + }} + > + Sign up + + + + + Forgot password? + + +
); }; diff --git a/ai-trip-creator/src/components/auth/signup.js b/ai-trip-creator/src/components/auth/signup.js index 7a75dab1..154962e0 100644 --- a/ai-trip-creator/src/components/auth/signup.js +++ b/ai-trip-creator/src/components/auth/signup.js @@ -5,7 +5,8 @@ import { Typography, Link, Box, - InputLabel, + Alert, + CircularProgress, } from "@mui/material"; import { createUserWithEmailAndPassword, @@ -15,25 +16,38 @@ import { import { doc, setDoc } from "firebase/firestore"; import { auth, firestore } from "../../firebase/firebase-config"; import { useNavigate } from "react-router-dom"; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import dayjs from 'dayjs'; const Signup = ({ closeSignup, openLogin }) => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [name, setName] = useState(""); - const [birthday, setBirthday] = useState(""); + const [birthday, setBirthday] = useState(null); const [showSuccess, setShowSuccess] = useState(false); const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); const navigate = useNavigate(); const handleSignup = async (e) => { e.preventDefault(); setError(""); + setLoading(true); const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/; if (!password.match(passwordRegex)) { setError( - "Password must be at least 8 characters long and contain at least one number.", + "Password must be at least 8 characters long and contain at least one number." ); + setLoading(false); + return; + } + + if (!birthday) { + setError("Please select your birthday"); + setLoading(false); return; } @@ -41,7 +55,7 @@ const Signup = ({ closeSignup, openLogin }) => { const userCredential = await createUserWithEmailAndPassword( auth, email.trim(), - password, + password ); const user = userCredential.user; @@ -50,18 +64,21 @@ const Signup = ({ closeSignup, openLogin }) => { await setDoc(doc(firestore, "users", user.uid), { uid: user.uid, name: name.trim(), - birthday: birthday.trim(), + birthday: birthday.format('YYYY-MM-DD'), email: email.trim(), }); await sendEmailVerification(auth.currentUser); - + setLoading(false); setShowSuccess(true); } catch (error) { console.error("Signup failed:", error); - if (error.message === "Firebase: Error (auth/email-already-in-use).") - setError("Signup failed: Email already in use"); - else setError("Signup failed: " + error.message); + setError( + error.code === "auth/email-already-in-use" + ? "Email already in use" + : "Signup failed: " + error.message + ); + setLoading(false); } }; @@ -69,109 +86,287 @@ const Signup = ({ closeSignup, openLogin }) => { {!showSuccess ? ( <> - Name + + Create Account + + + {error && {error}} + setName(e.target.value)} required + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: '12px', + backgroundColor: 'rgba(255, 255, 255, 0.9)', + '&:hover': { + '& fieldset': { + borderColor: '#2563eb', + }, + }, + }, + '& .MuiOutlinedInput-notchedOutline': { + borderWidth: '1.5px', + }, + }} /> - Birthday - setBirthday(e.target.value)} - required - InputLabelProps={{ shrink: true }} - /> + + setBirthday(newValue)} + disableFuture + sx={{ + width: '100%', + '& .MuiOutlinedInput-root': { + borderRadius: '12px', + backgroundColor: 'rgba(255, 255, 255, 0.9)', + '&:hover': { + '& fieldset': { + borderColor: '#2563eb', + }, + }, + }, + '& .MuiOutlinedInput-notchedOutline': { + borderWidth: '1.5px', + }, + '& .MuiInputLabel-root': { + color: 'text.secondary', + }, + '& .MuiIconButton-root': { + color: '#2563eb', + padding: '8px', + position: 'absolute', + right: '8px', + '&:hover': { + backgroundColor: 'rgba(37, 99, 235, 0.1)', + }, + }, + '& .MuiInputAdornment-root': { + position: 'absolute', + right: 0, + top: '50%', + transform: 'translateY(-50%)', + marginLeft: 0, + marginRight: 0, + }, + }} + slotProps={{ + textField: { + required: true, + error: !birthday && error, + helperText: !birthday && error ? 'Birthday is required' : '', + }, + popper: { + sx: { + '& .MuiPaper-root': { + borderRadius: '12px', + boxShadow: '0 4px 20px rgba(0, 0, 0, 0.1)', + border: '1px solid rgba(0, 0, 0, 0.08)', + }, + '& .MuiPickersDay-root': { + borderRadius: '8px', + '&.Mui-selected': { + backgroundColor: '#2563eb', + color: 'white', + '&:hover': { + backgroundColor: '#1d4ed8', + }, + }, + '&:hover': { + backgroundColor: 'rgba(37, 99, 235, 0.1)', + }, + }, + '& .MuiPickersCalendarHeader-root': { + '& .MuiIconButton-root': { + borderRadius: '8px', + }, + }, + '& .MuiPickersYear-yearButton': { + borderRadius: '8px', + '&.Mui-selected': { + backgroundColor: '#2563eb', + color: 'white', + }, + }, + }, + }, + }} + /> + - Email setEmail(e.target.value)} required + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: '12px', + backgroundColor: 'rgba(255, 255, 255, 0.9)', + '&:hover': { + '& fieldset': { + borderColor: '#2563eb', + }, + }, + }, + '& .MuiOutlinedInput-notchedOutline': { + borderWidth: '1.5px', + }, + }} /> - Password setPassword(e.target.value)} required + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: '12px', + backgroundColor: 'rgba(255, 255, 255, 0.9)', + '&:hover': { + '& fieldset': { + borderColor: '#2563eb', + }, + }, + }, + '& .MuiOutlinedInput-notchedOutline': { + borderWidth: '1.5px', + }, + }} /> - {error && ( - - {error} - - )} - - Already have an account?{" "} - { - closeSignup(); - openLogin(); - }} - > - Login - - + + + Already have an account?{' '} + { + closeSignup(); + openLogin(); + }} + sx={{ + fontWeight: 600, + background: 'linear-gradient(135deg, #2563eb 0%, #3b82f6 100%)', + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + textDecoration: 'none', + '&:hover': { + textDecoration: 'underline', + } + }} + > + Sign in + + + ) : ( - - Signup successful! - + + + Signup Successful! + + A verification link has been sent to your email. )} diff --git a/ai-trip-creator/src/components/splash/splash.css b/ai-trip-creator/src/components/splash/splash.css index 0c210ee2..3935cdca 100644 --- a/ai-trip-creator/src/components/splash/splash.css +++ b/ai-trip-creator/src/components/splash/splash.css @@ -274,6 +274,14 @@ body, html { :root { --font-family: "Poppins", sans-serif; --font-family-body: "Roboto", sans-serif; + --primary-color: #1565c0; + --secondary-color: #ffb74d; + --accent-color: #5c6bc0; + --background-color: #f8f9fa; + --text-color: #333; + --heading1-color: #5A7BB8; + --modal-background: white; + --modal-shadow: rgba(0, 0, 0, 0.1); } body, @@ -304,7 +312,7 @@ h1 { } .get-started-button { - background-color: #1573d8; + background-color: var(--primary-color); color: white; font-size: 1.5rem; padding: 0.5rem 2rem; @@ -315,7 +323,7 @@ h1 { } .get-started-button:hover { - background-color: #0f3d82; + background-color: #0d47a1; } .modal { diff --git a/ai-trip-creator/src/components/splash/splash.js b/ai-trip-creator/src/components/splash/splash.js index ca8dbf61..1e3b8222 100644 --- a/ai-trip-creator/src/components/splash/splash.js +++ b/ai-trip-creator/src/components/splash/splash.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect, useCallback, useRef, useMemo, Suspense } from "react"; import { ThemeProvider } from "@mui/material/styles"; import { Button, @@ -13,6 +13,8 @@ import { Toolbar, IconButton, Slide, + Fade, + CircularProgress, } from "@mui/material"; import { useTheme, createTheme } from "@mui/material/styles"; import Login from "../auth/login"; @@ -25,6 +27,11 @@ import { import { AiOutlineStar, AiOutlineMenu } from "react-icons/ai"; import { Link } from "react-scroll"; import CssBaseline from "@mui/material/CssBaseline"; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import { keyframes } from '@mui/system'; +import TravelExploreIcon from '@mui/icons-material/TravelExplore'; +import FlightTakeoffIcon from '@mui/icons-material/FlightTakeoff'; +import { throttle } from 'lodash'; const theme = createTheme({ typography: { @@ -76,19 +83,49 @@ const reviews = [ ]; const images = [ - "https://images.unsplash.com/photo-1519985176271-adb1088fa94c", - "https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0", - "https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e", - "https://images.unsplash.com/photo-1521747116042-5a810fda9664", - "https://images.unsplash.com/photo-1507525428034-b723cf961d3e", - "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40", - "https://images.unsplash.com/photo-1487058792275-0ad4aaf24ca7", - "https://images.unsplash.com/photo-1494526585095-c41746248156", - "https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0", - "https://images.unsplash.com/photo-1472214103451-9374bd1c798e", + { + src: "https://images.unsplash.com/photo-1519985176271-adb1088fa94c?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1519985176271-adb1088fa94c?w=200&q=60&format=webp", + alt: "Travel destination 1" + }, + { + src: "https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0?w=200&q=60&format=webp", + alt: "Travel destination 2" + }, + { + src: "https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e?w=200&q=60&format=webp", + alt: "Travel destination 3" + }, + { + src: "https://images.unsplash.com/photo-1521747116042-5a810fda9664?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1521747116042-5a810fda9664?w=200&q=60&format=webp", + alt: "Travel destination 4" + }, + { + src: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=200&q=60&format=webp", + alt: "Travel destination 5" + }, + { + src: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1454165804606-c3d57bc86b40?w=200&q=60&format=webp", + alt: "Travel destination 6" + }, + { + src: "https://images.unsplash.com/photo-1494526585095-c41746248156?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1494526585095-c41746248156?w=200&q=60&format=webp", + alt: "Travel destination 7" + }, + { + src: "https://images.unsplash.com/photo-1472214103451-9374bd1c798e?w=1200&q=80&format=webp", + thumbnail: "https://images.unsplash.com/photo-1472214103451-9374bd1c798e?w=200&q=60&format=webp", + alt: "Travel destination 8" + }, ]; -const ReviewCard = () => { +const ReviewCard = React.memo(() => { const [currentReviewIndex, setCurrentReviewIndex] = useState(0); const goToNextReview = () => { @@ -105,6 +142,17 @@ const ReviewCard = () => { const currentReview = reviews[currentReviewIndex]; + // Add debounced navigation + const debouncedGoToNext = useCallback( + throttle(goToNextReview, 300), + [goToNextReview] + ); + + const debouncedGoToPrevious = useCallback( + throttle(goToPreviousReview, 300), + [goToPreviousReview] + ); + return ( {

); +}); + +const optimizedImages = images.map(url => ({ + original: url, + thumbnail: url + '?w=100', // Add query param for smaller thumbnail + loading: 'lazy' +})); + +const useIntersectionObserver = (ref, options) => { + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const observer = new IntersectionObserver(([entry]) => { + setIsVisible(entry.isIntersecting); + }, options); + + if (ref.current) { + observer.observe(ref.current); + } + + return () => { + if (ref.current) { + observer.unobserve(ref.current); + } + }; + }, [ref, options]); + + return isVisible; }; -const SplashPage = ({ setIsLoggedIn }) => { - const [visibleLogin, setVisibleLogin] = useState(false); - const [visibleSignup, setVisibleSignup] = useState(false); +const ScrollToTop = () => { + const [isVisible, setIsVisible] = useState(false); - const openLogin = () => setVisibleLogin(true); - const closeLogin = () => setVisibleLogin(false); - const openSignup = () => setVisibleSignup(true); - const closeSignup = () => setVisibleSignup(false); + useEffect(() => { + const toggleVisibility = () => { + if (window.pageYOffset > 300) { + setIsVisible(true); + } else { + setIsVisible(false); + } + }; + window.addEventListener("scroll", toggleVisibility); + return () => window.removeEventListener("scroll", toggleVisibility); + }, []); + + const scrollToTop = () => { + window.scrollTo({ + top: 0, + behavior: "smooth" + }); + }; return ( - - + - {/* Animated Heading */} - - - + + + ); +}; + +const float = keyframes` + 0% { transform: translateY(0px); } + 50% { transform: translateY(-20px); } + 100% { transform: translateY(0px); } +`; + +const fadeIn = keyframes` + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +`; + +const heroStyles = { + position: 'relative', + minHeight: '100vh', + display: 'flex', + alignItems: 'center', + overflow: 'hidden', + background: 'linear-gradient(135deg, #0A2647 0%, #144272 50%, #205295 100%)', + '&::before': { + content: '""', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundImage: 'url("data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%239C92AC" fill-opacity="0.05"%3E%3Cpath d="M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2V6h4V4H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")', + opacity: 0.1, + }, + willChange: 'transform', // Optimize animations +}; + +const featureCardStyles = { + backgroundColor: 'white', + borderRadius: '16px', + padding: '2rem', + height: '100%', + transition: 'all 0.3s ease', + boxShadow: '0 4px 20px rgba(0,0,0,0.1)', + '&:hover': { + transform: 'translateY(-10px)', + boxShadow: '0 8px 30px rgba(0,0,0,0.15)', + } +}; + +const appBarStyles = { + backgroundColor: 'rgba(255, 255, 255, 0.95)', + backdropFilter: 'blur(8px)', + boxShadow: '0 2px 10px rgba(0,0,0,0.1)', + transition: 'all 0.3s ease', + transform: 'translateZ(0)', // Force GPU acceleration + willChange: 'transform', +}; + +const navButtonStyles = { + mx: 2, + color: theme.palette.primary.main, + fontWeight: 600, + position: 'relative', + '&::after': { + content: '""', + position: 'absolute', + bottom: -2, + left: 0, + width: '0%', + height: '2px', + backgroundColor: theme.palette.primary.main, + transition: 'width 0.3s ease', + }, + '&:hover::after': { + width: '100%', + } +}; + +const improvedButtonStyles = { + ...navButtonStyles, + transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)', + willChange: 'transform', + '&:active': { + transform: 'scale(0.98)', + } +}; + +const GallerySection = React.memo(() => { + const sectionRef = useRef(null); + const [isVisible, setIsVisible] = useState(false); + const [currentImageIndex, setCurrentImageIndex] = useState(0); + const [isHovered, setIsHovered] = useState(false); + + // Optimize intersection observer + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + // Only trigger once when becoming visible + if (entry.isIntersecting && !isVisible) { + setIsVisible(true); + // Optionally disconnect observer after first visibility + observer.disconnect(); + } + }, + { + threshold: 0.1, + rootMargin: '50px' // Pre-load slightly before section is visible + } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, [isVisible]); + + // Memoize navigation functions + const goToNextImage = useCallback(() => { + setCurrentImageIndex(prev => + prev === images.length - 1 ? 0 : prev + 1 + ); + }, []); + + const goToPreviousImage = useCallback(() => { + setCurrentImageIndex(prev => + prev === 0 ? images.length - 1 : prev - 1 + ); + }, []); + + // Optimize auto-play with useCallback and proper cleanup + useEffect(() => { + if (isHovered) return; + + const timer = setInterval(goToNextImage, 5000); + return () => clearInterval(timer); + }, [isHovered, goToNextImage]); + + return ( + + + + Explore Destinations + + + setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {/* Main Image - Optimize image rendering */} + + + {/* Navigation Arrows - Only render when hovered */} + {isHovered && ( + - AI Trip Creator -
- + {[ + { onClick: goToPreviousImage, icon: '←' }, + { onClick: goToNextImage, icon: '→' }, + ].map((button, index) => ( + + + {button.icon} + + + ))} +
+ )} + + {/* Image Counter - Simplified and optimized */} + + {images.map((_, index) => ( + setCurrentImageIndex(index)} + sx={{ + width: 8, + height: 8, + borderRadius: '50%', + backgroundColor: index === currentImageIndex ? 'white' : 'rgba(255,255,255,0.5)', + cursor: 'pointer', + transition: 'all 0.3s ease', + }} + /> + ))} + - {/* Floating and Sticky Navigation Bar */} - + {images.slice(0, 5).map((image, index) => ( + setCurrentImageIndex(index)} + sx={{ + width: 100, + height: 60, + borderRadius: 2, + overflow: 'hidden', + cursor: 'pointer', + border: index === currentImageIndex ? `3px solid ${theme.palette.primary.main}` : 'none', + opacity: index === currentImageIndex ? 1 : 0.6, + transition: 'all 0.3s ease', + transform: 'translateZ(0)', // Force GPU acceleration + }} + > + {`Thumbnail + + ))} +
+ + + ); +}); + +const useScrollPosition = () => { + const [scrollPosition, setScrollPosition] = useState(0); + + useEffect(() => { + const handleScroll = throttle(() => { + setScrollPosition(window.pageYOffset); + }, 100); + + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + return scrollPosition; +}; + +const SplashPage = React.memo(({ setIsLoggedIn }) => { + const [visibleLogin, setVisibleLogin] = useState(false); + const [visibleSignup, setVisibleSignup] = useState(false); + const scrollPosition = useScrollPosition(); + + const openLogin = () => setVisibleLogin(true); + const closeLogin = () => setVisibleLogin(false); + const openSignup = () => setVisibleSignup(true); + const closeSignup = () => setVisibleSignup(false); + + // Memoize heavy calculations and callbacks + const memoizedHeroContent = useMemo(() => ( + // Add id="hero" here + + + {/* Left Content */} + + + + Your Journey Begins Here + + + + Let AI craft your perfect adventure while you focus on making memories. + + + + + + + + + {/* Feature Pills */} + + {['AI-Powered', 'Personalized', 'Time-Saving'].map((feature) => ( + + + + {feature} + + + ))} + + + + + {/* Right Content - Floating Elements */} + + {[ + { img: 'https://images.unsplash.com/photo-1469854523086-cc02fe5d8800', delay: 0 }, + { img: 'https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1', delay: 0.2 }, + { img: 'https://images.unsplash.com/photo-1530521954074-e64f6810b32d', delay: 0.4 }, + ].map((item, index) => ( + + Travel + + ))} + + + + + ), []); + + // Use lazy loading for sections + return ( + + + + {/* Critical content loads immediately */} + { offset={-70} duration={500} > - + { offset={-70} duration={500} > - + { offset={-70} duration={500} > - + - {/* Hero Section */} + {memoizedHeroContent} - - - {/* - Plan Your Perfect Trip - */} - - Plan Your Perfect Trip - - - Discover new destinations, create your itinerary, and explore with - ease. - - - - - + {/* Lazy load non-critical sections */} + }> + + + + How It Works + + + {[ + { + icon: , + title: "Discover", + description: "Find your ideal destinations with our AI-powered search" + }, + { + icon: , + title: "Plan", + description: "Create personalized itineraries with smart suggestions" + }, + { + icon: , + title: "Explore", + description: "Experience your journey with our interactive guides" + } + ].map((feature, index) => ( + + + + {feature.icon} + + + {feature.title} + + + {feature.description} + + + + ))} + + + + + + }> + + + + {/* Optimize dialogs with dynamic imports */} {visibleLogin && ( - - Login - - - - + }> + + Login + + + + + )} {visibleSignup && ( @@ -534,84 +1102,6 @@ const SplashPage = ({ setIsLoggedIn }) => { /> )} - {/* How It Works Section */} - - - - How It Works - - - - - - - Search - - - Find your ideal trip by searching our extensive database of - destinations. - - - - - - - - Plan - - - Organize your itinerary with our easy-to-use planning tools. - - - - - - - - Explore - - - Discover hidden gems and popular spots with expert - recommendations. - - - - - - - - {/* Gallery Section */} - - - - - Gallery - - - - - - {/* Reviews Section */} {/* Footer */} @@ -646,6 +1136,22 @@ const SplashPage = ({ setIsLoggedIn }) => { ); +}); + +// 6. Add performance monitoring +const withPerformanceTracking = (WrappedComponent) => { + return function WithPerformanceTracking(props) { + useEffect(() => { + const startTime = performance.now(); + + return () => { + const endTime = performance.now(); + console.log(`Component rendered in ${endTime - startTime}ms`); + }; + }, []); + + return ; + }; }; -export default SplashPage; +export default withPerformanceTracking(SplashPage);