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 ImageCarousel = () => {
+const ImageCarousel = React.memo(() => {
const [currentImageIndex, setCurrentImageIndex] = useState(0);
const goToNextImage = () => {
@@ -285,7 +333,7 @@ const ImageCarousel = () => {
>
{
);
+});
+
+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
+ }}
+ >
+
+
+ ))}
+
+
+
+ );
+});
+
+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.
+
+
+
+ }
+ sx={{
+ py: 1.5,
+ px: 4,
+ borderRadius: '30px',
+ backgroundColor: theme.palette.secondary.main,
+ color: 'black',
+ fontWeight: 600,
+ '&:hover': {
+ backgroundColor: theme.palette.secondary.dark,
+ transform: 'translateY(-3px)',
+ boxShadow: '0 6px 20px rgba(0,0,0,0.2)',
+ },
+ transition: 'all 0.3s ease',
+ }}
+ >
+ Start Planning
+
+
+
+
+
+ {/* 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) => (
+
+
+
+ ))}
+
+
+
+
+ ), []);
+
+ // 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 && (
-
+ }>
+
+
)}
{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);