Skip to content

feat: benefits section implementation #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions src/components/features/BenefitsSection/BenefitsContent.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement>(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 (
<div>
{isMobile ? (
// Mobile View (Carousel)
<div className="py-12">
<div
ref={carouselRef}
className="relative overflow-hidden"
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
<div
className="flex transition-transform duration-300 ease-in-out"
style={{ transform: `translateX(-${currentSlide * 100}%)` }}
>
{FEATURES.map((feature, index) => (
<div key={index} className="w-full flex-shrink-0 px-4">
<FeatureCard
key={index}
id={index}
icon={feature.icon}
title={feature.title}
content={feature.content}
variant={feature.variant}
orientation={feature.orientation}
showBackgroundText
/>
</div>
))}
</div>

{/* Pagination Dots */}
<div className="flex justify-center mt-6 gap-2">
{FEATURES.map((_, index) => (
<button
key={index}
onClick={() => goToSlide(index)}
className={cn(
"h-3 w-3 rounded-full transition-all",
currentSlide === index
? "bg-accent w-6"
: "bg-gray-300"
)}
/>
))}
</div>
</div>
</div>
) : (
// Desktop View (Grid)
<div className="grid grid-cols-3 lg:grid-cols-3 gap-6 lg:gap-8 py-12 lg:py-24">
{FEATURES.map((feature, index) => (
<FeatureCard
key={index}
id={index}
icon={feature.icon}
title={feature.title}
content={feature.content}
variant={feature.variant}
orientation={feature.orientation}
showBackgroundText
/>
))}
</div>
)}
</div>
)
}
26 changes: 26 additions & 0 deletions src/components/features/BenefitsSection/BenefitsSection.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<section>
<header className="py-24">
<h2
style={{
["--bg-image-before" as string]: `url(${yellowStar.src})`,
}}
className={cn(
"text-h2 leading-tight text-center max-w-3xl mx-auto relative",
delaGothic.className,
"lg:before:absolute lg:before:bg-[image:var(--bg-image-before)] lg:before:z-10 lg:before:h-16 lg:before:-left-16 lg:before:w-16",
)}
>
Be part of pandesal.dev
</h2>
</header>
<BenefitsContent />
</section>
);
}
13 changes: 13 additions & 0 deletions src/components/features/BenefitsSection/BriefcaseIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from "react";
import { SVGProps } from "react";

const BriefcaseIcon = (props: SVGProps<SVGSVGElement>) => (
<svg width="72" height="72" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M30 6C27.6131 6 25.3239 6.94821 23.636 8.63604C21.9482 10.3239 21 12.6131 21 15V18H15C12.6131 18 10.3239 18.9482 8.63604 20.636C6.94821 22.3239 6 24.6131 6 27V34.146L10.341 36.315L10.356 36.324L10.437 36.363L10.797 36.531C11.123 36.685 11.609 36.897 12.255 37.167C13.542 37.698 15.423 38.415 17.757 39.132C22.443 40.572 28.89 42 36 42C43.116 42 49.56 40.575 54.24 39.132C56.58 38.412 58.458 37.698 59.745 37.167C60.3573 36.9134 60.9634 36.6453 61.563 36.363L61.644 36.324L61.659 36.318L66 34.143V27C66 24.6131 65.0518 22.3239 63.364 20.636C61.6761 18.9482 59.3869 18 57 18H51V15C51 12.6131 50.0518 10.3239 48.364 8.63604C46.6761 6.94821 44.3869 6 42 6H30ZM45 18V15C45 14.2044 44.6839 13.4413 44.1213 12.8787C43.5587 12.3161 42.7957 12 42 12H30C29.2044 12 28.4413 12.3161 27.8787 12.8787C27.3161 13.4413 27 14.2044 27 15V18H45ZM64.341 41.682L66 40.854V57C66 59.3869 65.0518 61.6761 63.364 63.364C61.6761 65.0518 59.3869 66 57 66H15C12.6131 66 10.3239 65.0518 8.63604 63.364C6.94821 61.6761 6 59.3869 6 57V40.854L7.659 41.682L7.665 41.688L7.677 41.694L7.716 41.712L7.839 41.772L8.292 41.982C8.682 42.162 9.246 42.414 9.963 42.708C11.397 43.302 13.452 44.088 15.993 44.868C21.057 46.428 28.11 48 36 48C43.884 48 50.94 46.425 56.01 44.868C58.0524 44.2417 60.0645 43.521 62.04 42.708C62.7544 42.4131 63.4616 42.101 64.161 41.772L64.284 41.712L64.323 41.694L64.335 41.688L64.341 41.682ZM36 30C35.2044 30 34.4413 30.3161 33.8787 30.8787C33.3161 31.4413 33 32.2044 33 33C33 33.7956 33.3161 34.5587 33.8787 35.1213C34.4413 35.6839 35.2044 36 36 36H36.03C36.8256 36 37.5887 35.6839 38.1513 35.1213C38.7139 34.5587 39.03 33.7956 39.03 33C39.03 32.2044 38.7139 31.4413 38.1513 30.8787C37.5887 30.3161 36.8256 30 36.03 30H36Z"
/>
</svg>
);
export default BriefcaseIcon;
11 changes: 11 additions & 0 deletions src/components/features/BenefitsSection/CheckmarkIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from "react";
import { SVGProps } from "react";

