Skip to content

Commit 6fd0988

Browse files
committed
feat: add 'Contribute to Learn as Much as You CAN' section with paginated listing
1 parent 93ffa69 commit 6fd0988

File tree

3 files changed

+332
-2
lines changed

3 files changed

+332
-2
lines changed

public/swift-256x256_2x.png

245 KB
Loading

src/App.tsx

Lines changed: 302 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { useState, useEffect } from 'react';
2-
import { MapPin, Phone, MailOpen, Linkedin, Github, ExternalLink as Link, Code, Send, CheckCircle, AlertCircle, Award, Briefcase, Zap, Rocket, Stethoscope, Camera, Download, X, Menu, Users } from 'lucide-react';
2+
import { MapPin, Phone, MailOpen, Linkedin, Github, ExternalLink as Link, Code, Send, CheckCircle, AlertCircle, Award, Briefcase, Zap, Rocket, Stethoscope, Camera, Download, X, Menu, Users, BookOpen, ChevronLeft, ChevronRight, Calendar, ArrowRight } from 'lucide-react';
3+
import { contributions } from './contributionsData';
4+
import type { ContributionItem } from './contributionsData';
35

46
// Custom Cursor Component
57
const CustomCursor = () => {
@@ -84,7 +86,7 @@ const StatCard = ({ icon: Icon, number, label }: { icon: any, number: string, la
8486

8587
// Navigation
8688
const Navigation = ({ currentPage, setCurrentPage, mobileMenuOpen, setMobileMenuOpen }: { currentPage: string, setCurrentPage: (p: string) => void, mobileMenuOpen: boolean, setMobileMenuOpen: (b: boolean) => void }) => {
87-
const pages = ['home', 'about', 'experience', 'projects', 'contact'];
89+
const pages = ['home', 'about', 'experience', 'projects', 'contributions', 'contact'];
8890

8991
return (
9092
<nav className="navbar">
@@ -775,6 +777,95 @@ const ContactPage = () => {
775777
);
776778
};
777779

780+
// Contributions Page
781+
const ITEMS_PER_PAGE = 10;
782+
783+
const ContributionsPage = () => {
784+
const [currentPage, setCurrentPage] = useState(1);
785+
const totalPages = Math.ceil(contributions.length / ITEMS_PER_PAGE);
786+
const startIdx = (currentPage - 1) * ITEMS_PER_PAGE;
787+
const currentItems = contributions.slice(startIdx, startIdx + ITEMS_PER_PAGE);
788+
789+
const goToPage = (page: number) => {
790+
if (page >= 1 && page <= totalPages) {
791+
setCurrentPage(page);
792+
window.scrollTo({ top: 0, behavior: 'smooth' });
793+
}
794+
};
795+
796+
return (
797+
<div className="page contributions-page">
798+
<div className="contributions-header">
799+
<div className="contributions-header-content">
800+
<BookOpen size={40} className="contributions-header-icon" />
801+
<div>
802+
<h1 className="page-title" style={{ marginBottom: '0.25rem' }}>Contribute to Learn as Much as You CAN</h1>
803+
<p className="contributions-subtitle">Curated resources, guides, and insights for continuous learning</p>
804+
</div>
805+
</div>
806+
</div>
807+
808+
<div className="contributions-list">
809+
{currentItems.map((item: ContributionItem) => (
810+
<a
811+
key={item.id}
812+
href={item.redirectionLink}
813+
target="_blank"
814+
rel="noopener noreferrer"
815+
className="contribution-card-link"
816+
>
817+
<GlassCard hover className="contribution-card">
818+
<div className="contribution-image-wrapper">
819+
<img src={`/${item.imageUrl}`} alt={item.title} className="contribution-image" />
820+
</div>
821+
<div className="contribution-content">
822+
<div className="contribution-meta">
823+
<span className="contribution-date">
824+
<Calendar size={14} />
825+
{item.date}
826+
</span>
827+
</div>
828+
<h3 className="contribution-title">{item.title}</h3>
829+
<p className="contribution-card-subtitle">{item.subtitle}</p>
830+
<p className="contribution-description">{item.description}</p>
831+
<span className="contribution-read-more">
832+
Read More <ArrowRight size={16} />
833+
</span>
834+
</div>
835+
</GlassCard>
836+
</a>
837+
))}
838+
</div>
839+
840+
{totalPages > 1 && (
841+
<div className="pagination-controls">
842+
<button
843+
className="pagination-btn"
844+
onClick={() => goToPage(currentPage - 1)}
845+
disabled={currentPage === 1}
846+
>
847+
<ChevronLeft size={20} /> Previous
848+
</button>
849+
<span className="pagination-info">Page {currentPage} of {totalPages}</span>
850+
<button
851+
className="pagination-btn"
852+
onClick={() => goToPage(currentPage + 1)}
853+
disabled={currentPage === totalPages}
854+
>
855+
Next <ChevronRight size={20} />
856+
</button>
857+
</div>
858+
)}
859+
860+
{totalPages <= 1 && contributions.length > 0 && (
861+
<div className="pagination-controls">
862+
<span className="pagination-info">Page 1 of 1</span>
863+
</div>
864+
)}
865+
</div>
866+
);
867+
};
868+
778869
// Main App
779870
const App = () => {
780871
const [currentPage, setCurrentPage] = useState('home');
@@ -790,6 +881,7 @@ const App = () => {
790881
case 'about': return <AboutPage />;
791882
case 'experience': return <ExperiencePage />;
792883
case 'projects': return <ProjectsPage />;
884+
case 'contributions': return <ContributionsPage />;
793885
case 'contact': return <ContactPage />;
794886
default: return <HomePage setCurrentPage={setCurrentPage} />;
795887
}
@@ -1986,6 +2078,194 @@ const App = () => {
19862078
color: #475569;
19872079
}
19882080
2081+
/* ========== Contributions Page Styles ========== */
2082+
.contributions-page {
2083+
max-width: 900px;
2084+
margin: 0 auto;
2085+
}
2086+
2087+
.contributions-header {
2088+
margin-bottom: 2.5rem;
2089+
}
2090+
2091+
.contributions-header-content {
2092+
display: flex;
2093+
align-items: center;
2094+
gap: 1.25rem;
2095+
}
2096+
2097+
.contributions-header-icon {
2098+
color: #06b6d4;
2099+
flex-shrink: 0;
2100+
}
2101+
2102+
.contributions-subtitle {
2103+
color: #94a3b8;
2104+
font-size: 1rem;
2105+
margin-top: 0.25rem;
2106+
}
2107+
2108+
.contributions-list {
2109+
display: flex;
2110+
flex-direction: column;
2111+
gap: 1.5rem;
2112+
}
2113+
2114+
.contribution-card-link {
2115+
text-decoration: none;
2116+
color: inherit;
2117+
display: block;
2118+
}
2119+
2120+
.contribution-card {
2121+
display: flex;
2122+
flex-direction: row;
2123+
overflow: hidden;
2124+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2125+
}
2126+
2127+
.contribution-card:hover {
2128+
transform: translateY(-4px);
2129+
box-shadow: 0 20px 40px rgba(6, 182, 212, 0.15);
2130+
border-color: rgba(6, 182, 212, 0.4);
2131+
}
2132+
2133+
.contribution-image-wrapper {
2134+
width: 220px;
2135+
min-width: 220px;
2136+
max-width: 220px;
2137+
display: flex;
2138+
align-items: center;
2139+
justify-content: center;
2140+
background: linear-gradient(135deg, rgba(6, 182, 212, 0.08), rgba(37, 99, 235, 0.08));
2141+
padding: 1.5rem;
2142+
flex-shrink: 0;
2143+
}
2144+
2145+
.contribution-image {
2146+
width: 100%;
2147+
max-width: 140px;
2148+
height: auto;
2149+
object-fit: contain;
2150+
border-radius: 16px;
2151+
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.3));
2152+
transition: transform 0.3s ease;
2153+
}
2154+
2155+
.contribution-card:hover .contribution-image {
2156+
transform: scale(1.08);
2157+
}
2158+
2159+
.contribution-content {
2160+
flex: 1;
2161+
padding: 1.5rem 1.75rem;
2162+
display: flex;
2163+
flex-direction: column;
2164+
gap: 0.5rem;
2165+
}
2166+
2167+
.contribution-meta {
2168+
display: flex;
2169+
align-items: center;
2170+
gap: 0.75rem;
2171+
}
2172+
2173+
.contribution-date {
2174+
display: flex;
2175+
align-items: center;
2176+
gap: 0.4rem;
2177+
color: #06b6d4;
2178+
font-size: 0.85rem;
2179+
font-weight: 500;
2180+
}
2181+
2182+
.contribution-title {
2183+
font-size: 1.35rem;
2184+
font-weight: 700;
2185+
background: linear-gradient(135deg, #e2e8f0, #06b6d4);
2186+
-webkit-background-clip: text;
2187+
-webkit-text-fill-color: transparent;
2188+
background-clip: text;
2189+
margin: 0;
2190+
}
2191+
2192+
.contribution-card-subtitle {
2193+
font-size: 0.95rem;
2194+
color: #94a3b8;
2195+
font-weight: 500;
2196+
margin: 0;
2197+
}
2198+
2199+
.contribution-description {
2200+
font-size: 0.9rem;
2201+
color: #cbd5e1;
2202+
line-height: 1.6;
2203+
margin: 0.25rem 0 0;
2204+
display: -webkit-box;
2205+
-webkit-line-clamp: 3;
2206+
-webkit-box-orient: vertical;
2207+
overflow: hidden;
2208+
}
2209+
2210+
.contribution-read-more {
2211+
display: inline-flex;
2212+
align-items: center;
2213+
gap: 0.4rem;
2214+
color: #06b6d4;
2215+
font-size: 0.9rem;
2216+
font-weight: 600;
2217+
margin-top: auto;
2218+
padding-top: 0.5rem;
2219+
transition: gap 0.3s ease, color 0.3s ease;
2220+
}
2221+
2222+
.contribution-card:hover .contribution-read-more {
2223+
gap: 0.75rem;
2224+
color: #22d3ee;
2225+
}
2226+
2227+
/* Pagination */
2228+
.pagination-controls {
2229+
display: flex;
2230+
align-items: center;
2231+
justify-content: center;
2232+
gap: 1.5rem;
2233+
margin-top: 2.5rem;
2234+
padding: 1rem;
2235+
}
2236+
2237+
.pagination-btn {
2238+
display: flex;
2239+
align-items: center;
2240+
gap: 0.4rem;
2241+
padding: 0.6rem 1.25rem;
2242+
border-radius: 12px;
2243+
border: 1px solid rgba(6, 182, 212, 0.3);
2244+
background: rgba(6, 182, 212, 0.08);
2245+
color: #06b6d4;
2246+
font-size: 0.9rem;
2247+
font-weight: 600;
2248+
cursor: pointer;
2249+
transition: all 0.3s ease;
2250+
}
2251+
2252+
.pagination-btn:hover:not(:disabled) {
2253+
background: rgba(6, 182, 212, 0.2);
2254+
border-color: rgba(6, 182, 212, 0.5);
2255+
box-shadow: 0 4px 15px rgba(6, 182, 212, 0.15);
2256+
}
2257+
2258+
.pagination-btn:disabled {
2259+
opacity: 0.35;
2260+
cursor: not-allowed;
2261+
}
2262+
2263+
.pagination-info {
2264+
color: #94a3b8;
2265+
font-size: 0.9rem;
2266+
font-weight: 500;
2267+
}
2268+
19892269
@media (max-width: 768px) {
19902270
.custom-cursor {
19912271
display: none;
@@ -2098,6 +2378,26 @@ const App = () => {
20982378
.experience-company {
20992379
font-size: 1.1rem;
21002380
}
2381+
2382+
.contribution-card {
2383+
flex-direction: column !important;
2384+
}
2385+
2386+
.contribution-image-wrapper {
2387+
width: 100% !important;
2388+
min-width: unset !important;
2389+
max-width: unset !important;
2390+
height: 200px !important;
2391+
}
2392+
2393+
.contribution-content {
2394+
padding: 1.25rem !important;
2395+
}
2396+
2397+
.pagination-controls {
2398+
flex-direction: row;
2399+
gap: 0.75rem;
2400+
}
21012401
}
21022402
`}</style>
21032403
</div>

src/contributionsData.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Contribution Item Interface
2+
export interface ContributionItem {
3+
id: number;
4+
title: string;
5+
subtitle: string;
6+
date: string;
7+
description: string;
8+
imageUrl: string;
9+
redirectionLink: string;
10+
}
11+
12+
// ============================================================
13+
// 📝 HOW TO ADD A NEW CONTRIBUTION:
14+
// Simply add a new object at the TOP of this array (newest first).
15+
// The listing page will automatically paginate with 10 items/page.
16+
// ============================================================
17+
18+
export const contributions: ContributionItem[] = [
19+
{
20+
id: 1,
21+
title: "A Swift Tour",
22+
subtitle: "Explore the features and syntax of Swift",
23+
date: "March 24, 2026",
24+
description:
25+
"Get started with Apple's powerful and intuitive programming language. This guide provides a comprehensive overview of Swift's core features, from simple values and control flow to functions, closures, and object-oriented patterns. Perfect for developers looking to master the fundamentals of pure Swift through practical, hands-on examples.",
26+
imageUrl: "swift-256x256_2x.png",
27+
redirectionLink:
28+
"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/guidedtour/",
29+
},
30+
];

0 commit comments

Comments
 (0)