Skip to content

Commit 30b9f46

Browse files
Merge pull request #133 from sadwika-sabbella-zs/ADD-JOURNEY-TIMELINE
Added Events Page
2 parents 0352639 + b8d5c0e commit 30b9f46

10 files changed

+403
-90
lines changed

package-lock.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"react": "18.2.0",
3636
"react-dom": "18.2.0",
3737
"react-highlight-words": "^0.20.0",
38+
"react-timeline-animation": "^1.2.3",
3839
"refractor": "^4.8.1",
3940
"simple-functional-loader": "^1.2.1",
4041
"tailwindcss": "^3.3.3"

src/app/events/events.json

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"date": "10-02-2020",
4+
"title": "GopherCon Africa",
5+
"description": "GoFr received an amazing response at GopherCon Africa! We showcased our seamless database integration, HTTP/gRPC support, observability, and WebSockets.",
6+
"imageSrc": "https://media.discordapp.net/attachments/1230609009162059836/1301136150156677141/1729836948894.jpeg?ex=67620038&is=6760aeb8&hm=4cbee5de4bec5f526b941db402a3d1607c990054c3cf1853edb4934c70f776ba&=&format=webp&width=2160&height=944"
7+
},
8+
{
9+
"date": "2021",
10+
"title": "Open Source India 2024",
11+
"description": "At Open Source India in Bengaluru, we kicked off with an amazing response at our booth on 23rd and an interactive Developer’s Meet on Oct 24th. It was a pleasure connecting with everyone at our booth, where demos highlighted GoFr’s innovative approach to writing microservices, making backend development simpler and more efficient.",
12+
"imageSrc": "https://media.discordapp.net/attachments/1230609009162059836/1301136149749698650/1729836948365.jpeg?ex=67620038&is=6760aeb8&hm=f43bbf9dacbc631f36dfa7ae4402f61caaddcde1c8e4357236418d11c86d8006&=&format=webp&width=1418&height=1056"
13+
},
14+
{
15+
"date": "2022",
16+
"title": "GoFr is on a roll with its workshops!",
17+
"description": "Past two months, we’ve hosted workshops at top colleges such as IIT BHU, IIIT Sri City, Thapar University, JIIT Noida, NIT Jalandhar, and NIT Nagpur. At Gravitas in VIT Vellore, we proudly supported the Hack the Hackathon event, where students explored GoFr and contributed innovative ideas to enhance the framework.",
18+
"imageSrc": "https://media.discordapp.net/attachments/1230609050899710052/1301491118780973137/WhatsApp_Image_2024-10-30_at_15.47.27.jpeg?ex=6761f94f&is=6760a7cf&hm=4bc52e39b921aeb00e444522fa49744dcbfa9d6fb7e35b1d3b2c167b6b6111d9&=&format=webp&width=1408&height=1056"
19+
},
20+
{
21+
"date": "2020",
22+
"title": "GopherCon Africa",
23+
"description": "GoFr received an amazing response at GopherCon Africa! We showcased our seamless database integration, HTTP/gRPC support, observability, and WebSockets.",
24+
"imageSrc": "https://media.discordapp.net/attachments/1230609009162059836/1301136150156677141/1729836948894.jpeg?ex=67620038&is=6760aeb8&hm=4cbee5de4bec5f526b941db402a3d1607c990054c3cf1853edb4934c70f776ba&=&format=webp&width=2160&height=944"
25+
},
26+
{
27+
"date": "2021",
28+
"title": "Open Source India 2024",
29+
"description": "At Open Source India in Bengaluru, we kicked off with an amazing response at our booth on 23rd and an interactive Developer’s Meet on Oct 24th. It was a pleasure connecting with everyone at our booth, where demos highlighted GoFr’s innovative approach to writing microservices, making backend development simpler and more efficient.",
30+
"imageSrc": "https://media.discordapp.net/attachments/1230609009162059836/1301136149749698650/1729836948365.jpeg?ex=67620038&is=6760aeb8&hm=f43bbf9dacbc631f36dfa7ae4402f61caaddcde1c8e4357236418d11c86d8006&=&format=webp&width=1418&height=1056"
31+
},
32+
{
33+
"date": "2022",
34+
"title": "GoFr is on a roll with its workshops!",
35+
"description": "Past two months, we’ve hosted workshops at top colleges such as IIT BHU, IIIT Sri City, Thapar University, JIIT Noida, NIT Jalandhar, and NIT Nagpur. At Gravitas in VIT Vellore, we proudly supported the Hack the Hackathon event, where students explored GoFr and contributed innovative ideas to enhance the framework.",
36+
"imageSrc": "https://media.discordapp.net/attachments/1230609050899710052/1301491118780973137/WhatsApp_Image_2024-10-30_at_15.47.27.jpeg?ex=6761f94f&is=6760a7cf&hm=4bc52e39b921aeb00e444522fa49744dcbfa9d6fb7e35b1d3b2c167b6b6111d9&=&format=webp&width=1408&height=1056"
37+
}
38+
]

