Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3ecba1c
Update if-nodejs-pr-testing.yml
Shriya-Chauhan Jul 23, 2025
aebe794
Revert "Update if-nodejs-pr-testing.yml"
Shriya-Chauhan Jul 23, 2025
fa2ecf0
feat: updated case studies page
Shriya-Chauhan Oct 22, 2025
2409193
fix: blog desktop design
Shriya-Chauhan Oct 25, 2025
d2fbef1
fix: fixing mobile view
Shriya-Chauhan Oct 25, 2025
0880914
fix: changing heading sizes
Shriya-Chauhan Oct 25, 2025
d1072e1
fix: update ambassadors page
Shriya-Chauhan Nov 3, 2025
92ab9d0
feat:adding graphic on ambassadors page
Shriya-Chauhan Nov 4, 2025
b629cdf
fix: tsc page draft
Shriya-Chauhan Nov 4, 2025
ea75db5
fix: roadmaps page redesign
Shriya-Chauhan Nov 4, 2025
3f7b4f8
fix: adding events and updates page
Shriya-Chauhan Nov 6, 2025
2ed5445
fix: lint errors
Shriya-Chauhan Nov 6, 2025
c108262
fix:add dark mode to individual blog post
Shriya-Chauhan Nov 7, 2025
5588f6e
fix: lint issues and warnings
Shriya-Chauhan Nov 7, 2025
51cbe93
fix:enhanced tools dashboard page
Shriya-Chauhan Nov 9, 2025
e1a3b59
fix:enhanced blog page card animation
Shriya-Chauhan Nov 9, 2025
f3830ee
fix: community page
Shriya-Chauhan Nov 10, 2025
0e3c2e6
fix:lint errors-s
Shriya-Chauhan Nov 13, 2025
3559d23
fix: added dark mode background colour to body
Shriya-Chauhan Nov 14, 2025
1cb8f7f
fix: dark mode smooth transition
Shriya-Chauhan Nov 14, 2025
957b74b
fix: fixed dashboard
Shriya-Chauhan Nov 16, 2025
dabbd74
fix: lint errors
Shriya-Chauhan Nov 16, 2025
287ebd8
fix: creating a single pagination component
Shriya-Chauhan Nov 16, 2025
3c4da31
fix: dark mode mode for board members page
Shriya-Chauhan Nov 17, 2025
4457207
fix: fixing community icons
Shriya-Chauhan Nov 17, 2025
315b5b2
fix: dark mode to tools pages
Shriya-Chauhan Nov 17, 2025
eb69e83
fix: dark mode to roadmaps arrows
Shriya-Chauhan Nov 17, 2025
c0d6c82
fix: dark mode subscribe section
Shriya-Chauhan Nov 17, 2025
6c1a29c
fix: added date data to blog cards
Shriya-Chauhan Nov 17, 2025
59f8d9f
fix: fixing code rabbit suggestions
Shriya-Chauhan Dec 16, 2025
146c329
refactor: address code review comments
Shriya-Chauhan Dec 16, 2025
8d4bf2f
refactor: remove dymmy data
Shriya-Chauhan Dec 16, 2025
516a6f1
test
Shriya-Chauhan Dec 16, 2025
d679ca2
test
Shriya-Chauhan Dec 16, 2025
8883deb
fixing svgs
Shriya-Chauhan Dec 23, 2025
2138655
fix: adding svgs as seperate files
Shriya-Chauhan Jan 28, 2026
e059cfd
Remove tsconfig.tsbuildinfo from version control
Shriya-Chauhan Jan 28, 2026
e63e396
refactor(community): remove redundant computed fields from types
Shriya-Chauhan Jan 28, 2026
1280672
refactor: seprated data rom components
Shriya-Chauhan Jan 28, 2026
036d98c
fix: add missing isBoardChair to Ambassador interface and fix paginat…
Shriya-Chauhan Jan 28, 2026
033648b
fix: lint errors - import sorting and prettier formatting
Shriya-Chauhan Jan 28, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ deno.lock
cypress/videos
cypress/screenshots
next-env.d.ts
*.tsbuildinfo
2 changes: 1 addition & 1 deletion components/AlgoliaSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ export function SearchButton({ children, indexName = INDEX_NAME, ...props }: ISe
} else {
setChildren(children);
}
}, []);
}, [actionKey, children]);

