diff --git a/app/events/page.tsx b/app/events/page.tsx index e9d2bea..cfbc435 100644 --- a/app/events/page.tsx +++ b/app/events/page.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Metadata } from "next"; -import EventsPageLayout from "@/layouts/EventsPageLayout"; +import EventsPageLayout from "@/layouts/EventPageLayout/EventsPageLayout"; export const metadata: Metadata = { title: "Events", }; diff --git a/app/globals.css b/app/globals.css index 63296f1..48931dd 100644 --- a/app/globals.css +++ b/app/globals.css @@ -82,4 +82,12 @@ .heading { @apply font-bold text-4xl md:text-5xl text-center; } + .no-scrollbar::-webkit-scrollbar { + display: none; + } + /* Hide scrollbar for IE, Edge and Firefox */ + .no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } } diff --git a/components/EventsCard.tsx b/components/EventsCard.tsx index 6a66b6c..4e7cbd8 100644 --- a/components/EventsCard.tsx +++ b/components/EventsCard.tsx @@ -1,7 +1,12 @@ +"use client"; + +import { getEventTimeLine } from "@/lib/utils"; import { CardContainer, CardBody, CardItem } from "./ui/3d-card"; import { IconClock } from "@tabler/icons-react"; import Image from "next/image"; -import Link from "next/link"; +// import Link from "next/link"; +import MobileCard from "./MobileCard"; +import ModalCard from "./ModalCard"; export interface EventsCardProps { title: string; @@ -30,74 +35,122 @@ export default function EventsCard({ deadline, formLink, }: EventsCardProps): JSX.Element { + const tagsMap = { + past: ( +
+ Completed +
+ ), + present: ( +
+ Live +
+ ), + future: ( +
+ Coming Up +
+ ), + }; return ( - -
+ +
- {title} + {tagsMap[getEventTimeLine(deadline)]} + {eventName} - {/* Event organizing club logo */} - -
- {clubName} +
+
+ + {title} + + {/* Event organizing club logo */} + +
+ {clubName} +
+
- -
- - {eventName} - - - {description} - -
-
-
+ + + {description} + + - -

{String(deadline)}

+ {isOver(deadline) ? ( +
+ +

{String(deadline)}

+
+ ) : null}
- {isOver(deadline) ? null : ( - - - Register +
+
+
+ + {isOver(deadline) ? ( +
+ Registration closed +
+ ) : ( + // +
+ + +
+ // + )}
- - )} +
+
diff --git a/components/Hero.tsx b/components/Hero.tsx index 27c337d..a6e7418 100644 --- a/components/Hero.tsx +++ b/components/Hero.tsx @@ -12,6 +12,7 @@ const Hero = () => { words="Transforming people into experienced developers" className="text-center text-3xl md:text-4xl lg:text-6xl" /> + + +
+ View Details +
+
+ +
+ +
+ {eventName} +
+
+ + {title} +
+ {clubName} +
+
+ + {description} + +
+

Last date: {String(deadline)}

+
+
+
+ + +
+ Register +
+ + +
+ Cancel +
+
+
+
+
+ + ); +} diff --git a/components/ModalCard.tsx b/components/ModalCard.tsx new file mode 100644 index 0000000..f3e58ce --- /dev/null +++ b/components/ModalCard.tsx @@ -0,0 +1,141 @@ +"use client"; + +import { + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalTrigger, +} from "./ui/animated-modal"; +import Image from "next/image"; +import Link from "next/link"; +import styles from "layouts/layouts.module.css"; +import { motion } from "framer-motion"; +import { getEventTimeLine } from "@/lib/utils"; + +export interface ModalCardProps { + title: string; + description: string; + eventName: string; + eventImage: string; + clubName: string; + clubImage: string; + deadline: Date | string; + formLink: string; +} + +export default function ModalCard({ + title, + description, + eventName, + eventImage, + clubName, + clubImage, + deadline, + formLink, +}: ModalCardProps): JSX.Element { + const tagsMap = { + past: ( +
+ Completed +
+ ), + present: ( +
+ Live +
+ ), + future: ( +
+ Coming Up +
+ ), + }; + + return ( + + +
+ View Details +
+
+ + +
+ {/* part 1 -> image container */} +
+ + {tagsMap[getEventTimeLine(deadline)]} + {eventName} + +
+ + {/* part-2 -> details container */} +
+ {/* title name */} +
+
+ Event Name: {title} +
+
+ {/* divider */} +
+ {/* description */} +
+ + Description:{" "} + + {description} +
+ {/* last date */} +
+
+

Last date: {String(deadline)}

+
+
+
+
+ + +
+
+
+ {clubName} +
+
+ {/* register button */} +
+ {/* {isOver(deadline) ? null : ( */} + +
+ Register +
+ + {/* )} */} +
+
+
+ + + ); +} diff --git a/components/PageHeading.tsx b/components/PageHeading.tsx index f334963..dfff996 100644 --- a/components/PageHeading.tsx +++ b/components/PageHeading.tsx @@ -9,8 +9,11 @@ const PageHeading = ({ link?: string; }) => { return ( -
- +
+
); }; diff --git a/components/ui/animated-modal.tsx b/components/ui/animated-modal.tsx new file mode 100644 index 0000000..1a0a747 --- /dev/null +++ b/components/ui/animated-modal.tsx @@ -0,0 +1,245 @@ +"use client"; +import { cn } from "@/lib/utils"; +import { AnimatePresence, motion } from "framer-motion"; +import { createPortal } from "react-dom"; +import React, { + ReactNode, + createContext, + useContext, + useEffect, + useRef, + useState, +} from "react"; + +interface ModalContextType { + open: boolean; + setOpen: (open: boolean) => void; +} + +const ModalContext = createContext(undefined); + +export const ModalProvider = ({ children }: { children: ReactNode }) => { + const [open, setOpen] = useState(false); + + return ( + + {children} + + ); +}; + +export const useModal = () => { + const context = useContext(ModalContext); + if (!context) { + throw new Error("useModal must be used within a ModalProvider"); + } + return context; +}; + +export function Modal({ children }: { children: ReactNode }) { + return {children}; +} + +export const ModalTrigger = ({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) => { + const { setOpen } = useModal(); + return ( + + ); +}; + +export const ModalBody = ({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) => { + const { open } = useModal(); + + useEffect(() => { + if (open) { + document.body.style.overflow = "hidden"; + } else { + document.body.style.overflow = "auto"; + } + }, [open]); + + const modalRef = useRef(null); + const { setOpen } = useModal(); + useOutsideClick(modalRef, () => setOpen(false)); + + return createPortal( + + {open && ( + + + + + + {children} + + + )} + , + document.body + ); +}; + +export const ModalContent = ({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) => { + return ( +
+ {children} +
+ ); +}; + +export const ModalFooter = ({ + children, + className, +}: { + children: ReactNode; + className?: string; +}) => { + return ( +
+ {children} +
+ ); +}; + +const Overlay = ({ className }: { className?: string }) => { + return ( + + ); +}; + +const CloseIcon = () => { + const { setOpen } = useModal(); + return ( + + ); +}; + +// Hook to detect clicks outside of a component. +// Add it in a separate file, I've added here for simplicity +export const useOutsideClick = ( + ref: React.RefObject, + callback: Function +) => { + useEffect(() => { + const listener = (event: any) => { + // DO NOTHING if the element being clicked is the target element or their children + if (!ref.current || ref.current.contains(event.target)) { + return; + } + callback(event); + }; + + document.addEventListener("mousedown", listener); + document.addEventListener("touchstart", listener); + + return () => { + document.removeEventListener("mousedown", listener); + document.removeEventListener("touchstart", listener); + }; + }, [ref, callback]); +}; diff --git a/components/ui/drawer.tsx b/components/ui/drawer.tsx new file mode 100644 index 0000000..1e7e4d3 --- /dev/null +++ b/components/ui/drawer.tsx @@ -0,0 +1,118 @@ +"use client" + +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "@/lib/utils" + +const Drawer = ({ + shouldScaleBackground = true, + ...props +}: React.ComponentProps) => ( + +) +Drawer.displayName = "Drawer" + +const DrawerTrigger = DrawerPrimitive.Trigger + +const DrawerPortal = DrawerPrimitive.Portal + +const DrawerClose = DrawerPrimitive.Close + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName + +const DrawerContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + +
+ {children} + + +)) +DrawerContent.displayName = "DrawerContent" + +const DrawerHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DrawerHeader.displayName = "DrawerHeader" + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DrawerFooter.displayName = "DrawerFooter" + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerTitle.displayName = DrawerPrimitive.Title.displayName + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerDescription.displayName = DrawerPrimitive.Description.displayName + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +} diff --git a/layouts/EventsPageLayout.tsx b/layouts/EventPageLayout/EventsPageLayout.tsx similarity index 96% rename from layouts/EventsPageLayout.tsx rename to layouts/EventPageLayout/EventsPageLayout.tsx index 958a423..3240962 100644 --- a/layouts/EventsPageLayout.tsx +++ b/layouts/EventPageLayout/EventsPageLayout.tsx @@ -35,11 +35,11 @@ const EventsPageLayout = () => { deadline={event.date} formLink={event.form_link} /> - ))} + ))}
); }; -export default EventsPageLayout; +export default EventsPageLayout; \ No newline at end of file diff --git a/layouts/layouts.module.css b/layouts/layouts.module.css index 960a459..2808c3d 100644 --- a/layouts/layouts.module.css +++ b/layouts/layouts.module.css @@ -9,7 +9,14 @@ width: 22rem; height: 25rem; } -@media screen and (min-width: 768px) { +/* ============EventCard================== */ +.container{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-around; +} +@media screen and (min-width: 769px) { .formContainer { width: 50%; } @@ -18,3 +25,11 @@ height: 26rem; } } +@media screen and (min-width: 1025px) { +.container{ + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-around; +} +} \ No newline at end of file diff --git a/lib/utils.ts b/lib/utils.ts index 365058c..1c1be31 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,6 +1,20 @@ -import { type ClassValue, clsx } from "clsx"; +import { ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } + +export const getEventTimeLine = (time: any) => { + const eventDate = new Date(time); + const currentDate = new Date(); + const difference = eventDate.getTime() - currentDate.getTime(); + const differenceInCalendarDays = difference / (1000 * 60 * 60 * 24); + if (differenceInCalendarDays <= 2 && differenceInCalendarDays >= 0) { + return "present"; + } else if (differenceInCalendarDays <= -1) { + return "past"; + } else { + return "future"; + } +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a183282..473c3d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@types/three": "^0.163.0", "axios": "^1.7.2", "class-variance-authority": "^0.7.0", - "clsx": "^2.1.0", + "clsx": "^2.1.1", "framer-motion": "^11.0.25", "lucide-react": "^0.365.0", "mini-svg-data-uri": "^1.4.4", @@ -31,7 +31,8 @@ "react-redux": "^9.1.2", "resend": "^3.4.0", "tailwind-merge": "^2.2.2", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "vaul": "^1.1.2" }, "devDependencies": { "@types/node": "^20", @@ -1074,6 +1075,42 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", @@ -7756,6 +7793,19 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8004,21 +8054,6 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.1.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz", - "integrity": "sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } diff --git a/package.json b/package.json index bcb66d0..3734114 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@types/three": "^0.163.0", "axios": "^1.7.2", "class-variance-authority": "^0.7.0", - "clsx": "^2.1.0", + "clsx": "^2.1.1", "framer-motion": "^11.0.25", "lucide-react": "^0.365.0", "mini-svg-data-uri": "^1.4.4", @@ -33,7 +33,8 @@ "react-redux": "^9.1.2", "resend": "^3.4.0", "tailwind-merge": "^2.2.2", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "vaul": "^1.1.2" }, "devDependencies": { "@types/node": "^20",