src/app/events/page.jsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"use client"
2+
import Events from "@/components/EventsTimeline";
3+
4+
const EventsTimeline = () => {
5+
return(
6+
<Events/>
7+
)
8+
9+
};
10+
11+
export default EventsTimeline;
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React, { useRef, useEffect, useState } from 'react';
2+
3+
const AnimatedTimelineItem = ({ date, title, description, imageSrc, isLeft }) => {
4+
const itemRef = useRef(null);
5+
const [isVisible, setIsVisible] = useState(false);
6+
const [isExiting, setIsExiting] = useState(false);
7+
8+
useEffect(() => {
9+
const observer = new IntersectionObserver(
10+
([entry]) => {
11+
if (entry.isIntersecting) {
12+
setIsVisible(true);
13+
setIsExiting(false);
14+
} else {
15+
setIsVisible(false);
16+
setIsExiting(true);
17+
}
18+
},
19+
{
20+
threshold: 0.1,
21+
}
22+
);
23+
24+
if (itemRef.current) {
25+
observer.observe(itemRef.current);
26+
}
27+
28+
return () => {
29+
if (itemRef.current) {
30+
observer.unobserve(itemRef.current);
31+
}
32+
};
33+
}, []);
34+
35+
const animationStyle = isVisible
36+
? {
37+
transform: 'translateX(0)',
38+
opacity: 1,
39+
transition: 'all 1s ease-out',
40+
}
41+
: isExiting
42+
? isLeft
43+
? {
44+
transform: 'translateX(100px)',
45+
opacity: 0,
46+
transition: 'all 1s ease-out',
47+
}
48+
: {
49+
transform: 'translateX(-100px)',
50+
opacity: 0,
51+
transition: 'all 1s ease-out',
52+
}
53+
: isLeft
54+
? {
55+
transform: 'translateX(-100px)',
56+
opacity: 0,
57+
}
58+
: {
59+
transform: 'translateX(100px)',
60+
opacity: 0,
61+
};
62+
63+
return (
64+
<div
65+
ref={itemRef}
66+
className="flex flex-col md:flex-row items-center w-full xs:mb-20 md:mb-60 relative transition-all duration-1000 ease-out"
67+
>
68+
<div
69+
className={`w-full md:w-5/12 ${
70+
isLeft ? 'md:text-right md:pr-8' : 'md:order-last md:text-left md:pl-8'
71+
}`}
72+
>
73+
<div className="mb-4">
74+
<h3 className="text-xl text-white font-bold mb-2">{title}</h3>
75+
<p className="text-white">{description}</p>
76+
</div>
77+
</div>
78+
<div className="w-full px-4 md:w-2/12 flex md:justify-center items-center mb-4 md:mb-0">
79+
<div className="w-auto h-6 bg-primary rounded-full z-20 flex items-center justify-center">
80+
<span className="text-xs px-4 bg-[#23293b] p-2 rounded-3xl text-white font-semibold items-center whitespace-nowrap">
81+
{date}
82+
</span>
83+
</div>
84+
</div>
85+
86+
87+
<div
88+
className={`w-full md:w-5/12 ${
89+
isLeft ? 'md:pl-8' : 'md:order-first md:pr-8'
90+
}`}
91+
style={animationStyle}
92+
>
93+
<img
94+
src={imageSrc}
95+
alt={title}
96+
className="w-full h-72 object-cover rounded-lg shadow-md"
97+
/>
98+
</div>
99+
</div>
100+
);
101+
};
102+
103+
export default AnimatedTimelineItem;

src/components/EventsTimeline.jsx

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"use client"
2+
import React, { useEffect } from 'react';
3+
import VerticalTimeline from './VerticalTimeline';
4+
import events from '../app/events/events.json'
5+
6+
const EventsTimeline = () => {
7+
useEffect(() => {
8+
if (window?.innerWidth >= 768) {
9+
window?.scrollBy(0, 70);
10+
}
11+
}, []);
12+
return (
13+
<div className="bg-slate-900 min-h-screen py-4 md:py-28">
14+
<div className="sm:mx-auto px-4 py-8">
15+
<h1 className="flex justify-center items-center text-4xl font-bold text-center mb-12 text-white sm:hidden">
16+
Events
17+
</h1>
18+
<VerticalTimeline events={events} />
19+
</div>
20+
</div>
21+
);
22+
};
23+
24+
export default EventsTimeline;

src/components/Footer.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const footerLinks = [
3636
// { title: 'Releases', link: '/releases' },
3737
{ title: 'Documentation', link: '/docs' },
3838
{ title: 'Blogs', link: 'https://medium.com/gofr', target_blank: true },
39+
{ title: 'Events', link: '/events' }
3940
]
4041