const CheckmarkIcon = (props: SVGProps<SVGSVGElement>) => (
<svg width="72" height="72" viewBox="0 0 72 72"xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M31.7579 6.29952C32.8199 5.23894 34.2408 4.61488 35.7402 4.55046C37.2397 4.48603 38.7089 4.98593 39.8579 5.95152L40.2419 6.30252L45.9419 11.9995H53.9999C55.5131 11.9998 56.9703 12.5718 58.0797 13.601C59.189 14.6302 59.8684 16.0406 59.9819 17.5495L59.9999 17.9995V26.0575L65.6999 31.7575C66.7613 32.8196 67.3859 34.2411 67.4503 35.7413C67.5148 37.2415 67.0144 38.7113 66.0479 39.8605L65.6969 40.2415L59.9969 45.9415V53.9995C59.9974 55.5132 59.4257 56.9712 58.3964 58.0812C57.3671 59.1911 55.9564 59.871 54.4469 59.9845L53.9999 59.9995H45.9449L40.2449 65.6995C39.1828 66.7609 37.7613 67.3855 36.2611 67.45C34.7609 67.5144 33.2911 67.014 32.1419 66.0475L31.7609 65.6995L26.0609 59.9995H17.9999C16.4862 60 15.0282 59.4283 13.9183 58.399C12.8083 57.3698 12.1284 55.959 12.0149 54.4495L11.9999 53.9995V45.9415L6.29991 40.2415C5.23849 39.1794 4.6139 37.7579 4.54948 36.2577C4.48505 34.7575 4.98546 33.2877 5.95191 32.1385L6.29991 31.7575L11.9999 26.0575V17.9995C12.0002 16.4863 12.5722 15.0291 13.6014 13.9198C14.6306 12.8105 16.041 12.131 17.5499 12.0175L17.9999 11.9995H26.0579L31.7579 6.29952ZM45.2369 26.9485L32.5049 39.6805L27.2009 34.3765C26.638 33.814 25.8747 33.4981 25.0789 33.4984C24.283 33.4987 23.5199 33.8151 22.9574 34.378C22.3949 34.9409 22.079 35.7043 22.0793 36.5001C22.0796 37.2959 22.396 38.059 22.9589 38.6215L30.1709 45.8335C30.4774 46.1401 30.8412 46.3833 31.2417 46.5493C31.6422 46.7152 32.0714 46.8006 32.5049 46.8006C32.9384 46.8006 33.3676 46.7152 33.7681 46.5493C34.1686 46.3833 34.5325 46.1401 34.8389 45.8335L49.4789 31.1905C50.0254 30.6247 50.3278 29.8669 50.3209 29.0803C50.3141 28.2937 49.9986 27.5413 49.4424 26.9851C48.8861 26.4288 48.1337 26.1133 47.3471 26.1065C46.5605 26.0997 45.8027 26.402 45.2369 26.9485Z"
/>
</svg>
);
export default CheckmarkIcon;
11 changes: 11 additions & 0 deletions src/components/features/BenefitsSection/HandshakeIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from "react";
import { SVGProps } from "react";

