profile layout
This commit is contained in:
parent
1573a267e6
commit
1494e1d979
292
src/components/common/ProfileHeader.jsx
Normal file
292
src/components/common/ProfileHeader.jsx
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
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 Tooltip from "@mui/material/Tooltip";
|
||||||
|
import MenuIcon from "@mui/icons-material/Menu";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import Divider from "@mui/material/Divider";
|
||||||
|
import { InboxIcon, MailIcon } from "lucide-react";
|
||||||
|
import LazyImage from "./LazyImage";
|
||||||
|
import Logo from "../../assets/images/logo.png";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import Drawer from "@mui/material/Drawer";
|
||||||
|
import { useState, useRef, useEffect } from "react";
|
||||||
|
import { useTheme, useMediaQuery, ListItemIcon } from "@mui/material";
|
||||||
|
import {Badge, } from "@mui/material";
|
||||||
|
import { Home, Users, Heart, MessageCircle, Search, Bell } from "lucide-react";
|
||||||
|
|
||||||
|
/* ----------------------------------------------------
|
||||||
|
SPARKLE NAVBAR (same as your original code)
|
||||||
|
---------------------------------------------------- */
|
||||||
|
// Sparkle Navbar Component
|
||||||
|
const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick }) => {
|
||||||
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
|
const [indicatorStyle, setIndicatorStyle] = useState({});
|
||||||
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
|
const navRef = useRef(null);
|
||||||
|
const buttonRefs = useRef([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateIndicator(activeIndex);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const updateIndicator = (index) => {
|
||||||
|
const button = buttonRefs.current[index];
|
||||||
|
if (button && navRef.current) {
|
||||||
|
const navRect = navRef.current.getBoundingClientRect();
|
||||||
|
const btnRect = button.getBoundingClientRect();
|
||||||
|
setIndicatorStyle({
|
||||||
|
left: btnRect.left - navRect.left + btnRect.width / 2 - 18,
|
||||||
|
opacity: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = (index) => {
|
||||||
|
if (index === activeIndex || isAnimating) return;
|
||||||
|
setIsAnimating(true);
|
||||||
|
const newButton = buttonRefs.current[index];
|
||||||
|
|
||||||
|
if (newButton && navRef.current) {
|
||||||
|
const navRect = navRef.current.getBoundingClientRect();
|
||||||
|
const newRect = newButton.getBoundingClientRect();
|
||||||
|
|
||||||
|
setIndicatorStyle(prev => ({
|
||||||
|
...prev,
|
||||||
|
left: newRect.left - navRect.left + newRect.width / 2 - 18,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setActiveIndex(index);
|
||||||
|
setIsAnimating(false);
|
||||||
|
if (onItemClick) onItemClick(items[index], index);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav ref={navRef} className="relative flex items-center gap-8 px-6 py-4">
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<button
|
||||||
|
key={item}
|
||||||
|
ref={el => buttonRefs.current[index] = el}
|
||||||
|
onClick={() => handleClick(index)}
|
||||||
|
className={`cursor-pointer relative uppercase text-sm font-medium transition-all duration-300 pb-2 ${
|
||||||
|
activeIndex === index ? 'text-black' : 'text-gray-900 hover:text-gray-600'
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
textShadow: activeIndex === index
|
||||||
|
? `0 0 10px ${color}, 0 0 20px ${color}, 0 0 30px ${color}`
|
||||||
|
: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Underline only - no dots */}
|
||||||
|
<div
|
||||||
|
className="absolute bottom-3 h-[3px] w-9 rounded-full transition-all duration-300 ease-out"
|
||||||
|
style={{
|
||||||
|
left: indicatorStyle.left,
|
||||||
|
opacity: indicatorStyle.opacity,
|
||||||
|
backgroundColor: color,
|
||||||
|
boxShadow: `0 0 10px ${color}, 0 0 20px ${color}, 0 0 30px ${color}, 0 0 40px ${color}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ----------------------------------------------------
|
||||||
|
MAIN COMPONENT — UPDATED WITH PROFILE DRAWER
|
||||||
|
---------------------------------------------------- */
|
||||||
|
const ProfileHeader = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
||||||
|
const [profileDrawerOpen, setProfileDrawerOpen] = useState(false);
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const isDesktop = useMediaQuery(theme.breakpoints.up("md"));
|
||||||
|
|
||||||
|
const toggleMobileDrawer = (open) => () => setMobileDrawerOpen(open);
|
||||||
|
const toggleProfileDrawer = (open) => () => setProfileDrawerOpen(open);
|
||||||
|
|
||||||
|
/* -----------------------------------------
|
||||||
|
PROFILE DRAWER CONTENT (RIGHT SIDE)
|
||||||
|
------------------------------------------ */
|
||||||
|
const ProfileDrawerContent = (
|
||||||
|
<Box sx={{ width: isDesktop ? 300 : 250 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="h6">Account</Typography>
|
||||||
|
{!isDesktop && (
|
||||||
|
<IconButton onClick={toggleProfileDrawer(false)}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<List>
|
||||||
|
{[
|
||||||
|
"Profile",
|
||||||
|
"Subscription",
|
||||||
|
"Subscription History",
|
||||||
|
"Change Password",
|
||||||
|
"View Reports",
|
||||||
|
"Blocked Users",
|
||||||
|
].map((text) => (
|
||||||
|
<ListItem key={text} disablePadding>
|
||||||
|
<ListItemButton>
|
||||||
|
<ListItemText primary={text} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<Typography sx={{ px: 2, py: 1, fontSize: "14px", opacity: 0.6 }}>
|
||||||
|
Account Settings
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<List>
|
||||||
|
{[
|
||||||
|
"Privacy Policy",
|
||||||
|
"Terms & Conditions",
|
||||||
|
"Contact Us",
|
||||||
|
"Be Safe Online",
|
||||||
|
"Delete Account",
|
||||||
|
"Logout",
|
||||||
|
].map((text) => (
|
||||||
|
<ListItem key={text} disablePadding>
|
||||||
|
<ListItemButton>
|
||||||
|
<ListItemText primary={text} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
/* -----------------------------------------
|
||||||
|
MOBILE LEFT DRAWER
|
||||||
|
------------------------------------------ */
|
||||||
|
const MobileDrawerMenu = (
|
||||||
|
<Box sx={{ width: 250 }}>
|
||||||
|
<Box sx={{ p: 2, display: "flex", justifyContent: "space-between" }}>
|
||||||
|
<Typography variant="h6">Menu</Typography>
|
||||||
|
<IconButton onClick={toggleMobileDrawer(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>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* --------------------------- NAVBAR ----------------------------- */}
|
||||||
|
<AppBar position="sticky" sx={{ backgroundColor: "#fff" }}>
|
||||||
|
<Container maxWidth="xl">
|
||||||
|
<Toolbar disableGutters>
|
||||||
|
{/* Desktop Logo */}
|
||||||
|
<Box sx={{ display: { xs: "none", md: "flex" }, mr: 1 }}>
|
||||||
|
<LazyImage
|
||||||
|
src={Logo}
|
||||||
|
className="w-full h-[50px] rounded-lg object-cover"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* MOBILE: Menu Button */}
|
||||||
|
<Box sx={{ flexGrow: 1, display: { xs: "flex", md: "none" } }}>
|
||||||
|
<IconButton onClick={toggleMobileDrawer(true)}>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Mobile Logo */}
|
||||||
|
<Box
|
||||||
|
sx={{ display: { xs: "flex", md: "none" }, flexGrow: 1 }}
|
||||||
|
onClick={() => navigate("/")}
|
||||||
|
>
|
||||||
|
<LazyImage
|
||||||
|
src={Logo}
|
||||||
|
className="w-full h-[50px] rounded-lg object-cover"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Desktop Menu */}
|
||||||
|
<Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
|
||||||
|
<SparkleNavbar
|
||||||
|
items={["Home", "Matches", "Interest", "Messages", "Search", "Notifications"]}
|
||||||
|
color="#034E08"
|
||||||
|
onItemClick={(item) => setSelectedItem(item)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* AVATAR CLICK → RIGHT DRAWER */}
|
||||||
|
<Box sx={{ flexGrow: 0 }}>
|
||||||
|
<Tooltip title="Account Menu">
|
||||||
|
<IconButton onClick={toggleProfileDrawer(true)}>
|
||||||
|
<Avatar src="/static/images/avatar/2.jpg" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
</Toolbar>
|
||||||
|
</Container>
|
||||||
|
</AppBar>
|
||||||
|
|
||||||
|
{/* MOBILE LEFT DRAWER */}
|
||||||
|
<SwipeableDrawer
|
||||||
|
anchor="left"
|
||||||
|
open={mobileDrawerOpen}
|
||||||
|
onOpen={toggleMobileDrawer(true)}
|
||||||
|
onClose={toggleMobileDrawer(false)}
|
||||||
|
>
|
||||||
|
{MobileDrawerMenu}
|
||||||
|
</SwipeableDrawer>
|
||||||
|
|
||||||
|
{/* PROFILE RIGHT DRAWER */}
|
||||||
|
<SwipeableDrawer
|
||||||
|
anchor="right"
|
||||||
|
open={profileDrawerOpen}
|
||||||
|
onOpen={toggleProfileDrawer(true)}
|
||||||
|
onClose={toggleProfileDrawer(false)}
|
||||||
|
>
|
||||||
|
{ProfileDrawerContent}
|
||||||
|
</SwipeableDrawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfileHeader;
|
||||||
14
src/layout/ProfileLayout.jsx
Normal file
14
src/layout/ProfileLayout.jsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import ProfileHeader from "../components/common/ProfileHeader";
|
||||||
|
const ProfileLayout = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ProfileHeader/>
|
||||||
|
<div className="body-container body-bg w-[100%] max-w-[1400px] mx-auto p-2" style={{ marginBottom:'90px' }}>
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProfileLayout
|
||||||
162
src/pages/UserDashboardHome.jsx
Normal file
162
src/pages/UserDashboardHome.jsx
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||||
|
import { Navigation, Autoplay } from 'swiper/modules';
|
||||||
|
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||||
|
import 'swiper/css';
|
||||||
|
import 'swiper/css/navigation';
|
||||||
|
|
||||||
|
const images = [
|
||||||
|
"https://images.unsplash.com/photo-1612423284934-5b6e7f9e8b5e",
|
||||||
|
"https://images.unsplash.com/photo-1520975911596-5f7f2c1a1c6b",
|
||||||
|
"https://images.unsplash.com/photo-1544005313-94ddf0286df2",
|
||||||
|
"https://images.unsplash.com/photo-1494790108377-be9c29b29330",
|
||||||
|
"https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e",
|
||||||
|
];
|
||||||
|
const UserDashboardHome = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className=" flex items-center justify-center p-4">
|
||||||
|
<div className="w-full max-w-7xl relative">
|
||||||
|
{/* Left Arrow */}
|
||||||
|
<button className="swiper-button-prev-custom absolute left-0 top-1/2 -translate-y-1/2 z-10 w-12 h-12 bg-white/20 backdrop-blur-sm rounded-full flex items-center justify-center hover:bg-white/30 transition-all">
|
||||||
|
<ChevronLeft className="w-6 h-6 text-white" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Right Arrow */}
|
||||||
|
<button className="swiper-button-next-custom absolute right-0 top-1/2 -translate-y-1/2 z-10 w-12 h-12 bg-white/20 backdrop-blur-sm rounded-full flex items-center justify-center hover:bg-white/30 transition-all">
|
||||||
|
<ChevronRight className="w-6 h-6 text-white" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Swiper
|
||||||
|
modules={[Navigation, Autoplay]}
|
||||||
|
navigation={{
|
||||||
|
prevEl: '.swiper-button-prev-custom',
|
||||||
|
nextEl: '.swiper-button-next-custom',
|
||||||
|
}}
|
||||||
|
grabCursor={true}
|
||||||
|
centeredSlides={true}
|
||||||
|
slidesPerView="auto"
|
||||||
|
spaceBetween={20}
|
||||||
|
autoplay={{
|
||||||
|
delay: 3000,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
}}
|
||||||
|
loop={true}
|
||||||
|
className="mySwiper"
|
||||||
|
>
|
||||||
|
{images.map((img, idx) => (
|
||||||
|
<SwiperSlide key={idx}>
|
||||||
|
<div className="relative overflow-hidden rounded-3xl shadow-2xl w-[100%] max-w-[400px] h-[280px]">
|
||||||
|
<img
|
||||||
|
src={img}
|
||||||
|
alt={`Slide ${idx + 1}`}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
<div className="absolute bottom-6 left-1/2 -translate-x-1/2 bg-black/50 backdrop-blur-sm px-6 py-2 rounded-full">
|
||||||
|
<span className="text-white font-semibold text-lg">Slide {idx + 1}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SwiperSlide>
|
||||||
|
))}
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>{`
|
||||||
|
.swiper {
|
||||||
|
width: 100%;
|
||||||
|
padding: 60px 80px;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide {
|
||||||
|
width: 320px;
|
||||||
|
height: 420px;
|
||||||
|
transition: all 0.4s ease;
|
||||||
|
opacity: 0.4;
|
||||||
|
transform: scale(0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide-active {
|
||||||
|
width: 500px !important;
|
||||||
|
height: 600px !important;
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide > div {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-button-prev-custom,
|
||||||
|
.swiper-button-next-custom {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.swiper {
|
||||||
|
padding: 50px 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide {
|
||||||
|
width: 240px;
|
||||||
|
height: 340px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide-active {
|
||||||
|
width: 380px !important;
|
||||||
|
height: 480px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.swiper {
|
||||||
|
padding: 40px 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide {
|
||||||
|
width: 180px;
|
||||||
|
height: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide-active {
|
||||||
|
width: 300px !important;
|
||||||
|
height: 400px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-button-prev-custom,
|
||||||
|
.swiper-button-next-custom {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.swiper {
|
||||||
|
padding: 30px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide {
|
||||||
|
width: 140px;
|
||||||
|
height: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-slide-active {
|
||||||
|
width: 240px !important;
|
||||||
|
height: 340px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper-button-prev-custom,
|
||||||
|
.swiper-button-next-custom {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserDashboardHome
|
||||||
@ -1,10 +1,15 @@
|
|||||||
import { Route, useNavigate } from "react-router-dom";
|
import { Route, useNavigate } from "react-router-dom";
|
||||||
|
import ProfileLayout from "../layout/ProfileLayout";
|
||||||
|
import UserDashboardHome from "../pages/UserDashboardHome";
|
||||||
const UserRoutes = () => {
|
const UserRoutes = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <Route element={<MasterLayout />}>
|
<Route element={<ProfileLayout />}>
|
||||||
<Route path="/" element={<HomePage />} />
|
<Route path="/main/dashboard" element={<UserDashboardHome />} />
|
||||||
|
</Route>
|
||||||
|
{/* <Route element={<ProfileLayout />}>
|
||||||
|
<Route path="/terms" element={<UserDashboardHome />} />
|
||||||
</Route> */}
|
</Route> */}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user