Skip to content

Commit d0d7b60

Browse files
authored
Merge pull request #42 from codersforcauses/issue-33-Create_the_last_three_sections_of_the_landing_page
Issue 33 create the last three sections of the landing page
2 parents 79d010d + f3d5fa1 commit d0d7b60

5 files changed

Lines changed: 382 additions & 95 deletions

File tree

client/public/heart.png

803 Bytes
Loading
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { ChevronLeft, ChevronRight } from "lucide-react";
2+
import Image from "next/image";
3+
import Link from "next/link";
4+
import { useEffect, useRef, useState } from "react";
5+
6+
import { UiEvent as EventType } from "@/hooks/useEvents";
7+
8+
type EventCarouselProps = {
9+
items: EventType[];
10+
};
11+
12+
const GAP = 40;
13+
14+
export default function EventCarousel({ items }: EventCarouselProps) {
15+
const viewportRef = useRef<HTMLDivElement>(null);
16+
const firstItemRef = useRef<HTMLDivElement>(null);
17+
18+
const [currentIndex, setCurrentIndex] = useState(0);
19+
const [visibleCount, setVisibleCount] = useState(3);
20+
const [itemWidth, setItemWidth] = useState(0);
21+
22+
const maxIndex = Math.max(items.length - visibleCount, 0);
23+
const slideLeft = () => {
24+
setCurrentIndex((prev) => Math.max(prev - 1, 0));
25+
};
26+
const slideRight = () => {
27+
setCurrentIndex((prev) => Math.min(prev + 1, maxIndex));
28+
};
29+
const translateX = -(currentIndex * (itemWidth + GAP));
30+
31+
/* Observe item width */
32+
useEffect(() => {
33+
if (!firstItemRef.current) return;
34+
const observer = new ResizeObserver(() => {
35+
const width = firstItemRef.current?.clientWidth ?? 0;
36+
setItemWidth(width);
37+
});
38+
observer.observe(firstItemRef.current);
39+
return () => observer.disconnect();
40+
}, []);
41+
42+
useEffect(() => {
43+
const updateVisibleCount = () => {
44+
if (window.innerWidth < 768) {
45+
setVisibleCount(1);
46+
} else {
47+
setVisibleCount(3);
48+
}
49+
};
50+
updateVisibleCount();
51+
window.addEventListener("resize", updateVisibleCount);
52+
return () => window.removeEventListener("resize", updateVisibleCount);
53+
}, []);
54+
55+
return (
56+
<div className="container mx-auto rounded-lg bg-primary-foreground px-4 py-8 lg:px-12">
57+
<div className="flex items-center justify-between px-10">
58+
<div className="flex items-center">
59+
<h2 className="font-jersey10 text-4xl tracking-wide text-white">
60+
Upcoming Events
61+
</h2>
62+
63+
<div className="ml-5 flex gap-3 text-lg text-white/60">
64+
<ChevronLeft
65+
className={`hover:text-white ${
66+
currentIndex === 0 ? "opacity-40" : "cursor-pointer"
67+
}`}
68+
onClick={slideLeft}
69+
/>
70+
<ChevronRight
71+
className={`hover:text-white ${
72+
currentIndex === maxIndex ? "opacity-40" : "cursor-pointer"
73+
}`}
74+
onClick={slideRight}
75+
/>
76+
</div>
77+
</div>
78+
79+
<Link href="/events" className="font-jersey10">
80+
See More
81+
</Link>
82+
</div>
83+
84+
<div className="mt-10 px-10">
85+
<div ref={viewportRef} className="overflow-hidden">
86+
<div
87+
className="flex transition-transform duration-300 ease-out"
88+
style={{
89+
gap: GAP,
90+
transform: `translateX(${translateX}px)`,
91+
}}
92+
>
93+
{items.map((event, index) => (
94+
<div
95+
key={event.id}
96+
ref={index === 0 ? firstItemRef : undefined}
97+
className="w-full flex-shrink-0 md:w-[calc((100%-80px)/3)]"
98+
>
99+
<div className="relative aspect-[16/9] w-full overflow-hidden rounded-lg">
100+
<Image
101+
src={event.coverImage}
102+
alt={event.name}
103+
fill
104+
className="object-cover"
105+
/>
106+
</div>
107+
108+
<h3 className="mt-6 font-firaCode text-lg font-semibold tracking-wide text-white">
109+
{event.name}
110+
</h3>
111+
112+
{/* Needs proper processing and laying out */}
113+
<p className="text-sm tracking-wide text-white/70">
114+
{event.startTime}
115+
</p>
116+
117+
<div className="mt-3 w-full border-b border-white/20" />
118+
</div>
119+
))}
120+
</div>
121+
</div>
122+
</div>
123+
</div>
124+
);
125+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import Image from "next/image";
2+
3+
export type eventHighlightCardImage = {
4+
url: string;
5+
width: number;
6+
height: number;
7+
alt: string;
8+
};
9+
10+
export type eventHighlightCardType = {
11+
id: number;
12+
title: string;
13+
description: string;
14+
type: string;
15+
image: eventHighlightCardImage | null;
16+
row: number;
17+
};
18+
19+
// Purple card header section.
20+
const renderCardHeader = (card: eventHighlightCardType) => {
21+
// Renders differently if we want the techno border.
22+
if (card.type === "special-border") {
23+
return (
24+
<div
25+
style={{
26+
clipPath:
27+
"polygon(0% 0%, 71% 0%, 78% 7px, 100% 7px, 100% calc(100% - 8px), 0% calc(100% - 8px))",
28+
}}
29+
className="relative bg-accent"
30+
>
31+
<div
32+
style={{
33+
clipPath:
34+
"polygon(1px 1px, calc(71% - 1px) 1px, calc(78% - 1px) 8px, calc(100% - 1px) 8px, calc(100% - 1px) calc(100% - 8px - 1px), 1px calc(100% - 8px - 1px))",
35+
}}
36+
className="bg-dark_alt p-4 pt-3 font-jersey10 text-2xl font-semibold"
37+
>
38+
{card.title}
39+
</div>
40+
</div>
41+
);
42+
}
43+
44+
return (
45+
<div className="rounded-md border border-accent bg-dark_alt px-4 py-2 font-jersey10 text-2xl font-semibold">
46+
{card.title}
47+
</div>
48+
);
49+
};
50+
51+
export function EventHighlightCard({
52+
id,
53+
title,
54+
description,
55+
type,
56+
image,
57+
row,
58+
}: eventHighlightCardType) {
59+
return (
60+
<div key={id} className="flex flex-col">
61+
{renderCardHeader({ id, title, description, type, image, row })}
62+
63+
<div className="mt-4 rounded-md border border-muted bg-landingCard p-4 text-gray-200">
64+
<div className="flex gap-2">
65+
<span></span>
66+
<p>{description}</p>
67+
{image && (
68+
<Image
69+
src={image.url}
70+
width={image.width}
71+
height={image.height}
72+
alt={image.alt}
73+
className="m-3 size-20 [image-rendering:pixelated]"
74+
/>
75+
)}
76+
</div>
77+
</div>
78+
</div>
79+
);
80+
}

0 commit comments

Comments
 (0)