marquee
3009
package-lock.json
generated
@ -4,7 +4,7 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
@ -17,6 +17,8 @@
|
||||
"@mui/styled-engine-sc": "^7.3.5",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"axios": "^1.13.2",
|
||||
"framer-motion": "^12.23.24",
|
||||
"lightswind": "^3.1.15",
|
||||
"lucide-react": "^0.553.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
||||
42
src/App.css
@ -1,42 +0,0 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
41
src/App.jsx
@ -1,39 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
import reactLogo from './assets/react.svg'
|
||||
import viteLogo from '/vite.svg'
|
||||
import './App.css'
|
||||
|
||||
import "./App.css";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
import AppRoutes from "./routes/AppRoutes";
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<a href="https://vite.dev" target="_blank">
|
||||
<img src={viteLogo} className="logo" alt="Vite logo" />
|
||||
</a>
|
||||
<a href="https://react.dev" target="_blank">
|
||||
<img src={reactLogo} className="logo react" alt="React logo" />
|
||||
</a>
|
||||
</div>
|
||||
<h1>Vite + React</h1>
|
||||
<div className="card">
|
||||
<button onClick={() => setCount((count) => count + 1)}>
|
||||
count is {count}
|
||||
</button>
|
||||
<p>
|
||||
Edit <code>src/App.jsx</code> and save to test HMR
|
||||
</p>
|
||||
</div>
|
||||
<p className="read-the-docs">
|
||||
Click on the Vite and React logos to learn more
|
||||
</p>
|
||||
|
||||
<h1 class="text-3xl font-bold underline">
|
||||
Hello world!
|
||||
</h1>
|
||||
<Router>
|
||||
<AppRoutes />
|
||||
</Router>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
||||
BIN
src/assets/images/appstore.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/images/logo.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
57
src/assets/images/match-hour.svg
Normal file
|
After Width: | Height: | Size: 753 KiB |
BIN
src/assets/images/mobile.png
Normal file
|
After Width: | Height: | Size: 323 KiB |
BIN
src/assets/images/playstore.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
src/assets/images/scanner.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
77
src/assets/images/video-profile.svg
Normal file
|
After Width: | Height: | Size: 153 KiB |
73
src/assets/images/voice-call.svg
Normal file
|
After Width: | Height: | Size: 402 KiB |
BIN
src/assets/images/wedding1.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/images/wedding10.jpg
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
src/assets/images/wedding11.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
src/assets/images/wedding12.avif
Normal file
BIN
src/assets/images/wedding2.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
src/assets/images/wedding3.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
src/assets/images/wedding4.webp
Normal file
|
After Width: | Height: | Size: 862 KiB |
BIN
src/assets/images/wedding5.webp
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
src/assets/images/wedding6.jpeg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
src/assets/images/wedding7.jpg
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
src/assets/images/wedding8.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
src/assets/images/wedding9.avif
Normal file
203
src/components/common/LandingHeader.jsx
Normal file
@ -0,0 +1,203 @@
|
||||
import { useState } from "react";
|
||||
import * as React from "react";
|
||||
import AppBar from "@mui/material/AppBar";
|
||||
import Box from "@mui/material/Box";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import SwipeableDrawer from "@mui/material/SwipeableDrawer";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import Container from "@mui/material/Container";
|
||||
import Button from "@mui/material/Button";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import CloseIcon from "@mui/icons-material/Close"; // <-- Added
|
||||
import Divider from "@mui/material/Divider";
|
||||
import AdbIcon from "@mui/icons-material/Adb";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import { InboxIcon, MailIcon } from "lucide-react";
|
||||
|
||||
const pages = ["Matches", "Search", "Help"];
|
||||
const settings = ["Profile", "Account", "Dashboard", "Logout"];
|
||||
|
||||
const LandingHeader = () => {
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [anchorElUser, setAnchorElUser] = React.useState(null);
|
||||
|
||||
const toggleDrawer = (open) => () => {
|
||||
setDrawerOpen(open);
|
||||
};
|
||||
|
||||
const handleOpenUserMenu = (event) => {
|
||||
setAnchorElUser(event.currentTarget);
|
||||
};
|
||||
const handleCloseUserMenu = () => {
|
||||
setAnchorElUser(null);
|
||||
};
|
||||
|
||||
const DrawerList = (
|
||||
<Box
|
||||
sx={{ width: 250 }}
|
||||
role="presentation"
|
||||
onKeyDown={toggleDrawer(false)}
|
||||
>
|
||||
{/* Drawer Header + Close Button */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
px: 2,
|
||||
py: 1.5,
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">Menu</Typography>
|
||||
|
||||
{/* Close Button */}
|
||||
<IconButton onClick={toggleDrawer(false)}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<List>
|
||||
{["Matches", "Search", "Chat", "Mail"].map((text, index) => (
|
||||
<ListItem key={text} disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemIcon>
|
||||
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={text} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
|
||||
{/* <Divider />
|
||||
|
||||
<List>
|
||||
{["All mail", "Trash", "Spam"].map((text, index) => (
|
||||
<ListItem key={text} disablePadding>
|
||||
<ListItemButton>
|
||||
<ListItemIcon>
|
||||
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={text} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List> */}
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBar position="sticky">
|
||||
<Container maxWidth="xl">
|
||||
<Toolbar disableGutters>
|
||||
{/* Desktop Logo */}
|
||||
<AdbIcon sx={{ display: { xs: "none", md: "flex" }, mr: 1 }} />
|
||||
<Typography
|
||||
variant="h6"
|
||||
noWrap
|
||||
component="a"
|
||||
href="#"
|
||||
sx={{
|
||||
mr: 2,
|
||||
display: { xs: "none", md: "flex" },
|
||||
fontFamily: "monospace",
|
||||
fontWeight: 700,
|
||||
letterSpacing: ".3rem",
|
||||
color: "inherit",
|
||||
textDecoration: "none",
|
||||
position:"sticky",
|
||||
zIndex:"99"
|
||||
}}
|
||||
>
|
||||
LOGO
|
||||
</Typography>
|
||||
|
||||
{/* Mobile Drawer Button */}
|
||||
<Box sx={{ flexGrow: 1, display: { xs: "flex", md: "none" } }}>
|
||||
<IconButton size="large" color="inherit" onClick={toggleDrawer(true)}>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
{/* Mobile Logo */}
|
||||
<AdbIcon sx={{ display: { xs: "flex", md: "none" }, mr: 1 }} />
|
||||
<Typography
|
||||
variant="h5"
|
||||
noWrap
|
||||
component="a"
|
||||
href="#"
|
||||
sx={{
|
||||
mr: 2,
|
||||
display: { xs: "flex", md: "none" },
|
||||
flexGrow: 1,
|
||||
fontFamily: "monospace",
|
||||
fontWeight: 700,
|
||||
letterSpacing: ".3rem",
|
||||
color: "inherit",
|
||||
textDecoration: "none",
|
||||
}}
|
||||
>
|
||||
LOGO
|
||||
</Typography>
|
||||
|
||||
{/* Desktop Menu */}
|
||||
<Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
|
||||
{pages.map((page) => (
|
||||
<Button key={page} sx={{ my: 2, color: "white", display: "block" }}>
|
||||
{page}
|
||||
</Button>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* Avatar Menu */}
|
||||
<Box sx={{ flexGrow: 0 }}>
|
||||
<Tooltip title="Open settings">
|
||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||
<Avatar alt="User" src="/static/images/avatar/2.jpg" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
sx={{ mt: "45px" }}
|
||||
anchorEl={anchorElUser}
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
anchorOrigin={{ vertical: "top", horizontal: "right" }}
|
||||
transformOrigin={{ vertical: "top", horizontal: "right" }}
|
||||
>
|
||||
{settings.map((setting) => (
|
||||
<MenuItem key={setting} onClick={handleCloseUserMenu}>
|
||||
<Typography textAlign="center">{setting}</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
|
||||
{/* Drawer */}
|
||||
<SwipeableDrawer
|
||||
anchor="left"
|
||||
open={drawerOpen}
|
||||
onClose={toggleDrawer(false)}
|
||||
onOpen={toggleDrawer(true)}
|
||||
>
|
||||
{DrawerList}
|
||||
</SwipeableDrawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LandingHeader;
|
||||
341
src/components/landing/AppPromoteSection.jsx
Normal file
@ -0,0 +1,341 @@
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Users, Shield, Lock, Download, Smartphone, QrCode } from 'lucide-react';
|
||||
|
||||
const AppPromoteSection = () => {
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(true);
|
||||
}, []);
|
||||
|
||||
const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 60 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.6, ease: "easeOut" }
|
||||
}
|
||||
};
|
||||
|
||||
const staggerContainer = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const scaleIn = {
|
||||
hidden: { opacity: 0, scale: 0.8 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: { duration: 0.5 }
|
||||
}
|
||||
};
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: <Users className="w-8 h-8 text-pink-500" />,
|
||||
title: "100% Screened Profiles",
|
||||
description: "Search by location, community, profession & more from lakhs of active profiles",
|
||||
color: "bg-pink-50 dark:bg-pink-900/20"
|
||||
},
|
||||
{
|
||||
icon: <Shield className="w-8 h-8 text-blue-500" />,
|
||||
title: "Verifications by Personal Visit",
|
||||
description: "Special listing for profiles verified by our agents through personal visits",
|
||||
color: "bg-blue-50 dark:bg-blue-900/20"
|
||||
},
|
||||
{
|
||||
icon: <Lock className="w-8 h-8 text-purple-500" />,
|
||||
title: "Control over Privacy",
|
||||
description: "Restrict unwanted access to contact details & photos/videos",
|
||||
color: "bg-purple-50 dark:bg-purple-900/20"
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-pink-50 dark:from-gray-900 dark:to-pink-900/20">
|
||||
{/* Hero Section */}
|
||||
<section className="relative min-h-screen flex items-center justify-center overflow-hidden">
|
||||
{/* Background Image with Overlay */}
|
||||
<div
|
||||
className="absolute inset-0 z-0"
|
||||
style={{
|
||||
backgroundImage: 'url(https://images.unsplash.com/photo-1519741497674-611481863552?w=1200)',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/70 to-black/50"></div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate={isVisible ? "visible" : "hidden"}
|
||||
variants={fadeInUp}
|
||||
className="text-center"
|
||||
>
|
||||
<motion.p
|
||||
variants={fadeInUp}
|
||||
className="text-pink-300 text-sm font-medium tracking-wider uppercase mb-4"
|
||||
>
|
||||
MORE THAN 25 YEARS OF
|
||||
</motion.p>
|
||||
<motion.h1
|
||||
variants={fadeInUp}
|
||||
className="text-5xl md:text-7xl font-bold text-white mb-8"
|
||||
>
|
||||
Bringing People <span className="text-pink-400">Together</span>
|
||||
</motion.h1>
|
||||
</motion.div>
|
||||
|
||||
{/* Features Grid */}
|
||||
<motion.div
|
||||
variants={staggerContainer}
|
||||
initial="hidden"
|
||||
animate={isVisible ? "visible" : "hidden"}
|
||||
className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-16 max-w-6xl mx-auto"
|
||||
>
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
variants={scaleIn}
|
||||
whileHover={{ scale: 1.05, y: -5 }}
|
||||
className="bg-white dark:bg-gray-800 rounded-2xl p-8 shadow-xl hover:shadow-2xl transition-all duration-300"
|
||||
>
|
||||
<motion.div
|
||||
className={`${feature.color} w-16 h-16 rounded-full flex items-center justify-center mb-6`}
|
||||
whileHover={{ rotate: 360 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
{feature.icon}
|
||||
</motion.div>
|
||||
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-3">
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* App Download Section */}
|
||||
<section className="relative py-20 bg-gradient-to-br from-pink-50 to-rose-100 dark:from-pink-900/10 dark:to-rose-900/20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left Content */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<h2 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Download the Jeevansathi app
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8">
|
||||
Connect with your matches anytime, anywhere
|
||||
</p>
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 rounded-2xl p-8 shadow-xl">
|
||||
<p className="text-center text-gray-700 dark:text-gray-300 mb-6 font-medium">
|
||||
Point your phone camera at the QR code or use<br />one of the download links below
|
||||
</p>
|
||||
|
||||
{/* QR Code */}
|
||||
<div className="flex justify-center mb-6">
|
||||
<motion.div
|
||||
className="bg-white p-4 rounded-xl shadow-lg"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
>
|
||||
<div className="w-32 h-32 bg-gray-900 rounded-lg flex items-center justify-center">
|
||||
<QrCode className="w-24 h-24 text-white" />
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Download Buttons */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="bg-black text-white px-6 py-3 rounded-lg flex items-center justify-center space-x-2 hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
<Smartphone className="w-5 h-5" />
|
||||
<div className="text-left">
|
||||
<div className="text-xs">GET IT ON</div>
|
||||
<div className="font-bold">Google Play</div>
|
||||
</div>
|
||||
</motion.button>
|
||||
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="bg-black text-white px-6 py-3 rounded-lg flex items-center justify-center space-x-2 hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
<Download className="w-5 h-5" />
|
||||
<div className="text-left">
|
||||
<div className="text-xs">Download on the</div>
|
||||
<div className="font-bold">App Store</div>
|
||||
</div>
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
<p className="text-center mt-6">
|
||||
<span className="text-pink-600 dark:text-pink-400 font-medium">
|
||||
Or get a download link
|
||||
</span>
|
||||
<span className="text-gray-600 dark:text-gray-400"> on your SMS/Email</span>
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Right - Phone Mockup */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="relative"
|
||||
>
|
||||
<div className="relative">
|
||||
{/* Phone Frame */}
|
||||
<motion.div
|
||||
className="relative mx-auto w-full max-w-sm"
|
||||
animate={{ y: [0, -10, 0] }}
|
||||
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
|
||||
>
|
||||
<div className="bg-gray-900 rounded-[3rem] p-4 shadow-2xl">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-[2.5rem] overflow-hidden">
|
||||
{/* Phone Screen Content */}
|
||||
<div className="aspect-[9/19] bg-gradient-to-br from-pink-100 to-purple-100 dark:from-pink-900/20 dark:to-purple-900/20 p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="font-bold text-gray-900 dark:text-white">My Matches</h3>
|
||||
<div className="flex space-x-2">
|
||||
<div className="w-8 h-8 rounded-full bg-pink-400"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Profile Cards */}
|
||||
<div className="space-y-4">
|
||||
{[1, 2].map((i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: i * 0.2 }}
|
||||
className="bg-white dark:bg-gray-700 rounded-2xl p-4 shadow-lg"
|
||||
>
|
||||
<div className="flex items-center space-x-3 mb-3">
|
||||
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-pink-400 to-purple-500"></div>
|
||||
<div className="flex-1">
|
||||
<h4 className="font-semibold text-gray-900 dark:text-white">Profile Name</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">25 yrs, 5'6"</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<button className="flex-1 bg-pink-500 text-white py-2 rounded-lg text-sm font-medium">
|
||||
Connect
|
||||
</button>
|
||||
<button className="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg">
|
||||
<span className="text-xl">💬</span>
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Features Tags */}
|
||||
<div className="mt-6 space-y-2">
|
||||
<motion.div
|
||||
className="bg-white dark:bg-gray-700 rounded-full px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 inline-block"
|
||||
animate={{ x: [0, 5, 0] }}
|
||||
transition={{ duration: 2, repeat: Infinity }}
|
||||
>
|
||||
✓ Easy Verification
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="bg-white dark:bg-gray-700 rounded-full px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 inline-block ml-2"
|
||||
animate={{ x: [0, -5, 0] }}
|
||||
transition={{ duration: 2, repeat: Infinity, delay: 0.3 }}
|
||||
>
|
||||
✓ Voice & Video Calls
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Download Badge */}
|
||||
<motion.div
|
||||
className="absolute -bottom-4 -right-4 bg-white dark:bg-gray-800 rounded-2xl px-6 py-3 shadow-xl"
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.5 }}
|
||||
>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">10M+ downloads</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats Section */}
|
||||
<motion.section
|
||||
className="py-16 bg-white dark:bg-gray-900"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
{[
|
||||
{ number: "25+", label: "Years of Trust" },
|
||||
{ number: "10M+", label: "Happy Users" },
|
||||
{ number: "100%", label: "Verified Profiles" },
|
||||
{ number: "24/7", label: "Support" }
|
||||
].map((stat, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
className="text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
>
|
||||
<motion.h3
|
||||
className="text-4xl md:text-5xl font-bold text-pink-500 mb-2"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
>
|
||||
{stat.number}
|
||||
</motion.h3>
|
||||
<p className="text-gray-600 dark:text-gray-400 font-medium">{stat.label}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
</div>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppPromoteSection
|
||||
98
src/components/ui/ThreeDMarquee.jsx
Normal file
@ -0,0 +1,98 @@
|
||||
import Button from "@mui/material/Button";
|
||||
import { motion } from "framer-motion";
|
||||
import React from "react";
|
||||
|
||||
const ThreeDMarquee = ({ images = [], className = "", cols = 4, onImageClick }) => {
|
||||
// Clone the image list twice
|
||||
const duplicatedImages = [...images, ...images];
|
||||
|
||||
const groupSize = Math.ceil(duplicatedImages.length / cols);
|
||||
const imageGroups = Array.from({ length: cols }, (_, index) =>
|
||||
duplicatedImages.slice(index * groupSize, (index + 1) * groupSize)
|
||||
);
|
||||
|
||||
const handleImageClick = (image, globalIndex) => {
|
||||
if (onImageClick) {
|
||||
onImageClick(image, globalIndex);
|
||||
} else if (image.href) {
|
||||
window.open(image.href, image.target || "_self");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`mx-auto block h-[600px] max-sm:h-[400px]
|
||||
overflow-hidden bg-[#034E08] relative ${className} max-w-[1400px] ` }
|
||||
|
||||
style={{background: "linear-gradient(rgba(255, 255, 255, 0) 0%, rgba(2, 77, 14, 1) 70%)"}}
|
||||
>
|
||||
<div className="absolute bottom-0 left-0 w-full h-[300px] " style={{background: "linear-gradient(rgba(255, 255, 255, 0) 0%, rgb(255, 255, 255) 70%)"}} />
|
||||
<div className="absolute top-0 left-0 w-full h-[200px] " style={{background: "linear-gradient(rgba(255, 255, 255, 0.88) 10%, rgba(255, 255, 255, 0) 70% )"}} />
|
||||
<div className="text-white rounded-[0px] md:rounded-[10px] p-[20px] absolute md:top-[40%] md:left-[30%] w-full md:max-w-[600px] h-[100%] md:h-[230px] z-9 backdrop-blur-[5px] bg-black/50">
|
||||
|
||||
<h2 className="text-center text-[45px] font-900 pb-2">Now, chat for free !</h2>
|
||||
<p className="text-center text-[22px] font-900 pb-4">Finding your perfect match just become easier</p>
|
||||
<div className="flex itemes-center justify-center ">
|
||||
<Button variant="contained" size="medium" sx={{width:"fit-content", padding:"10px 20px"}}>Regsiter Now</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex w-full h-full items-center justify-center"
|
||||
style={{
|
||||
transform: "rotateX(55deg) rotateY(0deg) rotateZ(45deg)",
|
||||
}}
|
||||
>
|
||||
<div className="w-full overflow-hidden scale-90 sm:scale-100">
|
||||
<div
|
||||
className={`relative grid h-full w-full origin-center
|
||||
grid-cols-4 sm:grid-cols-${cols} gap-4 transform
|
||||
`}
|
||||
>
|
||||
{imageGroups.map((imagesInGroup, idx) => (
|
||||
<motion.div
|
||||
key={`column-${idx}`}
|
||||
animate={{ y: idx % 2 === 0 ? 100 : -100 }}
|
||||
transition={{
|
||||
// duration: idx % 2 === 0 ? 20 : 15,
|
||||
duration: idx % 2 === 0 ? 5 : 4,
|
||||
ease: "linear",
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse",
|
||||
}}
|
||||
className="flex flex-col items-center gap-6 relative"
|
||||
>
|
||||
<div className="absolute left-0 top-0 h-full w-0.5 " />
|
||||
{imagesInGroup.map((image, imgIdx) => {
|
||||
const globalIndex = idx * groupSize + imgIdx;
|
||||
const isClickable = image.href || onImageClick;
|
||||
|
||||
return (
|
||||
<div key={`img-${imgIdx}`} className="relative">
|
||||
<div className="absolute top-0 left-0 w-full h-0.5 " />
|
||||
<div className="max-w-[300px] h-[290px] rounded-lg overflow-hidden bg-white">
|
||||
<motion.img
|
||||
whileHover={{ y: -10 }}
|
||||
transition={{ duration: 0.3, ease: "easeInOut" }}
|
||||
src={image.src}
|
||||
alt={image.alt}
|
||||
width={970}
|
||||
height={700}
|
||||
className={`aspect-[970/700] w-full h-full object-cover shadow-xl hover:shadow-2xl transition-shadow duration-300 ${
|
||||
isClickable ? "cursor-pointer" : ""
|
||||
}`}
|
||||
onClick={() => handleImageClick(image, globalIndex)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThreeDMarquee;
|
||||
@ -1,71 +1,3 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
14
src/layout/LandingLayout.jsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import LandingHeader from "../components/common/LandingHeader";
|
||||
const LandingLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<LandingHeader/>
|
||||
<div className="body-container" style={{ marginBottom:'90px' }}>
|
||||
<Outlet />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LandingLayout
|
||||
20
src/main.jsx
@ -1,10 +1,14 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
import "./index.css";
|
||||
import App from "./App.jsx";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import theme from "./theme";
|
||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||
<React.StrictMode>
|
||||
<ThemeProvider theme={theme}>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
</ThemeProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
72
src/pages/HomePage.jsx
Normal file
@ -0,0 +1,72 @@
|
||||
import AppPromoteSection from "../components/landing/AppPromoteSection";
|
||||
import ThreeDMarquee from "../components/ui/ThreeDMarquee";
|
||||
import wedding1 from "../assets/images/wedding1.jpg";
|
||||
import wedding2 from "../assets/images/wedding2.jpg";
|
||||
import wedding3 from "../assets/images/wedding3.jpg";
|
||||
import wedding4 from "../assets/images/wedding4.webp";
|
||||
import wedding5 from "../assets/images/wedding5.webp";
|
||||
import wedding6 from "../assets/images/wedding6.jpeg";
|
||||
import wedding7 from "../assets/images/wedding7.jpg";
|
||||
import wedding8 from "../assets/images/wedding8.jpg";
|
||||
import wedding9 from "../assets/images/wedding9.avif";
|
||||
import wedding10 from "../assets/images/wedding10.jpg";
|
||||
import wedding11 from "../assets/images/wedding11.jpg";
|
||||
import wedding12 from "../assets/images/wedding12.avif";
|
||||
|
||||
|
||||
const HomePage = () => {
|
||||
const images = [
|
||||
{
|
||||
src: wedding1,
|
||||
alt: "Wedding Image",
|
||||
},
|
||||
{
|
||||
src: wedding2,
|
||||
alt: "Couple",
|
||||
},
|
||||
{
|
||||
src:wedding3,
|
||||
alt: "Engagement",
|
||||
},
|
||||
{
|
||||
src: wedding4,
|
||||
alt: "Wedding Image",
|
||||
},
|
||||
{
|
||||
src: wedding5,
|
||||
alt: "Couple",
|
||||
},
|
||||
{
|
||||
src: wedding6,
|
||||
alt: "Engagement",
|
||||
},
|
||||
|
||||
{
|
||||
src: wedding7,
|
||||
alt: "Wedding Image",
|
||||
},
|
||||
{
|
||||
src: wedding8,
|
||||
alt: "Couple",
|
||||
},
|
||||
{
|
||||
src: wedding9,
|
||||
alt: "Engagement",
|
||||
},
|
||||
{
|
||||
src: wedding10,
|
||||
alt: "Engagement",
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<ThreeDMarquee images={images} cols = {4} />
|
||||
<AppPromoteSection/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomePage;
|
||||
10
src/pages/LandingPage.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
|
||||
const LandingPage = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LandingPage
|
||||
19
src/routes/AppRoutes.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import UserRoutes from './UserRoutes';
|
||||
import PublicRoutes from './PublicRoutes';
|
||||
|
||||
|
||||
const AppRoutes = () => {
|
||||
return (
|
||||
<>
|
||||
{/* Wrap UserRoutes inside AuthProvider separately */}
|
||||
<Routes>
|
||||
{UserRoutes()}
|
||||
{PublicRoutes()}
|
||||
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppRoutes;
|
||||
15
src/routes/PublicRoutes.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { Route } from "react-router-dom";
|
||||
import HomePage from "../pages/HomePage";
|
||||
import LandingLayout from "../layout/LandingLayout";
|
||||
const PublicRoutes = () => {
|
||||
return (
|
||||
<>
|
||||
<Route element={<LandingLayout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
</Route>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PublicRoutes
|
||||
13
src/routes/UserRoutes.jsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { Route, useNavigate } from "react-router-dom";
|
||||
const UserRoutes = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <Route element={<MasterLayout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
</Route> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserRoutes
|
||||
17
src/theme.js
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: "#034E08", // Indigo
|
||||
contrastText: "#ffffff",
|
||||
},
|
||||
secondary: {
|
||||
main: "#A70710", // Pink
|
||||
contrastText: "#ffffff",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
@ -1,7 +1,13 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from "path"
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss(),],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||