4142
function FooterUi() {
@@ -44,7 +45,7 @@ function FooterUi() {
4445
<div className="mx-auto max-w-screen-2xl overflow-hidden px-6 py-4 lg:px-8">
4546
<nav
4647
aria-label="Footer"
47-
className="-mb-6 columns-2 sm:flex sm:justify-center sm:space-x-12"
48+
className="-mb-6 flex gap-5 justify-center sm:space-x-12"
4849
>
4950
{footerLinks.map((item) => (
5051
<div key={item.title} className="pb-6">

src/components/Layout.jsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ function Header() {
2424
let [isScrolled, setIsScrolled] = useState(false)
2525
let pathname = usePathname()
2626
const isCertificate = pathname.includes('certificate')
27+
const isEvents = pathname.includes('events');
2728
const [githubStars, setGithubStars] = useState(null)
2829

2930
useEffect(() => {
@@ -76,12 +77,13 @@ function Header() {
7677
</span>
7778
</Link>
7879
</div>
79-
{!isCertificate && (
80+
{!isCertificate && !isEvents && (
8081
<div className="-my-5 mr-6 sm:mr-8 md:mr-0">
8182
<Search />
8283
</div>
8384
)}
84-
<div className="relative flex basis-0 items-center justify-end gap-2 sm:gap-2 md:flex-grow">
85+
{isEvents && <div className='relative flex flex-grow basis-0 items-center justify-center sm:block hidden'><h1 className="justify-center items-center text-xl sm:text-3xl md:text-4xl font-bold text-center text-white">Events</h1></div>}
86+
<div className="relative flex flex-grow basis-0 items-center justify-end gap-2 sm:gap-3 md:flex-grow">
8587
<Link
8688
href="https://github.com/gofr-dev/gofr"
8789
className="group"

src/components/VerticalTimeline.jsx

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import React, { useEffect, useRef, useState } from "react";
2+
import dynamic from "next/dynamic";
3+
import AnimatedTimelineItem from "./AnimatedTimelineItem";
4+
import Image from "next/image";
5+
import blurCyanImage from "@/images/blur-cyan.png";
6+
7+
const TimelineObserver = dynamic(() => import("react-timeline-animation"), { ssr: false });
8+
9+
const Timeline = ({ setObserver, customHeight }) => {
10+
const timelineRef = useRef(null);
11+
12+
useEffect(() => {
13+
setObserver(timelineRef.current);
14+
}, [setObserver]);
15+
16+
return (
17+
<div
18+
id="timeline"
19+
ref={timelineRef}
20+
style={{ height: customHeight }}
21+
className="absolute left-3 xs:left-0 md:left-1/2 py-8 top-0 w-1 transform md:-translate-x-1/2 md:block hidden transition-all duration-100 ease-in-out"
22+
/>
23+
);
24+
};
25+
26+
const VerticalTimeline = ({ events }) => {
27+
const containerRef = useRef(null);
28+
const [timelineHeight, setTimelineHeight] = useState("100%");
29+
30+
useEffect(() => {
31+
const updateTimelineHeight = () => {
32+
if (containerRef.current) {
33+
const lastEvent = containerRef.current.querySelector(
34+
`.timeline-item:last-child`
35+
);
36+
if (lastEvent) {
37+
const timelineTop = containerRef.current.getBoundingClientRect().top;
38+
const lastEventBottom = lastEvent.getBoundingClientRect().bottom;
39+
const lastEventHeight = lastEvent.offsetHeight;
40+
setTimelineHeight(`${lastEventBottom - timelineTop - lastEventHeight / 2}px`);
41+
}
42+
}
43+
};
44+
updateTimelineHeight();
45+
}, [events]);
46+
47+
return (
48+
<div ref={containerRef} className="md:container mx-auto px-4 py-4 md:py-8 relative">
49+
<TimelineObserver
50+
initialColor="transparent"
51+
fillColor="cyan"
52+
hasReverse={true}
53+
handleObserve={(setObserver) => (
54+
<Timeline setObserver={setObserver} customHeight={timelineHeight} />
55+
)}
56+
/>
57+
58+
<Image
59+
className="fixed bottom-full right-full top-5 -mb-56 -mr-72 opacity-50"
60+
src={blurCyanImage}
61+
alt=""
62+
width={530}
63+
height={530}
64+
unoptimized
65+
priority
66+
/>
67+
68+
<div className="relative z-10">
69+
{events.map((event, index) => (
70+
<div key={index} className="timeline-item">
71+
<AnimatedTimelineItem
72+
date={event.date}
73+
title={event.title}
74+
description={event.description}
75+
imageSrc={event.imageSrc}
76+
isLeft={index % 2 === 0}
77+
/>
78+
</div>
79+
))}
80+
</div>
81+
82+
<Image
83+
className="fixed right-0 bottom-0 -mb-56 -mr-72 opacity-50"
84+
src={blurCyanImage}
85+
alt=""
86+
width={530}
87+
height={530}
88+
unoptimized
89+
priority
90+
/>
91+
</div>
92+
);
93+
};
94+
95+
export default VerticalTimeline;

0 commit comments

Comments
 (0)