return (
<button
Expand Down
18 changes: 13 additions & 5 deletions components/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ export default function Calendar({ className = '', size }: ICalendarProps) {
return (
<div
className={twMerge(
'overflow-hidden rounded-md border border-gray-200 bg-white p-4 h-full flex flex-col gap-2',
'overflow-hidden rounded-md border border-gray-200 bg-white dark:bg-dark-background dark:border-border p-4 h-full flex flex-col gap-2',
className
)}
>
<Heading level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.mdSemibold}>
<Heading
level={HeadingLevel.h2}
typeStyle={HeadingTypeStyle.mdSemibold}
className='dark:text-dark-heading text-gray-900'
>
{t('calendar.title')}
</Heading>
<ul>
Expand All @@ -49,8 +53,10 @@ export default function Calendar({ className = '', size }: ICalendarProps) {
<span className='flex-1 self-center text-center'>{moment(event.date).format('D')}</span>
</div>
<div className='grow text-left sm:mt-0 sm:pl-6'>
<h2 className='title-font font-medium text-gray-900 hover:text-gray-500'>{event.title}</h2>
<p className='text-gray-600'>
<h2 className='title-font font-medium text-gray-900 dark:text-white hover:text-gray-500 dark:hover:text-gray-300'>
{event.title}
</h2>
<p className='text-gray-600 dark:text-gray-300'>
{moment(event.date).local().format('LLLL')} UTC
{moment(event.date).local().format('Z')}
</p>
Expand All @@ -60,7 +66,9 @@ export default function Calendar({ className = '', size }: ICalendarProps) {
))}
</ul>
<div className='h-full content-center'>
{!eventsExist && <div className='font-bold text-gray-700 lg:pb-8'>{t('calendar.noMeetingsMessage')}</div>}
{!eventsExist && (
<div className='font-bold text-gray-700 dark:text-gray-300 lg:pb-8'>{t('calendar.noMeetingsMessage')}</div>
)}
<div className='sm:pt-0 md:pt-2 lg:pb-8 lg:pt-0' data-testid='Calendar-button'>
<GoogleCalendarButton href={CALENDAR_URL} text={t('calendar.viewCalendarBtn')} />
</div>
Expand Down
31 changes: 19 additions & 12 deletions components/CaseStudyCard.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import Link from 'next/link';
import React from 'react';

import type { ICaseStudies } from '@/types/post';
import { ParagraphTypeStyle } from '@/types/typography/Paragraph';

import Button from './buttons/Button';
import Paragraph from './typography/Paragraph';

interface ICaseStudyCardProps {
studies?: ICaseStudies;
}

/**
* @description A component that displays a list of case studies in a card format
* @description Displays case studies in a card grid layout
* @param {ICaseStudies} props.studies - The list of case studies to display
*/
export default function CaseStudyCard({ studies = [] }: ICaseStudyCardProps) {
Expand All @@ -19,21 +21,26 @@ export default function CaseStudyCard({ studies = [] }: ICaseStudyCardProps) {
}

return (
<div className='flex flex-wrap justify-center gap-3 pt-10 lg:gap-8 lg:text-center'>
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-8'>
{studies.map((study, index) => (
<a key={index} className='lg:w-[30%]' href={`casestudies/${study.id}`}>
<div
className='h-full min-h-[300px] max-w-sm overflow-hidden rounded-md border border-gray-200 bg-white p-4'
data-testid='CaseStudyCard-main'
>
<span className='mr-2'>
<img className='m-auto h-16' src={study.company.logo} alt={study.company.name} />
</span>
<Paragraph typeStyle={ParagraphTypeStyle.md} className='my-4'>
<Link key={index} href={`/casestudies/${study.id}`} className='group block h-full'>
<div className='h-full bg-white dark:bg-dark-background border border-gray-200 dark:border-gray-700 rounded-2xl p-6 transition-all duration-300 hover:shadow-xl hover:-translate-y-1'>
<div className='flex items-center justify-center h-20 mb-6'>
<img src={study.company.logo} alt={study.company.name} className='max-h-16 max-w-full object-contain' />
</div>

<Paragraph typeStyle={ParagraphTypeStyle.md} className='text-gray-600 dark:text-gray-400 mb-6 line-clamp-4'>
{study.company.description}
</Paragraph>

<div className='mt-auto'>
<Button
text='Read case study →'
className='w-full bg-primary-500 hover:bg-primary-600 text-white group-hover:bg-primary-600 transition-colors'
/>
</div>
</div>
</a>
</Link>
))}
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions components/CaseTOC.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ export default function CaseTOC({ className, cssBreakingPoint = 'xl', toc }: Cas
open && 'mb-4'
} flex-1 text-primary-500 font-medium uppercase tracking-wide text-sm font-sans antialiased ${
cssBreakingPoint === 'xl'
? 'xl:mb-4 xl:text-xs xl:text-gray-900 xl:font-bold'
: 'lg:mb-4 lg:text-xs lg:text-gray-900 lg:font-bold'
? 'xl:mb-4 xl:text-xs xl:text-gray-900 dark:text-gray-300 xl:font-bold'
: 'lg:mb-4 lg:text-xs lg:text-gray-900 dark:text-gray-300 lg:font-bold'
}`
)}
>
Expand Down
268 changes: 268 additions & 0 deletions components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
import React, { useState } from 'react';

interface PaginationProps {
currentPage: number;
totalPages: number;
onPageChange: (page: number) => void;
showGoToPage?: boolean;
variant?: 'default' | 'simple' | 'compact';
className?: string;
}

/**
* @description Unified Pagination component with customizable display modes and "Go to page" dropdown
* @param {PaginationProps} props - The props for the component
* @param {number} props.currentPage - The current active page number
* @param {number} props.totalPages - The total number of pages
* @param {(page: number) => void} props.onPageChange - Callback function when page changes
* @param {boolean} [props.showGoToPage=true] - Whether to show the "Go to page" dropdown
* @param {'default' | 'simple' | 'compact'} [props.variant='default'] - Display variant
* @param {string} [props.className=''] - Additional CSS classes
*/
export default function Pagination({
currentPage,
totalPages,
onPageChange,
showGoToPage = true,
variant = 'default',
className = ''
}: PaginationProps) {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [dropdownDirection, setDropdownDirection] = useState<'down' | 'up'>('down');

if (totalPages <= 1) return null;

const handlePageClick = (page: number) => {
if (page >= 1 && page <= totalPages && page !== currentPage) {
onPageChange(page);
}
};

const renderPageNumbers = () => {
if (variant === 'simple') {
// Simple variant: show all page numbers
return Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<button
key={page}
onClick={() => handlePageClick(page)}
className={`px-4 py-2 rounded-lg transition-colors ${
currentPage === page
? 'bg-primary-500 text-white'
: 'border border-gray-300 dark:border-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
}`}
>
{page}
</button>
));
}

if (variant === 'compact') {
// Compact variant: show current ±1, first, last with ellipsis
const pages: (number | 'ellipsis')[] = [];

if (totalPages <= 7) {
// Show all pages if 7 or fewer
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
} else {
// Always show first page
pages.push(1);

if (currentPage > 3) {
pages.push('ellipsis');
}

// Show pages around current
const start = Math.max(2, currentPage - 1);
const end = Math.min(totalPages - 1, currentPage + 1);

for (let i = start; i <= end; i++) {
if (i !== 1 && i !== totalPages) {
pages.push(i);
}
}

if (currentPage < totalPages - 2) {
pages.push('ellipsis');
}

// Always show last page
if (totalPages > 1) {
pages.push(totalPages);
}
}

return pages.map((page, index) => {
if (page === 'ellipsis') {
return (
<span key={`ellipsis-${index}`} className='px-2 text-gray-600 dark:text-gray-400'>
...
</span>
);
}

return (
<button
key={page}
onClick={() => handlePageClick(page)}
className={`px-3 py-2 rounded-lg transition-colors ${
currentPage === page
? 'bg-primary-500 text-white'
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'
}`}
>
{page}
</button>
);
});
}

// Default variant: show up to 5 pages with ellipsis
const maxVisible = 5;
let startPage: number;
let endPage: number;

if (totalPages <= maxVisible) {
startPage = 1;
endPage = totalPages;
} else if (currentPage <= 3) {
startPage = 1;
endPage = maxVisible;
} else if (currentPage >= totalPages - 2) {
startPage = totalPages - maxVisible + 1;
endPage = totalPages;
} else {
startPage = currentPage - 2;
endPage = currentPage + 2;
}

const pages: React.JSX.Element[] = [];

for (let i = startPage; i <= endPage; i++) {
pages.push(
<button
key={i}
onClick={() => handlePageClick(i)}
className={`px-4 py-2 rounded-lg transition-colors ${
currentPage === i
? 'bg-primary-500 text-white'
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'
}`}
>
{i}
</button>
);
}

return (
<>
{pages}
{totalPages > maxVisible && endPage < totalPages && (
<span className='px-2 text-gray-600 dark:text-gray-400'>...</span>
)}
</>
);
};

const handleDropdownToggle = (e: React.MouseEvent<HTMLButtonElement>) => {
const button = e.currentTarget;
const rect = button.getBoundingClientRect();
const spaceBelow = window.innerHeight - rect.bottom;
const spaceAbove = rect.top;
const dropdownHeight = 140; // max height of dropdown

// Open upward if not enough space below but enough space above
if (spaceBelow < dropdownHeight && spaceAbove > spaceBelow) {
setDropdownDirection('up');
} else {
setDropdownDirection('down');
}
setIsDropdownOpen(!isDropdownOpen);
};

return (
<div className={`flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-2 ${className}`}>
<div className='flex items-center gap-2'>
<button
onClick={() => handlePageClick(currentPage - 1)}
disabled={currentPage === 1}
className='px-3 py-2 rounded-lg text-gray-600 dark:text-gray-400 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors'
aria-label='Previous page'
>
</button>

{renderPageNumbers()}

<button
onClick={() => handlePageClick(currentPage + 1)}
disabled={currentPage === totalPages}
className='px-3 py-2 rounded-lg text-gray-600 dark:text-gray-400 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors'
aria-label='Next page'
>
</button>
</div>

{showGoToPage && (
<div className='relative flex items-center gap-2 sm:ml-4'>
<span className='text-sm text-gray-600 dark:text-gray-400'>Go to page</span>
<div className='relative'>
<button
onClick={handleDropdownToggle}
onBlur={() => setTimeout(() => setIsDropdownOpen(false), 200)}
className='px-2 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-white text-sm w-[60px] sm:w-[65px] md:w-[70px] text-left flex items-center justify-between gap-1 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2'
aria-label='Select page'
>
<span className='flex-1 text-left text-gray-900 dark:text-white'>{currentPage}</span>
{(() => {
let rotationClass = '';

if (isDropdownOpen) {
rotationClass = dropdownDirection === 'up' ? 'rotate-0' : 'rotate-180';
}

return (
<svg
className={`w-4 h-4 flex-shrink-0 text-gray-600 dark:text-gray-400 transition-transform ${rotationClass}`}
fill='none'
stroke='currentColor'
viewBox='0 0 24 24'
>
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M19 9l-7 7-7-7' />
</svg>
);
})()}
</button>

{isDropdownOpen && (
<div
className={`absolute left-0 w-[60px] sm:w-[65px] md:w-[70px] bg-white dark:bg-gray-800
border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg max-h-[140px]
overflow-y-auto z-50 ${dropdownDirection === 'up' ? 'bottom-full mb-1' : 'top-full mt-1'}`}
>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<button
key={page}
onClick={() => {
handlePageClick(page);
setIsDropdownOpen(false);
}}
className={`w-full px-3 py-2 text-left text-sm transition-colors ${
currentPage === page
? 'bg-primary-500 text-white hover:bg-primary-600'
: 'text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700'
}`}
>
{page}
</button>
))}
</div>
)}
</div>
</div>
)}
</div>
);
}
Loading