const HandshakeIcon = (props: SVGProps<SVGSVGElement>) => (
<svg width="72" height="72" viewBox="0 0 72 44" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M36.3825 2.385L25.4925 11.205C23.6812 12.6675 23.3325 15.3 24.705 17.1788C26.1562 19.1812 28.98 19.575 30.9262 18.0563L42.0975 9.37125C42.885 8.76375 44.01 8.89875 44.6287 9.68625C45.2475 10.4738 45.1012 11.5988 44.3137 12.2175L41.9625 14.04L61.8975 32.4H66.6C69.5812 32.4 72 29.9813 72 27V12.6C72 9.61875 69.5812 7.2 66.6 7.2H57.5212L57.0825 6.91875L48.915 1.6875C47.1937 0.585 45.18 0 43.1325 0C40.68 0 38.295 0.84375 36.3825 2.385ZM38.9475 16.38L33.1312 20.9025C29.5875 23.67 24.4462 22.95 21.7912 19.305C19.2937 15.8738 19.9237 11.0813 23.22 8.415L32.58 0.84375C31.275 0.2925 29.8687 0.0112498 28.44 0.0112498C26.325 -1.71829e-07 24.2662 0.63 22.5 1.8L14.4 7.2H5.4C2.41875 7.2 0 9.61875 0 12.6V27C0 29.9813 2.41875 32.4 5.4 32.4H17.5725L27.855 41.7825C30.06 43.7962 33.4688 43.6388 35.4825 41.4338C36.1012 40.7475 36.5175 39.9488 36.7313 39.1163L38.6437 40.8713C40.8375 42.885 44.2575 42.7388 46.2712 40.545C46.7775 39.9938 47.1488 39.3525 47.385 38.6888C49.5675 40.1513 52.5375 39.8475 54.3712 37.845C56.385 35.6512 56.2387 32.2313 54.045 30.2175L38.9475 16.38Z"
/>
</svg>
);
export default HandshakeIcon;
2 changes: 1 addition & 1 deletion src/components/features/HeroSection/HeroContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ interface ContainerProps {
className?: string;
}
export function HeroContainer({ children, className }: ContainerProps) {
return <div className={cn("bg-background text-foreground w-screen min-h-screen relative overflow-x-hidden pt-22 lg:pt-28 2xl:pt-40", className)} data-testid="hero-container">{children}</div>;
return <div className={cn("bg-background text-foreground w-full min-h-screen relative overflow-x-hidden pt-22 lg:pt-28 2xl:pt-40", className)} data-testid="hero-container">{children}</div>;

}
8 changes: 7 additions & 1 deletion src/components/layout.tsx/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
import React, { PropsWithChildren } from "react";

export const Layout: React.FC<PropsWithChildren> = ({ children }) => {
return <div className="container lg:container-lg bg-background text-foreground antialiased box-border">{children}</div>;
return (
<div className="bg-background">
<div className="container lg:container-lg bg-background text-foreground antialiased box-border">
{children}
</div>
</div>
)
};
77 changes: 77 additions & 0 deletions src/components/ui/cards/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as React from "react"

import { cn } from "@/util/cn"

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-3xl border bg-card text-card-foreground shadow",
"border-2 border-dashed shadow-lg",
className
)}
{...props}
/>
))
Card.displayName = "Card"

const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"

const CardTitle = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"

const CardDescription = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"

const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
Loading