diff --git a/package-lock.json b/package-lock.json index 73c9f13..f5108fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -79,7 +79,6 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -2561,7 +2560,6 @@ "integrity": "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.14.0" } @@ -2572,7 +2570,6 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2583,7 +2580,6 @@ "integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2634,7 +2630,6 @@ "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", @@ -2900,7 +2895,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3121,7 +3115,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -3313,8 +3306,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", @@ -3520,7 +3512,6 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4804,7 +4795,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -4954,7 +4944,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -4964,7 +4953,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -5385,7 +5373,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5472,7 +5459,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5607,7 +5593,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -5699,7 +5684,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/public/rocket.svg b/public/rocket.svg new file mode 100644 index 0000000..3e935e5 --- /dev/null +++ b/public/rocket.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/MemberCard.tsx b/src/components/MemberCard.tsx index 149dc82..9926288 100644 --- a/src/components/MemberCard.tsx +++ b/src/components/MemberCard.tsx @@ -13,13 +13,13 @@ const MemberCard = ({ member }: MemberCardProps) => { )}&background=random&size=200`; return ( -
+
{/* Avatar */} -
+
{member.name}
diff --git a/src/components/gallery/GalleryItem.tsx b/src/components/gallery/GalleryItem.tsx new file mode 100644 index 0000000..bf449eb --- /dev/null +++ b/src/components/gallery/GalleryItem.tsx @@ -0,0 +1,22 @@ +import './gallery.css'; + +interface GalleryItemProps { + imageSrc: string; + altText: string; + rotation?: number; +} + +const GalleryItem: React.FC = ({ imageSrc, altText, rotation = 0 }) => { + return ( +
+
+
+
+ {altText} +
+
+
+ ); +}; + +export default GalleryItem; diff --git a/src/components/gallery/InfiniteGallery.tsx b/src/components/gallery/InfiniteGallery.tsx new file mode 100644 index 0000000..63c2c8b --- /dev/null +++ b/src/components/gallery/InfiniteGallery.tsx @@ -0,0 +1,37 @@ +import GalleryItem from './GalleryItem'; +import './gallery.css'; + +const exampleImages = [ + 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=500&h=400&fit=crop', + 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=500&h=400&fit=crop', + 'https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=500&h=400&fit=crop', + 'https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=500&h=400&fit=crop', + 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=500&h=400&fit=crop', + 'https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=500&h=400&fit=crop', +]; + +const InfiniteGallery: React.FC = () => { + // Duplicate items to create seamless loop + const items = [...exampleImages, ...exampleImages, ...exampleImages]; + + return ( +
+ {/* The Line - Static */} +
+ + {/* Scrolling Track */} +
+ {items.map((src, index) => ( + + ))} +
+
+ ); +}; + +export default InfiniteGallery; diff --git a/src/components/gallery/gallery.css b/src/components/gallery/gallery.css new file mode 100644 index 0000000..c764766 --- /dev/null +++ b/src/components/gallery/gallery.css @@ -0,0 +1,30 @@ +.gallery-track { + animation: scroll 40s linear infinite; +} + +.gallery-container:hover .gallery-track { + animation-play-state: paused; +} + +@keyframes scroll { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(-33.333%); + } +} + +.gallery-item:hover { + transform: rotate(0deg) !important; +} + +.pin { + top: -14px; + left: 50%; + transform: translateX(-50%) translateY(20%); +} + +.polaroid-card { + transform: translateY(10%); +} \ No newline at end of file diff --git a/src/components/user/MemberUserCard.tsx b/src/components/user/MemberUserCard.tsx index 89cd3bd..9cbdcd4 100644 --- a/src/components/user/MemberUserCard.tsx +++ b/src/components/user/MemberUserCard.tsx @@ -27,8 +27,11 @@ const MemberUserCard = () => { if (!members.length) return

No members found.

; return ( -
-
+
+

+ Meet Our Team +

+
{members.map((member) => ( ))} diff --git a/src/components/user/ProjectSection.tsx b/src/components/user/ProjectSection.tsx index f09e617..d382aa3 100644 --- a/src/components/user/ProjectSection.tsx +++ b/src/components/user/ProjectSection.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Box, LaptopMinimal, TabletSmartphone, Bot, Github } from 'lucide-react'; +import { Box, LaptopMinimal, TabletSmartphone, Bot, Github, Calendar, Link } from 'lucide-react'; import { Button } from '../ui/button'; import { formatDate } from '@/utils/formatdate.utils'; import { getAllProjects } from '@/services/admin/projectServices'; @@ -40,22 +40,19 @@ const ProjectSection: React.FC = () => { : projects.filter((project) => (project.tags || []).some((tag) => tag.name === activeFilter)); return ( -
+

Projects

{/* Filter Buttons */}
- {FILTERS.map((filter) => ( + {FILTERS.map((filter, idx) => ( ))}
@@ -65,9 +62,9 @@ const ProjectSection: React.FC = () => { {filteredProjects.map((project) => (
-
+
{project.thumbnailUrl ? ( { ))}
+
+

+ {project.name} +

+

+ {formatDate(project.createdAt)} +

-

- {project.name} -

-

- Created: {formatDate(project.createdAt)} -

+ {/* Tech Stack */} +
+ {(project.techStacks || []).map((tech) => ( + + {tech} + + ))} +
- {/* Tech Stack */} -
- {(project.techStacks || []).map((tech, i) => ( - - {tech} - - ))} -
+ {/* Contributors*/} +
+ {(project.contributors || []).slice(0, 3).map((c) => ( +
+ {c.avatarUrl && ( + {c.name} + )} +
+ ))} +
- {/* Contributors*/} -
- {(project.contributors || []).slice(0, 3).map((c) => ( - - ))} -
- - {/* Buttons */} -
))} diff --git a/src/components/user/UpcomingEventUserCard.tsx b/src/components/user/UpcomingEventUserCard.tsx index 7b4f419..d06e635 100644 --- a/src/components/user/UpcomingEventUserCard.tsx +++ b/src/components/user/UpcomingEventUserCard.tsx @@ -35,7 +35,7 @@ const UpcomingEventUserCard = () => { const event = events[0]; return ( -
+

Upcoming Events

@@ -43,7 +43,7 @@ const UpcomingEventUserCard = () => { {/* Left Image */}
Upcoming Event diff --git a/src/components/user/UserFooter.tsx b/src/components/user/UserFooter.tsx new file mode 100644 index 0000000..684f607 --- /dev/null +++ b/src/components/user/UserFooter.tsx @@ -0,0 +1,102 @@ +import { FaDiscord, FaInstagram, FaLinkedin } from 'react-icons/fa'; +import { Link } from 'react-router-dom'; + +const socialLinks = [ + { + href: 'https://discord.com/', + icon: , + label: 'Discord', + }, + { + href: 'https://instagram.com/', + icon: , + label: 'Instagram', + }, + { + href: 'https://linkedin.com/', + icon: , + label: 'LinkedIn', + }, +]; + +const quickLinks = [ + { to: '/members', label: 'Members' }, + { to: '/events', label: 'Events' }, + { to: '/projects', label: 'Projects' }, +]; + +const communityLinks = [ + { to: '/', label: 'How it works!' }, + { to: '/', label: 'Get in touch' }, +]; + +const UserFooter = () => { + return ( +
+
+ {/* Left Section */} +
+ Devsphere Logo +

+ Join our Discord channel or follow us on Instagram to keep up to date with our latest + work, events and announcements. +

+
+ {socialLinks.map(({ href, icon, label }, idx) => ( + + {icon} + + ))} +
+
+ {/* Quick Link */} +
+

Quick Link

+
    + {quickLinks.map(({ to, label }, idx) => ( +
  • + + {label} + +
  • + ))} +
+
+ {/* Community */} +
+

Community

+
    + {communityLinks.map(({ to, label }, idx) => ( +
  • + + {label} + +
  • + ))} +
+
+
+ {/* Copyright and Privacy Terms */} +
+
Copyright © 2025 Devsphere All Rights Reserved.
+
+ + Privacy Policy + + + Terms of Use + +
+
+
+ ); +}; + +export default UserFooter; diff --git a/src/components/user/UserLayout.tsx b/src/components/user/UserLayout.tsx index 209943e..4ada6ac 100644 --- a/src/components/user/UserLayout.tsx +++ b/src/components/user/UserLayout.tsx @@ -1,11 +1,13 @@ import { Outlet } from "react-router-dom"; import UserNavbar from "@/components/user/UserNavbar"; +import UserFooter from "@/components/user/UserFooter"; const UserLayout = () => { return (
+
); }; diff --git a/src/components/user/UserNavbar.tsx b/src/components/user/UserNavbar.tsx index 399592d..086f712 100644 --- a/src/components/user/UserNavbar.tsx +++ b/src/components/user/UserNavbar.tsx @@ -1,8 +1,27 @@ +import MobileMenu from './navbar/MobileMenu'; +import NavLinks from './navbar/NavLinks'; +import SocialLinks from './navbar/SocialLinks'; const UserNavbar = () => { return ( -
UserNavbar
- ) -} + + ); +}; + +export default UserNavbar; diff --git a/src/components/user/navbar/MobileMenu.tsx b/src/components/user/navbar/MobileMenu.tsx new file mode 100644 index 0000000..a4ca7c5 --- /dev/null +++ b/src/components/user/navbar/MobileMenu.tsx @@ -0,0 +1,38 @@ +import { Menu, X } from 'lucide-react'; +import { useState } from 'react'; +import NavLinks from './NavLinks'; +import SocialLinks from './SocialLinks'; + +const MobileMenu = () => { + const [isOpen, setIsOpen] = useState(false); + + const toggle = () => setIsOpen((prev) => !prev); + const close = () => setIsOpen(false); + + return ( +
+ {/* Hamburger button */} + + + {/* Dropdown mobile menu */} + {isOpen && ( +
+ +
+ +
+
+ )} +
+ ); +}; + +export default MobileMenu; diff --git a/src/components/user/navbar/NavLinks.tsx b/src/components/user/navbar/NavLinks.tsx new file mode 100644 index 0000000..22402e9 --- /dev/null +++ b/src/components/user/navbar/NavLinks.tsx @@ -0,0 +1,30 @@ +import { NavLink } from 'react-router-dom'; +import { navLinks } from './navbarData'; + +interface NavLinksProps { + onClick?: () => void; +} + +const NavLinks = ({ onClick }: NavLinksProps) => { + return ( + <> + {navLinks.map(({ to, label, icon }, idx) => ( + + `flex items-center gap-2 rounded-full px-6 py-2 text-lg font-medium transition ${ + isActive ? 'border-r border-b text-red-500' : '' + }` + } + > + {icon} + {label} + + ))} + + ); +}; + +export default NavLinks; diff --git a/src/components/user/navbar/SocialLinks.tsx b/src/components/user/navbar/SocialLinks.tsx new file mode 100644 index 0000000..3b7dc24 --- /dev/null +++ b/src/components/user/navbar/SocialLinks.tsx @@ -0,0 +1,26 @@ +import { socialLinks } from './navbarData'; + +interface SocialLinksProps { + className?: string; +} + +const SocialLinks = ({ className = '' }: SocialLinksProps) => { + return ( +
+ {socialLinks.map(({ href, icon, label }, idx) => ( + + {icon} + + ))} +
+ ); +}; + +export default SocialLinks; diff --git a/src/components/user/navbar/navbarData.tsx b/src/components/user/navbar/navbarData.tsx new file mode 100644 index 0000000..a0764bb --- /dev/null +++ b/src/components/user/navbar/navbarData.tsx @@ -0,0 +1,34 @@ +import { + FaHome, + FaCalendarAlt, + FaRegClipboard, + FaUsers, + FaLinkedin, + FaDiscord, + FaInstagram, +} from 'react-icons/fa'; + +export interface NavLinkItem { + to: string; + label: string; + icon: React.ReactNode; +} + +export interface SocialLinkItem { + href: string; + icon: React.ReactNode; + label: string; +} + +export const navLinks: NavLinkItem[] = [ + { to: '/', label: 'Home', icon: }, + { to: '/events', label: 'Events', icon: }, + { to: '/projects', label: 'Projects', icon: }, + { to: '/members', label: 'Members', icon: }, +]; + +export const socialLinks: SocialLinkItem[] = [ + { href: 'https://linkedin.com/', icon: , label: 'LinkedIn' }, + { href: 'https://discord.com/', icon: , label: 'Discord' }, + { href: 'https://instagram.com/', icon: , label: 'Instagram' }, +]; diff --git a/src/index.css b/src/index.css index f5737cd..efd0bf4 100644 --- a/src/index.css +++ b/src/index.css @@ -189,53 +189,4 @@ --shadow-2xl: var(--shadow-2xl); --font-heading: 'Poppins'; --font-body: 'Open Sans'; -} -/* Custom added*/ -.tech-badge { - background-color: var(--muted); - color: var(--destructive); - padding: 0.25rem 0.5rem; - border-radius: 0.375rem; - font-size: 0.875rem; - white-space: nowrap; - font-weight: 500; - transition: transform 0.2s ease; -} - -.tech-badge:hover { - transform: scale(1.05); -} - -/* Tags badges */ -.tag-badge { - background-color: var(--accent); - color: var(--accent-foreground); - padding: 0.25rem 0.5rem; - border-radius: 0.375rem; - font-size: 0.875rem; - font-weight: 500; -} - -/* Contributors avatars */ -.contributor-avatar { - width: 28px; - height: 28px; - border-radius: 9999px; - overflow: hidden; - border: 2px solid var(--primary); - transition: transform 0.2s ease; -} - -.contributor-avatar:hover { - transform: scale(1.1); -} - -/* Project card hover effect */ -.project-card { - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.project-card:hover { - transform: translateY(-4px); - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.12); -} +} \ No newline at end of file diff --git a/src/pages/user/UserEvents.tsx b/src/pages/user/UserEvents.tsx index 1e2b61c..1fbd866 100644 --- a/src/pages/user/UserEvents.tsx +++ b/src/pages/user/UserEvents.tsx @@ -1,8 +1,8 @@ -import EventUserCard from "@/components/user/EventUserCard" - const UserEvents = () => { return ( - +
+ UserEvents +
) } diff --git a/src/pages/user/UserHome.tsx b/src/pages/user/UserHome.tsx index bb1d430..fd2c061 100644 --- a/src/pages/user/UserHome.tsx +++ b/src/pages/user/UserHome.tsx @@ -1,8 +1,41 @@ +import InfiniteGallery from '@/components/gallery/InfiniteGallery'; +import MemberUserCard from '@/components/user/MemberUserCard'; +import ProjectSection from '@/components/user/ProjectSection'; +import UpcomingEventUserCard from '@/components/user/UpcomingEventUserCard'; const UserHome = () => { return ( -
UserHome
- ) -} +
+
+
+ rocket +
+ Learn. + Code +
+
+ Grow. + Together +
+ rocket +
+
+ +
+ + + +
+
+ ); +}; -export default UserHome \ No newline at end of file +export default UserHome;