Skip to content

Commit ad8d677

Browse files
committed
added events page
1 parent ce9d328 commit ad8d677

File tree

18 files changed

+949
-195
lines changed

18 files changed

+949
-195
lines changed

docusaurus.config.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,22 +247,26 @@ const config = {
247247
to: "/projects/",
248248
},
249249
{
250-
label: "📚 E-books",
250+
label: "E-books",
251251
to: "/ebooks/",
252252
},
253253

254254
{
255-
label: "🛣️ Roadmap",
255+
label: "Roadmap",
256256
to: "/roadmap/",
257257
},
258258
{
259-
label: "🧑‍💻 Live Editor",
259+
label: "Live Editor",
260260
to: "/LiveEditor/",
261261
},
262262
{
263-
label: "📺 Broadcast",
263+
label: "Broadcast",
264264
to: "https://codeharborhub-broadcast-web.vercel.app/",
265265
},
266+
{
267+
label: "Events",
268+
to: "/events/",
269+
}
266270
],
267271
},
268272
// {

src/components/EventCard.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import Link from "@docusaurus/Link";
2+
import { Event } from "../data/events";
3+
4+
export default function EventCard({ event }: { event: Event }) {
5+
return (
6+
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-lg hover:shadow-2xl transition-all duration-300 p-6 flex flex-col items-center text-center">
7+
<img
8+
src={event.logo}
9+
alt={event.name}
10+
className="w-24 h-24 object-contain mb-4"
11+
/>
12+
<h2 className="text-xl font-semibold text-gray-900 dark:text-gray-100">
13+
{event.name}
14+
</h2>
15+
<p className="text-sm text-gray-600 dark:text-gray-300 mt-2">
16+
{event.dates}
17+
</p>
18+
<p className="text-gray-700 dark:text-gray-400 mt-3">{event.description}</p>
19+
<div className="flex gap-2 flex-wrap mt-3">
20+
{event.tags.map((tag) => (
21+
<span
22+
key={tag}
23+
className="px-3 py-1 text-xs rounded-full bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-200"
24+
>
25+
{tag}
26+
</span>
27+
))}
28+
</div>
29+
<div className="mt-5 flex gap-4">
30+
<Link
31+
href={`/events/${event.id}`}
32+
className="px-4 py-2 rounded-xl bg-indigo-600 text-white hover:bg-indigo-700 transition"
33+
>
34+
View Details
35+
</Link>
36+
<a
37+
href={event.link}
38+
target="_blank"
39+
className="px-4 py-2 rounded-xl border border-indigo-600 text-indigo-600 dark:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-gray-700 transition"
40+
>
41+
Official Site
42+
</a>
43+
</div>
44+
</div>
45+
);
46+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { BookOpenCheck, ArrowLeft } from 'lucide-react';
2+
import GuideStep from './GuideStep';
3+
import FAQSection from './FAQSection';
4+
import ResourcesSection from './ResourcesSection';
5+
import { GUIDE_STEPS, FAQS, RESOURCES } from './guide';
6+
7+
interface ContributionGuideProps {
8+
onBackToEvents: () => void;
9+
}
10+
11+
export default function ContributionGuide({ onBackToEvents }: ContributionGuideProps) {
12+
return (
13+
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
14+
<div className="bg-gradient-to-br from-green-50 to-emerald-50 dark:from-gray-900 dark:to-gray-800 border-b border-gray-200 dark:border-gray-700">
15+
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
16+
<button
17+
onClick={onBackToEvents}
18+
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white mb-6 transition-colors"
19+
>
20+
<ArrowLeft className="w-4 h-4" />
21+
<span>Back to Events</span>
22+
</button>
23+
24+
<div className="flex items-center gap-4 mb-6">
25+
<div className="flex items-center justify-center w-16 h-16 bg-green-600 dark:bg-green-500 rounded-2xl shadow-lg">
26+
<BookOpenCheck className="w-8 h-8 text-white" />
27+
</div>
28+
<div>
29+
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white">
30+
Contribution Guide
31+
</h1>
32+
<p className="text-gray-600 dark:text-gray-400 mt-1">
33+
Your step-by-step journey into open source
34+
</p>
35+
</div>
36+
</div>
37+
38+
<p className="text-lg text-gray-700 dark:text-gray-300 leading-relaxed max-w-3xl">
39+
Contributing to open source can seem intimidating at first, but it's one of the most rewarding
40+
ways to learn, teach, and build experience. This guide will walk you through every step of making
41+
your first contribution, from finding a project to getting your pull request merged.
42+
</p>
43+
</div>
44+
</div>
45+
46+
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
47+
<div className="mb-16">
48+
<div className="flex items-center gap-3 mb-8">
49+
<div className="h-1 w-12 bg-blue-600 dark:bg-blue-500 rounded-full"></div>
50+
<h2 className="text-3xl font-bold text-gray-900 dark:text-white">
51+
Getting Started
52+
</h2>
53+
</div>
54+
55+
<div className="space-y-0">
56+
{GUIDE_STEPS.map((step, index) => (
57+
<GuideStep key={step.id} step={step} index={index} />
58+
))}
59+
</div>
60+
</div>
61+
62+
<div className="space-y-8">
63+
<FAQSection faqs={FAQS} />
64+
<ResourcesSection resources={RESOURCES} />
65+
</div>
66+
67+
<div className="mt-12 bg-gradient-to-r from-blue-600 to-cyan-600 dark:from-blue-700 dark:to-cyan-700 rounded-2xl p-8 text-center text-white">
68+
<h3 className="text-2xl font-bold mb-3">Ready to Get Started?</h3>
69+
<p className="mb-6 text-blue-50">
70+
Browse open source events and find the perfect project for your first contribution.
71+
</p>
72+
<button
73+
onClick={onBackToEvents}
74+
className="inline-flex items-center gap-2 px-6 py-3 bg-white text-blue-600 font-semibold rounded-lg hover:bg-blue-50 transition-colors shadow-lg"
75+
>
76+
<ArrowLeft className="w-5 h-5" />
77+
Back to Events
78+
</button>
79+
</div>
80+
</div>
81+
</div>
82+
);
83+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Calendar, ExternalLink, CalendarPlus } from 'lucide-react';
2+
import { Event } from './events';
3+
4+
interface EventCardProps {
5+
event: Event;
6+
onAddToCalendar: (event: Event) => void;
7+
}
8+
9+
export default function EventCard({ event, onAddToCalendar }: EventCardProps) {
10+
return (
11+
<div className="group bg-white dark:bg-gray-800 rounded-2xl shadow-md hover:shadow-2xl transition-all duration-300 overflow-hidden border border-gray-100 dark:border-gray-700 flex flex-col h-full">
12+
<div className="p-6 bg-gradient-to-br from-gray-50 to-white dark:from-gray-800 dark:to-gray-750 flex items-center justify-center h-40 relative overflow-hidden">
13+
<div className="absolute inset-0 bg-gradient-to-br from-blue-50/50 to-cyan-50/50 dark:from-blue-900/10 dark:to-cyan-900/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
14+
<img
15+
src={event.logo_url}
16+
alt={`${event.name} logo`}
17+
className="max-w-full max-h-full object-contain filter group-hover:scale-105 transition-transform duration-300 relative z-10 bg-gray-700 p-4 rounded-lg"
18+
onError={(e) => {
19+
const target = e.target as HTMLImageElement;
20+
target.style.display = 'none';
21+
const fallback = document.createElement('div');
22+
fallback.className = 'text-4xl font-bold text-gray-400 dark:text-gray-600';
23+
fallback.textContent = event.name.charAt(0);
24+
target.parentElement?.appendChild(fallback);
25+
}}
26+
/>
27+
</div>
28+
29+
<div className="p-6 flex flex-col flex-grow">
30+
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-3 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">
31+
{event.name}
32+
</h3>
33+
34+
<div className="flex items-center text-sm text-gray-600 dark:text-gray-400 mb-4">
35+
<Calendar className="w-4 h-4 mr-2 flex-shrink-0" />
36+
<span>{event.start_date}{event.end_date}</span>
37+
</div>
38+
39+
<p className="text-gray-600 dark:text-gray-300 mb-4 leading-relaxed flex-grow">
40+
{event.description}
41+
</p>
42+
43+
<div className="flex flex-wrap gap-2 mb-4">
44+
{event.tags.map((tag) => (
45+
<span
46+
key={tag}
47+
className="px-3 py-1 bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 text-xs font-medium rounded-full border border-blue-100 dark:border-blue-800"
48+
>
49+
{tag}
50+
</span>
51+
))}
52+
</div>
53+
54+
<div className="flex gap-3 mt-auto">
55+
<a
56+
href={event.official_link}
57+
target="_blank"
58+
rel="noopener noreferrer"
59+
className="flex-1 flex items-center justify-center gap-2 px-4 py-2.5 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors duration-200 shadow-sm hover:shadow-md"
60+
>
61+
Learn More
62+
</a>
63+
64+
<button
65+
onClick={() => onAddToCalendar(event)}
66+
className="flex items-center justify-center px-4 py-2.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 font-medium rounded-lg transition-colors duration-200 border border-gray-200 dark:border-gray-600 shadow-sm hover:shadow-md"
67+
title="Add to Calendar"
68+
>
69+
<CalendarPlus className="w-4 h-4" />
70+
</button>
71+
</div>
72+
</div>
73+
</div>
74+
);
75+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { useState, useMemo } from 'react';
2+
import EventCard from './EventCard';
3+
import { Event, MOCK_EVENTS } from './events';
4+
5+
export default function EventsGrid() {
6+
const [searchQuery, setSearchQuery] = useState('');
7+
const [selectedTags, setSelectedTags] = useState<string[]>([]);
8+
9+
10+
11+
const filteredEvents = useMemo(() => {
12+
return MOCK_EVENTS.filter((event) => {
13+
const matchesSearch =
14+
searchQuery === '' ||
15+
event.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
16+
event.description.toLowerCase().includes(searchQuery.toLowerCase());
17+
18+
const matchesTags =
19+
selectedTags.length === 0 ||
20+
selectedTags.some((tag) => event.tags.includes(tag));
21+
22+
return matchesSearch && matchesTags;
23+
});
24+
}, [searchQuery, selectedTags]);
25+
26+
27+
28+
const handleAddToCalendar = (event: Event) => {
29+
const title = encodeURIComponent(event.name);
30+
const details = encodeURIComponent(event.description);
31+
const dates = encodeURIComponent(`${event.start_date} - ${event.end_date}`);
32+
33+
const googleCalendarUrl = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${title}&details=${details}%0A%0A${event.official_link}&dates=${dates}`;
34+
35+
window.open(googleCalendarUrl, '_blank');
36+
};
37+
38+
return (
39+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
40+
<>
41+
<div className="mb-6 text-sm text-gray-600 dark:text-gray-400">
42+
Showing {filteredEvents.length} {filteredEvents.length === 1 ? 'event' : 'events'}
43+
</div>
44+
45+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
46+
{filteredEvents.map((event) => (
47+
<EventCard
48+
key={event.id}
49+
event={event}
50+
onAddToCalendar={handleAddToCalendar}
51+
/>
52+
))}
53+
</div>
54+
</>
55+
</div>
56+
);
57+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useState } from 'react';
2+
import { ChevronDown, HelpCircle } from 'lucide-react';
3+
import { FAQ } from './guide';
4+
5+
interface FAQSectionProps {
6+
faqs: FAQ[];
7+
}
8+
9+
function FAQItem({ faq }: { faq: FAQ }) {
10+
const [isOpen, setIsOpen] = useState(false);
11+
12+
return (
13+
<div className="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
14+
<button
15+
onClick={() => setIsOpen(!isOpen)}
16+
className="w-full flex items-center justify-between p-4 text-left bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-750 transition-colors"
17+
>
18+
<span className="font-semibold text-gray-900 dark:text-white pr-4">
19+
{faq.question}
20+
</span>
21+
<ChevronDown
22+
className={`w-5 h-5 text-gray-500 dark:text-gray-400 flex-shrink-0 transition-transform duration-200 ${
23+
isOpen ? 'transform rotate-180' : ''
24+
}`}
25+
/>
26+
</button>
27+
{isOpen && (
28+
<div className="px-4 pb-4 bg-white dark:bg-gray-800">
29+
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">
30+
{faq.answer}
31+
</p>
32+
</div>
33+
)}
34+
</div>
35+
);
36+
}
37+
38+
export default function FAQSection({ faqs }: FAQSectionProps) {
39+
return (
40+
<div className="bg-gradient-to-br from-blue-50 to-cyan-50 dark:from-gray-900 dark:to-gray-800 rounded-2xl p-8 md:p-12">
41+
<div className="flex items-center gap-3 mb-6">
42+
<div className="flex items-center justify-center w-12 h-12 bg-blue-600 dark:bg-blue-500 rounded-xl">
43+
<HelpCircle className="w-6 h-6 text-white" />
44+
</div>
45+
<h2 className="text-3xl font-bold text-gray-900 dark:text-white">
46+
Frequently Asked Questions
47+
</h2>
48+
</div>
49+
<p className="text-gray-600 dark:text-gray-400 mb-8">
50+
Common questions from new contributors, answered by the community.
51+
</p>
52+
<div className="space-y-3">
53+
{faqs.map((faq) => (
54+
<FAQItem key={faq.id} faq={faq} />
55+
))}
56+
</div>
57+
</div>
58+
);
59+
}

0 commit comments

Comments
 (0)