From b78f7df4240302f8bdac04a3504e901068c67e24 Mon Sep 17 00:00:00 2001 From: ztest95 <110767420+ztest95@users.noreply.github.com> Date: Tue, 4 Mar 2025 02:34:18 +0800 Subject: [PATCH 1/2] feat: benefits section --- .../BenefitsSection/BenefitsContent.tsx | 165 ++++++++++++++++++ .../BenefitsSection/BenefitsSection.tsx | 26 +++ .../BenefitsSection/BriefcaseIcon.tsx | 13 ++ .../BenefitsSection/CheckmarkIcon.tsx | 11 ++ .../BenefitsSection/HandshakeIcon.tsx | 11 ++ src/components/ui/cards/Card.tsx | 77 ++++++++ src/components/ui/cards/FeatureCard.tsx | 135 ++++++++++++++ src/pages/index.tsx | 6 +- src/styles/globals.css | 6 + 9 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 src/components/features/BenefitsSection/BenefitsContent.tsx create mode 100644 src/components/features/BenefitsSection/BenefitsSection.tsx create mode 100644 src/components/features/BenefitsSection/BriefcaseIcon.tsx create mode 100644 src/components/features/BenefitsSection/CheckmarkIcon.tsx create mode 100644 src/components/features/BenefitsSection/HandshakeIcon.tsx create mode 100644 src/components/ui/cards/Card.tsx create mode 100644 src/components/ui/cards/FeatureCard.tsx diff --git a/src/components/features/BenefitsSection/BenefitsContent.tsx b/src/components/features/BenefitsSection/BenefitsContent.tsx new file mode 100644 index 0000000..a34c61f --- /dev/null +++ b/src/components/features/BenefitsSection/BenefitsContent.tsx @@ -0,0 +1,165 @@ +import { useState, useRef, useEffect } from "react"; +import { cn } from "@/util/cn"; +import HandshakeIcon from "./HandshakeIcon"; +import BriefcaseIcon from "./BriefcaseIcon"; +import CheckmarkIcon from "./CheckmarkIcon"; +import { FeatureCard } from "@/components/ui/cards/FeatureCard"; + +// Feature data +const FEATURES = [ + { + icon: HandshakeIcon, + title: "Connect", + content: "Network, collaborate with the Pandesal Dev community members on Discord. Whether you are looking for banter, sharing your portfolio, discussing your hobbies or seeking advice, our community is here to support and engage with you.", + variant: "lightblue", + orientation: "bottom", + }, + { + icon: CheckmarkIcon, + title: "Mentorship", + content: "Explore mentorship avenues in our tech community, where industry professionals volunteer their expertise to help individuals with mock interview practice, CV improvement, and providing career advice. Empower yourself with the guidance you need for career growth and skill enhancement.", + variant: "primary", + orientation: "top", + }, + { + icon: BriefcaseIcon, + title: "Job Opportunities", + content: "Browse through our community's comprehensive selection of job listings, including opportunities across various industries and experience levels. Whether you're seeking a new career path or looking for the perfect candidate, take a look at our available job openings here.", + variant: "pandesal", + orientation: "bottom", + }, +]; + + +export function BenefitsContent() { + const [currentSlide, setCurrentSlide] = useState(0); + const [isMobile, setIsMobile] = useState(false); + const carouselRef = useRef(null); + const touchStartX = useRef(0); + const touchEndX = useRef(0); + + // Check if screen is mobile + useEffect(() => { + const checkIsMobile = () => { + setIsMobile(window.innerWidth < 768); + }; + + // Set on initial load + checkIsMobile(); + + // Add resize listener + window.addEventListener('resize', checkIsMobile); + + // Clean up + return () => window.removeEventListener('resize', checkIsMobile); + }, []); + + // Handle next/prev slide + const goToSlide = (index: number) => { + let newIndex = index; + + // Handle bounds + if (index < 0) { + newIndex = FEATURES.length - 1; + } else if (index >= FEATURES.length) { + newIndex = 0; + } + + setCurrentSlide(newIndex); + }; + + // Touch handlers for swipe + const handleTouchStart = (e: React.TouchEvent) => { + touchStartX.current = e.touches[0].clientX; + }; + + const handleTouchMove = (e: React.TouchEvent) => { + touchEndX.current = e.touches[0].clientX; + }; + + const handleTouchEnd = () => { + const diff = touchStartX.current - touchEndX.current; + const threshold = 50; // minimum distance to be considered a swipe + + if (diff > threshold) { + // Swipe left, go to next + if (currentSlide != FEATURES.length - 1) { + goToSlide(currentSlide + 1); + } + } else if (diff < -threshold) { + // Swipe right, go to prev + if (currentSlide != 0) { + goToSlide(currentSlide - 1); + } + } + }; + + return ( +
+ {isMobile ? ( + // Mobile View (Carousel) +
+
+
+ {FEATURES.map((feature, index) => ( +
+ +
+ ))} +
+ + {/* Pagination Dots */} +
+ {FEATURES.map((_, index) => ( +
+
+
+ ) : ( + // Desktop View (Grid) +
+ {FEATURES.map((feature, index) => ( + + ))} +
+ )} +
+ ) +} \ No newline at end of file diff --git a/src/components/features/BenefitsSection/BenefitsSection.tsx b/src/components/features/BenefitsSection/BenefitsSection.tsx new file mode 100644 index 0000000..138bf9c --- /dev/null +++ b/src/components/features/BenefitsSection/BenefitsSection.tsx @@ -0,0 +1,26 @@ +import yellowStar from "@/assets/yellow-star.svg"; +import { delaGothic } from "@/components/ui/fonts"; +import { cn } from "@/util/cn"; +import { BenefitsContent } from "./BenefitsContent"; + +export default function BenefitsSection() { + return ( +
+
+

+ Be part of pandesal.dev +

+
+ +
+ ); +} \ No newline at end of file diff --git a/src/components/features/BenefitsSection/BriefcaseIcon.tsx b/src/components/features/BenefitsSection/BriefcaseIcon.tsx new file mode 100644 index 0000000..c6cb0f9 --- /dev/null +++ b/src/components/features/BenefitsSection/BriefcaseIcon.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import { SVGProps } from "react"; + +const BriefcaseIcon = (props: SVGProps) => ( + + + +); +export default BriefcaseIcon; diff --git a/src/components/features/BenefitsSection/CheckmarkIcon.tsx b/src/components/features/BenefitsSection/CheckmarkIcon.tsx new file mode 100644 index 0000000..8576599 --- /dev/null +++ b/src/components/features/BenefitsSection/CheckmarkIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import { SVGProps } from "react"; + +const CheckmarkIcon = (props: SVGProps) => ( + + + +); +export default CheckmarkIcon; diff --git a/src/components/features/BenefitsSection/HandshakeIcon.tsx b/src/components/features/BenefitsSection/HandshakeIcon.tsx new file mode 100644 index 0000000..2c86ff1 --- /dev/null +++ b/src/components/features/BenefitsSection/HandshakeIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import { SVGProps } from "react"; + +const HandshakeIcon = (props: SVGProps) => ( + + + +); +export default HandshakeIcon; diff --git a/src/components/ui/cards/Card.tsx b/src/components/ui/cards/Card.tsx new file mode 100644 index 0000000..ea15cb8 --- /dev/null +++ b/src/components/ui/cards/Card.tsx @@ -0,0 +1,77 @@ +import * as React from "react" + +import { cn } from "@/util/cn" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } \ No newline at end of file diff --git a/src/components/ui/cards/FeatureCard.tsx b/src/components/ui/cards/FeatureCard.tsx new file mode 100644 index 0000000..cabc13e --- /dev/null +++ b/src/components/ui/cards/FeatureCard.tsx @@ -0,0 +1,135 @@ +import React from "react"; +import { Card, CardHeader, CardTitle, CardDescription } from "./Card"; +import { cn } from "@/util/cn"; +import { delaGothic } from "../fonts"; + +const variants = { + primary: { + card: "text-primary hover:text-background group-hover:text-primary hover:bg-primary hover:shadow-md hover:shadow-primary hover:border-background", + index: "group-hover:text-primary", + }, + lightblue: { + card: "text-lightblue hover:text-background group-hover:text-lightblue hover:bg-lightblue hover:shadow-md hover:shadow-lightblue hover:border-background", + index: "group-hover:text-lightblue", + }, + pandesal: { + card: "text-pandesal hover:text-background group-hover:text-pandesal hover:bg-pandesal hover:shadow-md hover:shadow-pandesal hover:border-background", + index: "group-hover:text-pandesal", + }, +}; + +type VariantType = keyof typeof variants; + +interface FeatureCardProps { + id?: number; + icon: React.ElementType; + title: string; + content: string; + variant?: VariantType; + orientation?: "top" | "bottom"; + className?: string; + children?: React.ReactNode; + showBackgroundText?: boolean; +} + +interface IndexProps { + text?: string; + position?: "top" | "bottom"; + variant?: VariantType; +} + +// Internal BackgroundText component +const Index: React.FC = ({ + text = "text", + position = "bottom", + variant = "primary" +}) => { + const positionClasses = { + top: "-right-10 -top-40", + bottom: "-right-10 -bottom-60", + }; + + return ( +
+ {/* Outline */} +
+ {text} +
+ {/* Body */} +
+ {text} +
+
+ ); +}; + +const FeatureCard = ({ + id, + icon: Icon, + title, + content, + variant = "primary", + orientation = "bottom", + className, + children, + showBackgroundText = false +}: FeatureCardProps) => { + return ( + + {orientation === "top" && +
+ {showBackgroundText && ( + + )} +
+ } + + {Icon && } + + {title} + + + {content} + + + {orientation === "bottom" && +
+ {showBackgroundText && ( + + )} +
+ } + {children} +
+ ); +}; + +export { FeatureCard }; \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 0beb1e8..433e200 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,3 +1,4 @@ +import BenefitsSection from "@/components/features/BenefitsSection/BenefitsSection"; import HeroSection from "@/components/features/HeroSection/HeroSection"; import { Layout } from "@/components/layout.tsx/Layout"; import NavBar from "@/components/ui/navigation/NavBar"; @@ -7,7 +8,10 @@ export default function Home() { <> - {/* Other sections here */} + + {/* Other sections here */} + + ); } diff --git a/src/styles/globals.css b/src/styles/globals.css index b3001ac..f120e6d 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -9,6 +9,8 @@ --primary: hsl(105.22, 48.25%, 71.96%); /* #A6DA95 */ --secondary: hsl(40.25, 69.91%, 77.84%); /* #EED49F */ --accent: hsl(266.51, 82.69%, 79.61%); /* #C6A0F6 */ + --lightblue: hsl(199, 66%, 69%); /* #7DC4E4 */ + --pandesal: hsl(21, 86%, 73%); /* #F5A97F */ --hero-glow-opacity: 0.07; --hero-glow: hsl(36 83% 65% / var(--hero-glow-opacity)); @@ -28,12 +30,16 @@ --color-primary: var(--primary); --color-secondary: var(--secondary); --color-accent: var(--accent); + --color-lightblue: var(--lightblue); + --color-pandesal: var(--pandesal); --color-glow: var(--hero-glow); --ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035); --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1, 0.035); --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1); /* --font-dela: var(--font-dela); */ --text-h1: clamp(2.15rem, 7vw, 75px); + --text-h2: clamp(1.375rem, 5vw, 48px); + --text-h3: clamp(1rem, 3.5vw, 36px); --text-p-sm: clamp(0.9rem, 3.5vw, 1.5rem); --border-accent: 'var(--accent)'; From 4eb71d9387c733733f3e889ba77792387cd382fc Mon Sep 17 00:00:00 2001 From: ztest95 <110767420+ztest95@users.noreply.github.com> Date: Tue, 4 Mar 2025 02:58:32 +0800 Subject: [PATCH 2/2] fix: remove horizontal scrolling & fix white bg on xl screen --- .../features/BenefitsSection/BenefitsSection.tsx | 2 +- src/components/features/HeroSection/HeroContainer.tsx | 2 +- src/components/layout.tsx/Layout.tsx | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/features/BenefitsSection/BenefitsSection.tsx b/src/components/features/BenefitsSection/BenefitsSection.tsx index 138bf9c..1b24a01 100644 --- a/src/components/features/BenefitsSection/BenefitsSection.tsx +++ b/src/components/features/BenefitsSection/BenefitsSection.tsx @@ -6,7 +6,7 @@ import { BenefitsContent } from "./BenefitsContent"; export default function BenefitsSection() { return (
-
+

{children}

; + return
{children}
; } diff --git a/src/components/layout.tsx/Layout.tsx b/src/components/layout.tsx/Layout.tsx index 124a859..62143dc 100644 --- a/src/components/layout.tsx/Layout.tsx +++ b/src/components/layout.tsx/Layout.tsx @@ -2,5 +2,11 @@ import React, { PropsWithChildren } from "react"; export const Layout: React.FC = ({ children }) => { - return
{children}
; + return ( +
+
+ {children} +
+
+ ) };