diff --git a/.gitignore b/.gitignore index d673fa71784d..876ea0549dab 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ deno.lock cypress/videos cypress/screenshots next-env.d.ts +*.tsbuildinfo \ No newline at end of file diff --git a/components/AlgoliaSearch.tsx b/components/AlgoliaSearch.tsx index 06d37889c473..6117396c7398 100644 --- a/components/AlgoliaSearch.tsx +++ b/components/AlgoliaSearch.tsx @@ -315,7 +315,7 @@ export function SearchButton({ children, indexName = INDEX_NAME, ...props }: ISe } else { setChildren(children); } - }, []); + }, [actionKey, children]); return ( + )); + } + + 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 ( + + ... + + ); + } + + return ( + + ); + }); + } + + // 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( + + ); + } + + return ( + <> + {pages} + {totalPages > maxVisible && endPage < totalPages && ( + ... + )} + + ); + }; + + const handleDropdownToggle = (e: React.MouseEvent) => { + 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 ( +
+
+ + + {renderPageNumbers()} + + +
+ + {showGoToPage && ( +
+ Go to page +
+ + + {isDropdownOpen && ( +
+ {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( + + ))} +
+ )} +
+
+ )} +
+ ); +} diff --git a/components/community/Card.tsx b/components/community/Card.tsx index 00f4f0eb9ae8..02305b64175e 100644 --- a/components/community/Card.tsx +++ b/components/community/Card.tsx @@ -51,23 +51,27 @@ export default function Card({ return (
-
+
{icon} {tagline}
- + {heading}
- + {description}
-
+
@@ -77,23 +81,30 @@ export default function Card({ return (
{icon} {tagline}
- + {heading}
- {description} + + {description} +
diff --git a/components/community/FeatureCard.tsx b/components/community/FeatureCard.tsx new file mode 100644 index 000000000000..2e1663c6e7a5 --- /dev/null +++ b/components/community/FeatureCard.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; +import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; + +import Heading from '../typography/Heading'; +import Paragraph from '../typography/Paragraph'; + +interface FeatureCardProps { + icon: React.ReactNode; + title: string; + description: string; +} + +/** + * @description FeatureCard component for displaying a feature card with icon, title and description + * @param {FeatureCardProps} props - Props for the FeatureCard component + */ +export default function FeatureCard({ icon, title, description }: FeatureCardProps) { + return ( +
+
+ {icon} +
+ + {title} + + + {description} + +
+ ); +} diff --git a/components/community/Header.tsx b/components/community/Header.tsx index f531ce17f174..2aeb159da97d 100644 --- a/components/community/Header.tsx +++ b/components/community/Header.tsx @@ -25,7 +25,7 @@ export default function Header({ className = '' }: HeaderProps) { AsyncAPI Community
- + Welcome to the
AsyncAPI Community @@ -37,7 +37,7 @@ export default function Header({ className = '' }: HeaderProps) { level={HeadingLevel.h2} typeStyle={HeadingTypeStyle.bodyMd} textColor='text-gray-700' - className='text-slate-500' + className='text-slate-500 dark:text-gray-300' > We're an OSS community that's passionate about AsyncAPI. Join us in building the future of Event Driven APIs by asking questions, sharing ideas, and building connections. diff --git a/components/community/HomeCard.tsx b/components/community/HomeCard.tsx index de34b46cf42a..12f006ac1b47 100644 --- a/components/community/HomeCard.tsx +++ b/components/community/HomeCard.tsx @@ -27,22 +27,27 @@ interface HomeCardProps { */ export default function HomeCards({ headline, title, description, btnText, link, className }: HomeCardProps) { return ( -
+
- + {headline}
- + {title} {description} diff --git a/components/community/TSCMemberCard.tsx b/components/community/TSCMemberCard.tsx new file mode 100644 index 000000000000..da8dba13688c --- /dev/null +++ b/components/community/TSCMemberCard.tsx @@ -0,0 +1,138 @@ +import React from 'react'; + +import type { Tsc } from '@/types/pages/community/Community'; + +interface TSCMemberCardProps { + member: Tsc; +} + +/** + * @param {{ className?: string }} props - Props for the GitHub icon + * @returns {React.JSX.Element} GitHub icon component + */ +function GitHubIcon({ className = '' }: { className?: string }): React.JSX.Element { + return ( + + + + ); +} + +/** + * @param {{ className?: string }} props - Props for the Twitter icon + * @returns {React.JSX.Element} Twitter icon component + */ +function TwitterIcon({ className = '' }: { className?: string }): React.JSX.Element { + return ( + + + + ); +} + +/** + * @param {{ className?: string }} props - Props for the LinkedIn icon + * @returns {React.JSX.Element} LinkedIn icon component + */ +function LinkedInIcon({ className = '' }: { className?: string }): React.JSX.Element { + return ( + + + + ); +} + +/** + * @description Component for displaying a TSC member card + * @param {TSCMemberCardProps} props - The props for TSC member card + * @param {Tsc} props.member - The TSC member data + */ +export default function TSCMemberCard({ member }: TSCMemberCardProps) { + return ( +
+
+ {member.name} +
+

{member.name}

+

+ {member.availableForHire ? 'Available for hire' : member.company || 'Individual Member'} +

+
+ {member.availableForHire && ( + + Available + + )} +
+ + {member.repos && Array.isArray(member.repos) && member.repos.length > 0 && ( +
+

Maintainer of:

+
+ {member.repos.slice(0, 2).map((repoName: string) => ( + + {repoName} + + ))} + {member.repos.length > 2 && ( + + +{member.repos.length - 2} + + )} +
+
+ )} + +
+ {member.github && ( + + + + )} + {member.twitter && ( + + + + )} + {member.linkedin && ( + + + + )} +
+
+ ); +} diff --git a/components/community/ToolingCard.tsx b/components/community/ToolingCard.tsx new file mode 100644 index 000000000000..fa7cf6df41fb --- /dev/null +++ b/components/community/ToolingCard.tsx @@ -0,0 +1,124 @@ +import React from 'react'; + +interface ToolingCardProps { + name: string; + description: string; + badge?: string; + language: string; + link?: string; + isShuffling?: boolean; +} + +/** + * @description ToolingCard component for displaying AsyncAPI tools with stacked, rotated effect + */ +export default function ToolingCard({ name, description, badge, language, link, isShuffling }: ToolingCardProps) { + const CardContent = ( +
+ {/* Stacked Cards Behind - Positioned on LEFT side */} +
+
+ + {/* Main Card */} +
+ {/* Background Vector Graphics - Matching exact design */} +
+ {/* Top Background Layers - Gray to Purple gradient triangular shapes */} +
+
+
+ + {/* Bottom Large Fan/Petal Shape - Blue gradient */} +
+ +
+
+ + {/* Content */} +
+ {/* Badge */} + {badge && ( +
+ {badge} +
+ )} + + {/* Tool Name */} +

{name}

+ + {/* Description */} +

{description}

+ + {/* Language Tag */} +
+ {language} +
+
+
+
+ ); + + if (link) { + return ( + + {CardContent} + + ); + } + + return CardContent; +} diff --git a/components/community/ToolingsShowcase.tsx b/components/community/ToolingsShowcase.tsx new file mode 100644 index 000000000000..9c60dc99247f --- /dev/null +++ b/components/community/ToolingsShowcase.tsx @@ -0,0 +1,118 @@ +'use client'; + +import Link from 'next/link'; +import React, { useState } from 'react'; + +import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; +import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; + +import communityToolingsShowcase from '../../config/community-toolings-showcase.json'; +import Heading from '../typography/Heading'; +import Paragraph from '../typography/Paragraph'; +import ToolingCard from './ToolingCard'; + +interface ToolData { + name: string; + displayName: string; + description: string; + language: string; + link: string; + color: string; + badge?: string; +} + +const toolsData: Record = communityToolingsShowcase as Record; + +/** + * @description ToolingsShowcase component for displaying AsyncAPI tools collection + */ +export default function ToolingsShowcase() { + const [selectedTool, setSelectedTool] = useState('java-asyncapi'); + const [isShuffling, setIsShuffling] = useState(false); + + const currentTool = toolsData[selectedTool]; + + const handleToolChange = (toolName: string) => { + if (toolName === selectedTool) return; + + // Start shuffle animation + setIsShuffling(true); + + // Change tool after shuffle animation starts + setTimeout(() => { + setSelectedTool(toolName); + setIsShuffling(false); + }, 300); + }; + + return ( +
+
+
+ {/* Left Content */} +
+
+ + Checkout Our Collection of Toolings + + + Discover various AsyncAPI tools to optimize your journey! These tools are made by the community, for the + community. + +
+ + {/* Explore Tools Button */} + + Explore Tools + + + {/* Tool Tags */} +
+ {Object.values(toolsData).map((tool) => ( + + ))} +
+
+ + {/* Right Card Showcase */} +
+ +
+
+
+
+ ); +} diff --git a/components/community/UpcomingEventsSection.tsx b/components/community/UpcomingEventsSection.tsx new file mode 100644 index 000000000000..0afa8965569a --- /dev/null +++ b/components/community/UpcomingEventsSection.tsx @@ -0,0 +1,192 @@ +import React, { useState } from 'react'; + +import EventPostItem from '@/components/navigation/EventPostItem'; +import Heading from '@/components/typography/Heading'; +import Paragraph from '@/components/typography/Paragraph'; +import meetings from '@/config/meetings.json'; +import type { Event } from '@/types/pages/community/Community'; +import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; +import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; +import { getEvents } from '@/utils/staticHelpers'; + +/** + * @description UpcomingEventsSection component for displaying upcoming events with pagination + */ +export default function UpcomingEventsSection() { + const [events] = useState(getEvents(meetings)); + const [currentPage, setCurrentPage] = useState(1); + const eventsPerPage = 6; + + // Calculate pagination + const indexOfLastEvent = currentPage * eventsPerPage; + const indexOfFirstEvent = indexOfLastEvent - eventsPerPage; + const currentEvents = events.slice(indexOfFirstEvent, indexOfLastEvent); + const totalPages = Math.ceil(events.length / eventsPerPage); + + const goToPage = (pageNumber: number) => { + setCurrentPage(pageNumber); + // Scroll to top of events section + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + const renderPageNumbers = () => { + const pageNumbers = []; + const maxVisiblePages = 5; + let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2)); + const endPage = Math.min(totalPages, startPage + maxVisiblePages - 1); + + if (endPage - startPage + 1 < maxVisiblePages) { + startPage = Math.max(1, endPage - maxVisiblePages + 1); + } + + // Previous button + pageNumbers.push( + + ); + + // First page + if (startPage > 1) { + pageNumbers.push( + + ); + if (startPage > 2) { + pageNumbers.push( + + ... + + ); + } + } + + // Page numbers + for (let i = startPage; i <= endPage; i++) { + pageNumbers.push( + + ); + } + + // Last page + if (endPage < totalPages) { + if (endPage < totalPages - 1) { + pageNumbers.push( + + ... + + ); + } + pageNumbers.push( + + ); + } + + // Next button + pageNumbers.push( + + ); + + return pageNumbers; + }; + + return ( +
+
+ + Upcoming Events + + + Don't miss out on these amazing community gatherings + +
+ + {/* Events Grid */} +
+ {!currentEvents || currentEvents.length === 0 ? ( +
+ + No Events. Check back later! + +
+ ) : ( +
    + {currentEvents.map((event: Event, index: number) => { + return ; + })} +
+ )} +
+ + {/* Pagination */} + {totalPages > 1 && ( +
+
{renderPageNumbers()}
+
+ Go to page + +
+
+ )} +
+ ); +} diff --git a/components/dashboard/GoodFirstIssues.tsx b/components/dashboard/GoodFirstIssues.tsx index 29cec9a3a911..9183024fce9f 100644 --- a/components/dashboard/GoodFirstIssues.tsx +++ b/components/dashboard/GoodFirstIssues.tsx @@ -61,11 +61,26 @@ export default function GoodFirstIssues({ issues }: GoodFirstIssuesProps) { return ( - Good First Issues - +
+
+ + + + Good First Issues + +
} data={filteredIssues.slice(0, 24)} - className='grow' + className='flex-1' listClassName='lg:grid-cols-2' /> ); diff --git a/components/dashboard/GoodFirstIssuesTip.tsx b/components/dashboard/GoodFirstIssuesTip.tsx index 4a8a2197160a..19b6233312d3 100644 --- a/components/dashboard/GoodFirstIssuesTip.tsx +++ b/components/dashboard/GoodFirstIssuesTip.tsx @@ -20,6 +20,7 @@ export default function GoodFirstIssuesTip() { src='/img/illustrations/icons/tip-icon.svg' data-testid='GoodFirstIssuesTip-hover-icon' alt='Tooltip' + className='dark:invert' /> {open && ( @@ -31,12 +32,31 @@ export default function GoodFirstIssuesTip() { left: x ?? '' }} > -
-

Is this your first contribution?

-

- The issues in this column are perfect for you! These issues are of low-complexity and should be a quick - commit. Thanks for your help, and welcome! -

+
+
+
+ + + +
+
+

Is this your first contribution?

+

+ The issues in this column are perfect for you! These issues are of low-complexity and should be a + quick commit. Thanks for your help, and welcome! +

+
+
)} diff --git a/components/dashboard/Header.tsx b/components/dashboard/Header.tsx index 03b3e2b6b741..0d49c1491604 100644 --- a/components/dashboard/Header.tsx +++ b/components/dashboard/Header.tsx @@ -9,21 +9,21 @@ import SlackButton from '../buttons/SlackButton'; */ export default function Header() { return ( -
+
-
+

Dashboard

-

+

Visualize our progress. Get involved.{' '}

-
+
{open && (
0 ? `left-[${x}px]` : 'left-[14px]'}`} + className='z-50' style={{ + position: strategy, top: y ?? '', - left: x && x > 0 ? x : '' + left: x ?? '', + right: placement === 'bottom-end' ? '1rem' : undefined }} data-testid='Filter-menu' > -
-
-

Filter Issues

-
-
-
-
BY REPOSITORY
- +
+
+
+ BY REPOSITORY +
+ +
diff --git a/components/dashboard/table/Row.tsx b/components/dashboard/table/Row.tsx index 8b03be87263c..85656c016bd3 100644 --- a/components/dashboard/table/Row.tsx +++ b/components/dashboard/table/Row.tsx @@ -15,54 +15,66 @@ interface RowProps { export default function Row({ item }: RowProps) { return (
  • -
    -
    -
    - - + +
    +
    +
    + {item.title} - - {item.labels && item?.labels?.length > 0 && ( -
    - {item.labels.map((label) => ( - - {label.name} - - ))} + {item.labels && item?.labels?.length > 0 && ( +
    + {item.labels.map((label) => ( + + {label.name} + + ))} +
    + )} +
    +
    +
    + + +
    - )} +
    - - arrow icon -
    -
    +
  • ); } diff --git a/components/dashboard/table/Table.tsx b/components/dashboard/table/Table.tsx index f5153fabc979..1b81bc374d48 100644 --- a/components/dashboard/table/Table.tsx +++ b/components/dashboard/table/Table.tsx @@ -22,34 +22,68 @@ interface TableProps { */ export default function Table({ title, data, className, listClassName }: TableProps) { return ( -
    -
    -

    {title}

    +
    +
    +

    {title}

    -
    +
    {data.length === 0 && ( -
    -

    There aren't any good first issues open for the given repository and area at the moment.

    -
      -
    • - Join our{' '} - - Slack - {' '} - to seek help. -
    • -
    • - In the #11_contributing channel, call out the - maintainers that you want to work with. Ask them if there are any issues you could solve. You know who - these people are from CODEOWNERS file in each repo. -
    • -
    • - If there is no response, you need to look for a different issue from different repository. -
    • -
    +
    +
    +
    + + + +
    +

    + There aren't any good first issues open for the given repository and area at the moment. +

    +
      +
    • + + + Join our{' '} + + Slack + {' '} + to seek help. + +
    • +
    • + + + In the #11_contributing{' '} + channel, call out the maintainers that you want to work with. Ask them if there are any issues you + could solve. You know who these people are from{' '} + CODEOWNERS file in each repo. + +
    • +
    • + + + If there is no response, you need to look for a different issue from different repository. + +
    • +
    +
    )} -
      +
        {data.map((item) => ( ))} diff --git a/components/footer/FooterList.ts b/components/footer/FooterList.ts index 32839f49645e..c81b6272bd33 100644 --- a/components/footer/FooterList.ts +++ b/components/footer/FooterList.ts @@ -73,3 +73,33 @@ export const initiativeLinks: InitiativeLink[] = [ url: '/about#faqs' } ]; + +export const COMMUNITY_URLS = { + GITHUB: { + BASE: 'https://github.com/asyncapi/community', + AMBASSADOR_PROGRAM: + 'https://github.com/asyncapi/community/blob/master/docs/020-governance-and-policies/AMBASSADOR_PROGRAM.md', + AMBASSADOR_ORGANIZATION: + 'https://github.com/asyncapi/community/blob/master/AMBASSADOR_ORGANIZATION.md#are-you-interested-in-becoming-an-official-asyncapi-ambassador', + TSC_MEMBERSHIP: 'https://github.com/asyncapi/community/blob/master/TSC_MEMBERSHIP.md', + CONTRIBUTING: 'https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md' + }, + YOUTUBE: { + BASE: 'https://www.youtube.com/asyncapi', + AMBASSADOR_VIDEO_ID: '3rg_7hIb9PQ' + }, + BLOG: { + AMBASSADOR_PROGRAM: 'https://www.asyncapi.com/blog/asyncapi-ambassador-program' + } +} as const; + +export const AMBASSADORS_CONFIG = { + POSTS_PER_PAGE: 6, + YOUTUBE_VIDEO_ID: '3rg_7hIb9PQ' +} as const; + +export const SOCIAL_MEDIA_BASE_URLS = { + GITHUB: 'https://www.github.com', + LINKEDIN: 'https://www.linkedin.com/in', + TWITTER: 'https://www.twitter.com' +} as const; diff --git a/components/form/Select.tsx b/components/form/Select.tsx index 12d150fe6823..b4a60beac9c5 100644 --- a/components/form/Select.tsx +++ b/components/form/Select.tsx @@ -31,12 +31,12 @@ export default function Select({ className = '', onChange = () => {}, options, s data-testid='Select-form' onChange={handleOnChange} className={twMerge( - `form-select h-full pl-2 pr-8 inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500 ${className}` + `form-select h-full pl-2 pr-8 inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-700 shadow-sm px-4 py-2 bg-white dark:bg-dark-card text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-dark-background focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 dark:focus:ring-offset-dark-background focus:ring-primary-500 ${className}` )} value={selected} > {options.map((option, index) => ( - ))} diff --git a/components/icons/Ambassador.tsx b/components/icons/Ambassador.tsx index 196986141f20..ed5ba51c300c 100644 --- a/components/icons/Ambassador.tsx +++ b/components/icons/Ambassador.tsx @@ -10,7 +10,7 @@ export default function IconAmbassador({ className = '' }) { width='11' height='7' viewBox='0 0 512 512' - fill='#000000' + fill='currentColor' xmlns='http://www.w3.org/2000/svg' className={className} > diff --git a/components/icons/ArrowRightStroke.tsx b/components/icons/ArrowRightStroke.tsx new file mode 100644 index 000000000000..a6cd8db74b86 --- /dev/null +++ b/components/icons/ArrowRightStroke.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for asyncapi website + */ +export default function IconArrowRightStroke(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/components/icons/BadgeCheckmark.tsx b/components/icons/BadgeCheckmark.tsx new file mode 100644 index 000000000000..dd4096918409 --- /dev/null +++ b/components/icons/BadgeCheckmark.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for asyncapi website + */ +export default function IconBadgeCheckmark(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/components/icons/Book.tsx b/components/icons/Book.tsx new file mode 100644 index 000000000000..2b4e7adf58d4 --- /dev/null +++ b/components/icons/Book.tsx @@ -0,0 +1,15 @@ +/** + * @description Icon component for Book + */ +export default function IconBook({ className = '' }) { + return ( + + + + ); +} diff --git a/components/icons/Clipboard.tsx b/components/icons/Clipboard.tsx index c0add09acf31..4d7d266d0807 100644 --- a/components/icons/Clipboard.tsx +++ b/components/icons/Clipboard.tsx @@ -1,13 +1,15 @@ -import React from 'react'; - -/* eslint-disable max-len */ /** - * @description Icons for asyncapi website + * @description Icon component for Clipboard */ export default function IconClipboard({ className = '' }) { return ( - - + + + ); } diff --git a/components/icons/CodeBrackets.tsx b/components/icons/CodeBrackets.tsx new file mode 100644 index 000000000000..2562a8efece9 --- /dev/null +++ b/components/icons/CodeBrackets.tsx @@ -0,0 +1,14 @@ +/** + * @description Icon component for Code Brackets + */ +export default function IconCodeBrackets({ className = '' }) { + return ( + + + + ); +} diff --git a/components/icons/CommunityDriven.tsx b/components/icons/CommunityDriven.tsx new file mode 100644 index 000000000000..8a98f6f7c9ab --- /dev/null +++ b/components/icons/CommunityDriven.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for asyncapi website + */ +export default function IconCommunityDriven(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/components/icons/Contributing.tsx b/components/icons/Contributing.tsx index 090a7da3fa9d..f933db652c3c 100644 --- a/components/icons/Contributing.tsx +++ b/components/icons/Contributing.tsx @@ -14,7 +14,7 @@ export default function IconContributing({ className = '' }) { viewBox='0 0 29.07 26.59' > - + {' '} diff --git a/components/icons/Document.tsx b/components/icons/Document.tsx new file mode 100644 index 000000000000..1aa995d6ec7d --- /dev/null +++ b/components/icons/Document.tsx @@ -0,0 +1,14 @@ +/** + * @description Icon component for Document + */ +export default function IconDocument({ className = '' }) { + return ( + + + + ); +} diff --git a/components/icons/ExternalLink.tsx b/components/icons/ExternalLink.tsx new file mode 100644 index 000000000000..4cb1edb58250 --- /dev/null +++ b/components/icons/ExternalLink.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for asyncapi website + */ +export default function IconExternalLink(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/components/icons/Gear.tsx b/components/icons/Gear.tsx new file mode 100644 index 000000000000..4bafcc6abd12 --- /dev/null +++ b/components/icons/Gear.tsx @@ -0,0 +1,16 @@ +/** + * @description Icon component for Gear + */ +export default function IconGear({ className = '' }) { + return ( + + + + + ); +} diff --git a/components/icons/GithubOrganization.tsx b/components/icons/GithubOrganization.tsx index ab16310c397b..b4dc5c0e85a1 100644 --- a/components/icons/GithubOrganization.tsx +++ b/components/icons/GithubOrganization.tsx @@ -14,7 +14,7 @@ export default function IconGithubOrganization({ className = '' }) { viewBox='0 0 24.63 26.6' > - + - + + ); } diff --git a/components/icons/Lightning.tsx b/components/icons/Lightning.tsx new file mode 100644 index 000000000000..af9809b44402 --- /dev/null +++ b/components/icons/Lightning.tsx @@ -0,0 +1,10 @@ +/** + * @description Icon component for Lightning + */ +export default function IconLightning({ className = '' }) { + return ( + + + + ); +} diff --git a/components/icons/Microphone.tsx b/components/icons/Microphone.tsx new file mode 100644 index 000000000000..3fbd2d7134a3 --- /dev/null +++ b/components/icons/Microphone.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +/** + * @description Icon component for Microphone + */ +interface IconMicrophoneProps { + className?: string; +} + +/** + * @param {IconMicrophoneProps} props - The props for the Microphone icon + * @returns {React.JSX.Element} The Microphone icon component + */ +export default function IconMicrophone({ className = '' }: IconMicrophoneProps): React.JSX.Element { + return ( + + ); +} diff --git a/components/icons/Newsroom.tsx b/components/icons/Newsroom.tsx index e744b9d68081..32f9bffac05b 100644 --- a/components/icons/Newsroom.tsx +++ b/components/icons/Newsroom.tsx @@ -4,7 +4,7 @@ import React from 'react'; /** * @description Icons for asyncapi website */ -export default function IconNewsroom(className = '') { +export default function IconNewsroom({ className = '' }: { className?: string } = {}) { return ( - + ); diff --git a/components/icons/OpenSource.tsx b/components/icons/OpenSource.tsx new file mode 100644 index 000000000000..710aa0242bc9 --- /dev/null +++ b/components/icons/OpenSource.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for asyncapi website + */ +export default function IconOpenSource(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/components/icons/Play.tsx b/components/icons/Play.tsx new file mode 100644 index 000000000000..ef0a68090abf --- /dev/null +++ b/components/icons/Play.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +/** + * @description Icon component for Play + */ +interface IconPlayProps { + className?: string; +} + +/** + * @param {IconPlayProps} props - The props for the Play icon + * @returns {React.JSX.Element} The Play icon component + */ +export default function IconPlay({ className = '' }: IconPlayProps): React.JSX.Element { + return ( + + + + ); +} diff --git a/components/icons/Plus.tsx b/components/icons/Plus.tsx new file mode 100644 index 000000000000..526c0c0f4109 --- /dev/null +++ b/components/icons/Plus.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +/** + * @description Icon component for Plus + */ +interface IconProps { + className?: string; +} + +/** + * @param {IconProps} props - The props for the Plus icon + * @returns {React.JSX.Element} The Plus icon component + */ +export default function IconPlus({ className = '' }: IconProps): React.JSX.Element { + return ( + + + + ); +} diff --git a/components/icons/ProductionReady.tsx b/components/icons/ProductionReady.tsx new file mode 100644 index 000000000000..7a34b9c707c0 --- /dev/null +++ b/components/icons/ProductionReady.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +/* eslint-disable max-len */ +/** + * @description Icons for asyncapi website + */ +export default function IconProductionReady(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/components/icons/Star.tsx b/components/icons/Star.tsx new file mode 100644 index 000000000000..15d7bd77bb2c --- /dev/null +++ b/components/icons/Star.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +/** + * @description Icon component for Star + */ +interface IconStarProps { + className?: string; +} + +/** + * @param {IconStarProps} props - The props for the Star icon + * @returns {React.JSX.Element} The Star icon component + */ +export default function IconStar({ className = '' }: IconStarProps): React.JSX.Element { + return ( + + + + ); +} diff --git a/components/icons/TSC.tsx b/components/icons/TSC.tsx index ade1c0fdacbb..075e2f402f60 100644 --- a/components/icons/TSC.tsx +++ b/components/icons/TSC.tsx @@ -14,7 +14,7 @@ export default function IconTSC({ className = '' }) { viewBox='0 0 28.15 18.69' > - + + + + ); +} diff --git a/components/icons/UsersGroup.tsx b/components/icons/UsersGroup.tsx new file mode 100644 index 000000000000..4eb15ae6ff11 --- /dev/null +++ b/components/icons/UsersGroup.tsx @@ -0,0 +1,10 @@ +/** + * @description Icon component for Users Group + */ +export default function IconUsersGroup({ className = '' }) { + return ( + + + + ); +} diff --git a/components/icons/Video.tsx b/components/icons/Video.tsx new file mode 100644 index 000000000000..fb546066f318 --- /dev/null +++ b/components/icons/Video.tsx @@ -0,0 +1,10 @@ +/** + * @description Icon component for Video + */ +export default function IconVideo({ className = '' }) { + return ( + + + + ); +} diff --git a/components/layout/BlogLayout.tsx b/components/layout/BlogLayout.tsx index a939696399d3..5266f413b7d3 100644 --- a/components/layout/BlogLayout.tsx +++ b/components/layout/BlogLayout.tsx @@ -35,61 +35,67 @@ export default function BlogLayout({ post, children }: IBlogLayoutProps) { } return ( - - - - -
        -
        -

        - {post.title} -

        -
        -
        - -
        -
        -

        - - {post.authors - .map((author, index) => - author.link ? ( - - {author.name} - - ) : ( - author.name +

        + + + + +
        +
        +

        + {post.title} +

        +
        +
        + +
        +
        +

        + + {post.authors + .map((author, index) => + author.link ? ( + + {author.name} + + ) : ( + author.name + ) ) - ) - .reduce((prev, curr) => [prev, ' & ', curr] as any)} - -

        -
        - - · - {post.readingTime} min read + .reduce((prev, curr) => [prev, ' & ', curr] as any)} + +

        +
        + + · + {post.readingTime} min read +
        -
        -
        -
        - - {post.canonical && ( - - - - )} - {post.coverCaption} - {children} -
        -
        -
        -
        +
        +
        + + {post.canonical && ( + + + + )} + {post.coverCaption} + {children} +
        +
        +
        +
        +
    ); } diff --git a/components/layout/CommunityLayout.tsx b/components/layout/CommunityLayout.tsx index 1fdee1751fe7..2889f4f5e54b 100644 --- a/components/layout/CommunityLayout.tsx +++ b/components/layout/CommunityLayout.tsx @@ -45,18 +45,18 @@ function addAdditionalUserInfo(user: Tsc | Ambassador) { // add social links if (userData.github) { - userData.github = `https://www.github.com/${userData.github}`; + userData.githubUrl = `https://www.github.com/${userData.github}`; } if (userData.linkedin) { - userData.linkedin = `https://www.linkedin.com/in/${userData.linkedin}`; + userData.linkedinUrl = `https://www.linkedin.com/in/${userData.linkedin}`; } if (userData.twitter) { - userData.twitter = `https://www.twitter.com/${userData.twitter}`; + userData.twitterUrl = `https://www.twitter.com/${userData.twitter}`; } // add avatar url // github redirects to avatar url using `https://www.github.com/.png` - userData.avatarUrl = `${userData.github}.png`; + userData.avatarUrl = userData.githubUrl ? `${userData.githubUrl}.png` : ''; // make repo links if ('repos' in userData) { @@ -127,7 +127,7 @@ function SocialLink({ href, social }: SocialLinkProps) { @@ -149,7 +149,7 @@ function UserWorkStatus({ user }: TSCUser) { return (
    Available for hire
    @@ -159,7 +159,7 @@ function UserWorkStatus({ user }: TSCUser) { return (
    {user.company}
    @@ -169,7 +169,7 @@ function UserWorkStatus({ user }: TSCUser) { return (
    Individual Member
    @@ -184,12 +184,12 @@ function UserWorkStatus({ user }: TSCUser) { * @param {Membership} props.membership - determines the community members belong to board or TSC (ambassadors & maintainers). */ function UserInfo({ user, membership }: TSCUser) { - const githubUsername = user.github.split('/').pop(); + const githubUsername = user.github; return (
  • +
  • Question Mark -
    +
    Want to become a member? Follow this Link @@ -314,9 +317,9 @@ export default function CommunityLayout({ children, membership }: ICommunityLayo
    )}
    -
    +

    Current {membership} members

    - (in alphabetical order) + (in alphabetical order)
      diff --git a/components/layout/GenericLayout.tsx b/components/layout/GenericLayout.tsx index 9c10d09996aa..1764bc9122fc 100644 --- a/components/layout/GenericLayout.tsx +++ b/components/layout/GenericLayout.tsx @@ -36,7 +36,7 @@ export default function GenericLayout({ } return ( -
      +
      diff --git a/components/navigation/BlogPostItem.tsx b/components/navigation/BlogPostItem.tsx index 2b40723e626a..91417f7baa94 100644 --- a/components/navigation/BlogPostItem.tsx +++ b/components/navigation/BlogPostItem.tsx @@ -51,6 +51,15 @@ const BlogPostItem = ({ post, className = '', id = '' }: BlogPostItemProps, ref: case BlogPostType.Communication: typeColors = ['bg-teal-100', 'text-teal-800']; break; + case BlogPostType.Conference: + typeColors = ['bg-purple-100', 'text-purple-800']; + break; + case BlogPostType.Engineering: + typeColors = ['bg-blue-100', 'text-blue-800']; + break; + case BlogPostType.Community: + typeColors = ['bg-indigo-100', 'text-indigo-800']; + break; default: } @@ -59,79 +68,74 @@ const BlogPostItem = ({ post, className = '', id = '' }: BlogPostItemProps, ref:
      {post.featured && ( -
      - +
      + Featured
      )} -
      +
      - + {post.type} - - - {post.title} - - - - - + + {post.title} + + + +
      -
      -
      - +
      +
      +
      + +
      +
      + + + {post.authors + .map((author) => author.name) + .join(', ') + .split(', ') + .slice(0, 2) + .join(' & ')} + + + + {post.date ? moment(post.date).format('MMMM D, YYYY') : ''} + +
      -
      - - - {post.authors - .map((author, index) => - author.link ? ( - - ) : ( - author.name - ) - ) - .reduce((prev, curr, index) => ( - - {prev} & {curr} - - ))} - - - - - · - {post.readingTime} min read +
      + + {post.readingTime} min
      diff --git a/components/navigation/DocsNav.tsx b/components/navigation/DocsNav.tsx index 6306abf5a3b9..60303a107c45 100644 --- a/components/navigation/DocsNav.tsx +++ b/components/navigation/DocsNav.tsx @@ -76,7 +76,7 @@ export default function DocsNav({ item, active, onClick = () => {} }: DocsNavPro useEffect(() => { setOpenSubCategory(active.startsWith(item.item.slug)); - }, [active]); + }, [active, item.item.slug]); return (
    • diff --git a/components/navigation/EventPostItem.tsx b/components/navigation/EventPostItem.tsx index c270e53861b7..2437904ba31d 100644 --- a/components/navigation/EventPostItem.tsx +++ b/components/navigation/EventPostItem.tsx @@ -1,14 +1,9 @@ -import { ArrowRightIcon } from '@heroicons/react/outline'; import moment from 'moment'; import React from 'react'; import type { IEvent } from '@/types/event'; import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; -import IconCalendar from '../icons/Calendar'; -import Community from '../icons/Community'; -import Conference from '../icons/Conference'; -import Webinar from '../icons/Webinar'; import Heading from '../typography/Heading'; interface EventPostItemProps { @@ -28,25 +23,16 @@ function EventPostItem({ post, className = '', id }: EventPostItemProps): React. const localTime = moment().format('YYYY-MM-DD'); // store localTime const currentDate = `${localTime}T00:00:00.000Z`; const title = post.title || ''; - let color = ''; - let icon: React.ReactElement | null = null; let type = ''; if (title.includes('community')) { - icon = ; - color = 'text-green-800'; type = 'COMMUNITY'; } else if (title.includes('conference')) { - icon = ; - color = 'text-orange-800'; type = 'CONFERENCE'; } else if (title.includes('workshop')) { - icon = ; - color = 'text-blue-400'; type = 'WORKSHOP'; } - const defaultCover = 'https://github.com/asyncapi/community/assets/40604284/01c2b8de-fa5c-44dd-81a5-70cb96df4813'; let active = true; const postDate = moment(post.date); // Convert post.date to a moment object if necessary @@ -59,33 +45,41 @@ function EventPostItem({ post, className = '', id }: EventPostItemProps): React. return (
    • -
    • ); diff --git a/components/navigation/Filter.tsx b/components/navigation/Filter.tsx index 72cad59d17ce..ca984e71ba67 100644 --- a/components/navigation/Filter.tsx +++ b/components/navigation/Filter.tsx @@ -32,11 +32,11 @@ export default function Filter({ data, onFilter, checks, className }: FilterProp useEffect(() => { setQuery(route.query); applyFilterList(checks, data, setFilters); - }, [route]); + }, [route, checks, data]); useEffect(() => { onFilterApply(data, onFilter, routeQuery); - }, [routeQuery]); + }, [routeQuery, data, onFilter]); return checks.map((check) => { let selected = ''; diff --git a/components/navigation/NavBar.tsx b/components/navigation/NavBar.tsx index e3cd42f35024..10adc687ad3f 100644 --- a/components/navigation/NavBar.tsx +++ b/components/navigation/NavBar.tsx @@ -163,12 +163,12 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps
      {/*
      */} -
      +
      {!hideLogo && (
      @@ -248,7 +248,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps
      diff --git a/components/navigation/communityItems.tsx b/components/navigation/communityItems.tsx index fa6b3ee05965..fa8ee8a34710 100644 --- a/components/navigation/communityItems.tsx +++ b/components/navigation/communityItems.tsx @@ -4,7 +4,6 @@ import IconAmbassador from '../icons/Ambassador'; import IconContributing from '../icons/Contributing'; import IconDashboard from '../icons/Dashboard'; import IconGithubOrganization from '../icons/GithubOrganization'; -import IconMeetings from '../icons/Meetings'; import IconModelina from '../icons/Modelina'; import IconNewsroom from '../icons/Newsroom'; import IconSlack from '../icons/Slack'; @@ -66,17 +65,11 @@ const communityItems: CommunityItem[] = [ description: 'Just need a good first issue to start your contribution journey? or want to see what topics are hot in discussion?' }, - { - icon: IconMeetings, - title: 'Events', - href: '/community/events', - description: 'See what events and meetings are organized under AsyncAPI umbrella and join one of them.' - }, { icon: IconNewsroom, - title: 'Newsroom', - href: '/community/newsroom', - description: 'Get upto date with the recent activity in the initiative.' + title: 'Events & Updates', + href: '/community/events-and-updates', + description: 'Stay updated with AsyncAPI events, blogs, videos, and community news.' } ]; diff --git a/components/newsroom/NewsroomBlogPosts.tsx b/components/newsroom/NewsroomBlogPosts.tsx index 7e79c1fb5342..5d6d4c52784d 100644 --- a/components/newsroom/NewsroomBlogPosts.tsx +++ b/components/newsroom/NewsroomBlogPosts.tsx @@ -1,21 +1,12 @@ -import React, { useState } from 'react'; -import { A11y, Navigation } from 'swiper/modules'; -import { Swiper, SwiperSlide } from 'swiper/react'; +import React from 'react'; import { getAllPosts } from '../../utils/api'; -import ArrowLeft from '../icons/ArrowLeft'; -import ArrowRight from '../icons/ArrowRight'; import BlogPostItem from '../navigation/BlogPostItem'; -import { checkLastSnapIndex, useSwiperRef } from './swiper'; /** * @description This component displays the latest blog posts. */ export default function NewsroomBlogPosts() { - const [nextEl, nextElRef] = useSwiperRef(); - const [prevEl, prevElRef] = useSwiperRef(); - const [current, setCurrent] = useState(0); - const posts = getAllPosts() .blog.sort((i1, i2) => { const i1Date = new Date(i1.date); @@ -26,50 +17,15 @@ export default function NewsroomBlogPosts() { return i2Date.valueOf() - i1Date.valueOf(); }) - .slice(0, 5); - - const buttonClass = 'shadow-md rounded border mx-2 mb-2 focus:outline-none'; + .slice(0, 3); return ( -
      - setCurrent(swiper.snapIndex)} - navigation={{ - prevEl, - nextEl - }} - breakpoints={{ - 640: { - slidesPerView: 2 - } - }} - > +
      +
        {posts.map((post, index) => ( - - - + ))} - - -
        - - -
        +
      ); } diff --git a/components/newsroom/NewsroomSection.tsx b/components/newsroom/NewsroomSection.tsx index 7e660e69dfe4..0ecd482c6976 100644 --- a/components/newsroom/NewsroomSection.tsx +++ b/components/newsroom/NewsroomSection.tsx @@ -56,7 +56,7 @@ export default function NewsroomSection() {
      diff --git a/components/newsroom/NewsroomYoutube.tsx b/components/newsroom/NewsroomYoutube.tsx index ced5fceedbe2..723bafe59a99 100644 --- a/components/newsroom/NewsroomYoutube.tsx +++ b/components/newsroom/NewsroomYoutube.tsx @@ -1,11 +1,6 @@ -import React, { useState } from 'react'; -import { A11y, Navigation } from 'swiper/modules'; -import { Swiper, SwiperSlide } from 'swiper/react'; +import React from 'react'; import videoData from '../../config/newsroom_videos.json'; -import ArrowLeft from '../icons/ArrowLeft'; -import ArrowRight from '../icons/ArrowRight'; -import { checkLastSnapIndex, useSwiperRef } from './swiper'; import YouTubeCard from './YouTubeCard'; interface NewsroomYoutubeProps { @@ -13,57 +8,21 @@ interface NewsroomYoutubeProps { } /** - * Newsroom Youtube component displays a Swiper carousel of YouTubeCard components. + * Newsroom Youtube component displays a grid of YouTubeCard components. * @description This component displays the latest videos from the AsyncAPI YouTube channel. * @param {string} [props.className=''] - Additional CSS classes for styling. */ export default function NewsroomYoutube({ className = '' }: NewsroomYoutubeProps) { - const [nextEl, nextElRef] = useSwiperRef(); - const [prevEl, prevElRef] = useSwiperRef(); - const [current, setCurrent] = useState(0); - - const buttonClass = 'shadow-md rounded border mx-2 mb-2 focus:outline-none'; + // Only show the first 3 videos + const videos = videoData.slice(0, 3); return ( -
      - setCurrent(swiper.snapIndex)} - navigation={{ - prevEl, - nextEl - }} - breakpoints={{ - 640: { - slidesPerView: 2 - } - }} - > - {videoData.map((video, index) => ( - - - +
      +
        + {videos.map((video, index) => ( + ))} - - -
        - - -
        +
      ); } diff --git a/components/newsroom/YouTubeCard.tsx b/components/newsroom/YouTubeCard.tsx index f7d9ffa2b1b6..4de71df4187d 100644 --- a/components/newsroom/YouTubeCard.tsx +++ b/components/newsroom/YouTubeCard.tsx @@ -6,7 +6,6 @@ import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; import ArrowRight from '../icons/ArrowRight'; import Heading from '../typography/Heading'; import Paragraph from '../typography/Paragraph'; -import TextLink from '../typography/TextLink'; interface YouTubeVideo { image_url: string; @@ -25,34 +24,39 @@ interface YouTubeCardProps { */ export default function YouTubeCard({ video }: YouTubeCardProps) { return ( -
    • - +
    • ); } diff --git a/components/roadmap/GoalCardRoadmapPage.tsx b/components/roadmap/GoalCardRoadmapPage.tsx new file mode 100644 index 000000000000..442f5c9d3415 --- /dev/null +++ b/components/roadmap/GoalCardRoadmapPage.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +interface GoalCardRoadmapPageProps { + icon: React.ComponentType<{ className?: string }>; + title: string; + description: string; +} + +/** + * @description GoalCardRoadmapPage component displays a goal with an icon, title, and description. + * Used in the roadmap page to showcase AsyncAPI's goals. + */ +export default function GoalCardRoadmapPage({ icon: Icon, title, description }: GoalCardRoadmapPageProps) { + return ( +
      +
      +
      + +
      +
      +

      {title}

      +

      {description}

      +
      + ); +} diff --git a/components/roadmap/RoadmapColumn.tsx b/components/roadmap/RoadmapColumn.tsx index a8fffb5fb2cf..741ae6ebec06 100644 --- a/components/roadmap/RoadmapColumn.tsx +++ b/components/roadmap/RoadmapColumn.tsx @@ -13,6 +13,7 @@ interface IRoadmapColumnProps { colorClass: string; items?: any[]; childrenCollapsed?: boolean; + className?: string; } /** @@ -22,21 +23,23 @@ interface IRoadmapColumnProps { * @param {string} props.colorClass - The color class for styling. * @param {array} props.items - The array of items. * @param {boolean} props.childrenCollapsed - Whether children items are collapsed. + * @param {string} props.className - Optional className for custom styling. */ export default function RoadmapColumn({ title, description, colorClass, items = [], - childrenCollapsed = false + childrenCollapsed = false, + className = '' }: IRoadmapColumnProps): React.ReactElement { return (
      -
      - +
      + {title} - + {description}
      diff --git a/components/roadmap/RoadmapItem.tsx b/components/roadmap/RoadmapItem.tsx index 34787b7252e6..07d9293097b7 100644 --- a/components/roadmap/RoadmapItem.tsx +++ b/components/roadmap/RoadmapItem.tsx @@ -34,7 +34,7 @@ export default function RoadmapItem({ item, colorClass, showConnector = true, co const [isCollapsed, setIsCollapsed] = useState(collapsed); const isCollapsible = item.solutions !== undefined; - const connectorClasses = 'border-l-2 border-dashed border-gray-300'; + const connectorClasses = 'border-l-2 border-dashed border-gray-300 dark:border-gray-600'; const classNames = `pt-2 ${showConnector && connectorClasses}`; return ( @@ -42,7 +42,7 @@ export default function RoadmapItem({ item, colorClass, showConnector = true, co
      {showConnector && (
      -
      +
      )} + )} ); diff --git a/components/roadmap/RoadmapPill.tsx b/components/roadmap/RoadmapPill.tsx index d73ac46e84bf..d00e1f03cf7a 100644 --- a/components/roadmap/RoadmapPill.tsx +++ b/components/roadmap/RoadmapPill.tsx @@ -14,7 +14,7 @@ function DoneIcon() { viewBox='0 0 24 24' strokeWidth={1.5} stroke='currentColor' - className='-mt-0.5 mr-2 inline-block size-6 text-green-600' + className='-mt-0.5 mr-2 inline-block size-6 text-green-600 dark:text-green-400' >
      @@ -76,7 +76,7 @@ export default function Pill({ href={item.url} rel='noopener noreferrer' onClick={() => !item.url && item.description && setIsDescriptionVisible(true)} - className={`block text-left font-medium text-gray-900 ${item.description || item.url ? 'cursor-pointer hover:text-gray-600' : 'cursor-default'}`} + className={`block text-left font-medium text-gray-900 dark:text-gray-100 ${item.description || item.url ? 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-400' : 'cursor-default'}`} > {item.done && ( @@ -88,7 +88,9 @@ export default function Pill({
      {isCollapsible && ( )}
      diff --git a/components/tools/CategoryDropdown.tsx b/components/tools/CategoryDropdown.tsx index 02d091008a0f..1bc04c0ae77f 100644 --- a/components/tools/CategoryDropdown.tsx +++ b/components/tools/CategoryDropdown.tsx @@ -18,7 +18,7 @@ const ToolsData = ToolsDataList as ToolsListData; export default function CategoryDropdown({ setopenCategory }: CategoryDropdownProps) { return (
      {categoryName} diff --git a/components/tools/Checkbox.tsx b/components/tools/Checkbox.tsx index 25b4bceb2c2a..0233ee324a15 100644 --- a/components/tools/Checkbox.tsx +++ b/components/tools/Checkbox.tsx @@ -24,14 +24,14 @@ const Checkbox = ({ return (
      {checked ? ( - checked + checked ) : ( - unchecked + unchecked )}
      {name}
      diff --git a/components/tools/Filters.tsx b/components/tools/Filters.tsx index b3eb6f62544e..a51fb2ebb8a1 100644 --- a/components/tools/Filters.tsx +++ b/components/tools/Filters.tsx @@ -126,10 +126,13 @@ export default function Filters({ setOpenFilter }: FiltersProps) { return ( -
      +
      -
      +
      -
      +
      Undo Changes
      (checkPaid === 'free' ? setCheckPaid('all') : setCheckPaid('free'))} >
      Open Source
      - Free + Free
      (checkPaid === 'paid' ? setCheckPaid('all') : setCheckPaid('paid'))} >
      Commercial
      - Paid + Paid
      -
      +
      -
      +
      -
      +
      toggleDropdown(OpenedFiltersDropdownType.LANGUAGE)} > -
      +
      {/* eslint-disable-next-line no-nested-ternary */} {checkedLanguage.length > 0 ? checkedLanguage.length === 1 @@ -207,11 +213,11 @@ export default function Filters({ setOpenFilter }: FiltersProps) { : 'Select Languages...'}
      {openedFiltersDropown === OpenedFiltersDropdownType.LANGUAGE && ( -
      +
      -
      +
      toggleDropdown(OpenedFiltersDropdownType.TECHNOLOGY)} > -
      +
      {/* eslint-disable-next-line no-nested-ternary */} {checkedTechnology.length > 0 ? checkedTechnology.length === 1 @@ -249,11 +255,11 @@ export default function Filters({ setOpenFilter }: FiltersProps) { : 'Select Technologies...'}
      {openedFiltersDropown === OpenedFiltersDropdownType.TECHNOLOGY && ( -
      +
      -
      +
      toggleDropdown(OpenedFiltersDropdownType.CATEGORY)} > -
      +
      {/* eslint-disable-next-line no-nested-ternary */} {checkedCategory.length > 0 ? checkedCategory.length === 1 @@ -291,11 +297,11 @@ export default function Filters({ setOpenFilter }: FiltersProps) { : 'Select Categories...'}
      {openedFiltersDropown === OpenedFiltersDropdownType.CATEGORY && ( -
      +
      -
      +
      diff --git a/components/tools/FiltersDisplay.tsx b/components/tools/FiltersDisplay.tsx index 54be11a26153..f75ca7701a9f 100644 --- a/components/tools/FiltersDisplay.tsx +++ b/components/tools/FiltersDisplay.tsx @@ -38,16 +38,16 @@ export default function FiltersDisplay({ checkedOptions = [], setCheckedOptions
      {items}
      ); diff --git a/components/tools/Toggle.tsx b/components/tools/Toggle.tsx index ee7066e7b900..56b6e319f268 100644 --- a/components/tools/Toggle.tsx +++ b/components/tools/Toggle.tsx @@ -23,10 +23,10 @@ const Toggle = ({ />
      - {label &&
      {label}
      } + {label &&
      {label}
      } ); }; diff --git a/components/tools/ToolsCard.tsx b/components/tools/ToolsCard.tsx index a03f01b84dac..d7dbe21e81cf 100644 --- a/components/tools/ToolsCard.tsx +++ b/components/tools/ToolsCard.tsx @@ -32,7 +32,7 @@ export default function ToolsCard({ toolData }: ToolsCardProp) { if (descriptionRef.current) { setIsTruncated(descriptionRef.current?.scrollHeight! > descriptionRef.current?.clientHeight!); } - }, [descriptionRef.current]); + }, []); let onGit = false; @@ -49,13 +49,15 @@ export default function ToolsCard({ toolData }: ToolsCardProp) { }); return ( -
      +
      - {toolData.title} + + {toolData.title} +
      setTimeout(() => { if (!visible.desc) setVisible({ ...visible, desc: true }); @@ -72,7 +74,7 @@ export default function ToolsCard({ toolData }: ToolsCardProp) { > {toolData.filters?.hasCommercial === false ? 'Open Source' : 'Commercial'} {visible.desc && ( - + {Data.properties.filters.properties.hasCommercial.description} )} @@ -80,7 +82,7 @@ export default function ToolsCard({ toolData }: ToolsCardProp) {
      - + @@ -100,10 +102,10 @@ export default function ToolsCard({ toolData }: ToolsCardProp) { {showDescription && (
      setShowDescription(false)} > - + {toolData.description}
      @@ -111,14 +113,14 @@ export default function ToolsCard({ toolData }: ToolsCardProp) {
      -
      +
      {toolData.filters?.language || toolData?.filters?.technology?.length ? (
      {toolData?.filters?.language?.length !== 0 && (
      -
      +
      {toolData.filters?.language && toolData.filters?.language.map((item, index) => ( @@ -138,7 +140,7 @@ export default function ToolsCard({ toolData }: ToolsCardProp) { {toolData.filters.technology?.length !== 0 && (
      ) : ( -
      +
      {' '} No further details provided{' '} @@ -167,59 +169,71 @@ export default function ToolsCard({ toolData }: ToolsCardProp) {
      {(toolData?.links?.repoUrl || toolData?.links?.websiteUrl || toolData?.links?.docsUrl) && ( <> -
      -
      +
      +
      {toolData.links.repoUrl && (onGit ? ( -
      - GitHub -
      View Github
      +
      + GitHub +
      View Github
      ) : ( -
      -
      View Source Code
      +
      +
      View Source Code
      ))} {toolData.links.websiteUrl && ( -
      - Share -
      Visit Website
      +
      + Share +
      Visit Website
      )} {toolData.links.docsUrl && (
      -
      - Docs -
      Visit Docs
      +
      + Docs +
      Visit Docs
      )} diff --git a/components/tools/ToolsDashboard.tsx b/components/tools/ToolsDashboard.tsx index 2797b2d2a6d9..209d01aa9b42 100644 --- a/components/tools/ToolsDashboard.tsx +++ b/components/tools/ToolsDashboard.tsx @@ -163,7 +163,7 @@ export default function ToolsDashboard() { document.documentElement.style.scrollPaddingTop = '0'; } } - }, []); + }, [toolsList]); // Function to update the list of tools according to the current filters applied const clearFilters = () => { setOpenFilter(false); @@ -181,12 +181,12 @@ export default function ToolsDashboard() {
      }>
      setOpenFilter(!openFilter)} data-testid='ToolsDashboard-Filters-Click' > -
      Filter
      +
      Filter
      {openFilter && ( )}
      {isFiltered && ( -
      +
      - Clear Filters + Clear Filters
      )}
      @@ -239,8 +247,11 @@ export default function ToolsDashboard() { ) : (
      - not found -
      Sorry, we don't have tools according to your needs.
      + not found +
      + {' '} + Sorry, we don't have tools according to your needs.{' '} +
      )}
      diff --git a/components/tools/ToolsList.tsx b/components/tools/ToolsList.tsx index 5ce95974d722..d00da927bfa0 100644 --- a/components/tools/ToolsList.tsx +++ b/components/tools/ToolsList.tsx @@ -24,12 +24,19 @@ export default function ToolsList({ toolsListData }: ToolsListProp) { {Object.keys(toolsListData).map((categoryName, index) => { if (toolsListData[categoryName].toolsList.length > 0) { return ( -
      - - {categoryName} - - {toolsListData[categoryName].description} -
      +
      +
      + + {categoryName} + + + {toolsListData[categoryName].description} + +
      +
      {toolsListData[categoryName].toolsList.map((tool, toolIndex) => ( diff --git a/components/typography/Paragraph.tsx b/components/typography/Paragraph.tsx index 17f8eadcce88..223067400f68 100644 --- a/components/typography/Paragraph.tsx +++ b/components/typography/Paragraph.tsx @@ -27,7 +27,7 @@ export interface ParagraphProps { */ export default function Paragraph({ typeStyle = ParagraphTypeStyle.lg, - textColor = 'text-gray-700', + textColor = 'text-gray-700 dark:text-gray-300', fontWeight = '', className = '', children diff --git a/config/community-toolings-showcase.json b/config/community-toolings-showcase.json new file mode 100644 index 000000000000..3714b2b23fd6 --- /dev/null +++ b/config/community-toolings-showcase.json @@ -0,0 +1,65 @@ +{ + "go-asyncapi": { + "name": "go-asyncapi", + "displayName": "Go AsyncAPI", + "description": "This library helps to create AsyncAPI spec from your Go message structures. It uses reflection to translate Go structures in JSON Schema definitions and arrange them in AsyncAPI schema.", + "language": "Go", + "link": "https://github.com/swaggest/go-asyncapi", + "color": "border-cyan-400 text-cyan-600 dark:text-cyan-400 hover:bg-cyan-50 dark:hover:bg-cyan-900/20", + "badge": "Open Source" + }, + "faststream": { + "name": "faststream", + "displayName": "FastStream", + "description": "A powerful and easy-to-use Python framework for building asynchronous services interacting with event streams such as Apache Kafka, RabbitMQ and NATS.", + "language": "Python", + "link": "https://faststream.ag2.ai", + "color": "border-cyan-400 text-cyan-600 dark:text-cyan-400 hover:bg-cyan-50 dark:hover:bg-cyan-900/20", + "badge": "Open Source" + }, + "modelina": { + "name": "modelina", + "displayName": "AsyncAPI Modelina", + "description": "Generate payload models into Java, TypeScript, Go, etc, you name it, from AsyncAPI documents. This tool gives you full control over the models through high customization.", + "language": "TypeScript", + "link": "https://modelina.org", + "color": "border-cyan-400 text-cyan-600 dark:text-cyan-400 hover:bg-cyan-50 dark:hover:bg-cyan-900/20", + "badge": "Open Source" + }, + "converter": { + "name": "converter", + "displayName": "Converter", + "description": "Converts old versions of AsyncAPI files into the latest version.", + "language": "TypeScript", + "link": "https://www.asyncapi.com/tools/converter", + "color": "border-green-400 text-green-600 dark:text-green-400 hover:bg-green-50 dark:hover:bg-green-900/20", + "badge": "Open Source" + }, + "java-asyncapi": { + "name": "java-asyncapi", + "displayName": "Java AsyncAPI", + "description": "This tool stores modules, which simplifies interacting with AsyncAPI in jvm ecosystem.", + "language": "Java", + "link": "https://github.com/asyncapi/jasyncapi", + "color": "border-cyan-400 text-cyan-600 dark:text-cyan-400 hover:bg-cyan-50 dark:hover:bg-cyan-900/20", + "badge": "Open Source" + }, + "kotlin-asyncapi": { + "name": "kotlin-asyncapi", + "displayName": "Kotlin AsyncAPI", + "description": "The Kotlin AsyncAPI project aims to provide convenience tools for generating and serving AsyncAPI documentation. The core of this project is a Kotlin DSL for building the specification in a typesafe way.", + "language": "Kotlin", + "link": "https://github.com/OpenFolder/kotlin-asyncapi", + "color": "border-cyan-400 text-cyan-600 dark:text-cyan-400 hover:bg-cyan-50 dark:hover:bg-cyan-900/20", + "badge": "Open Source" + }, + "saunter": { + "name": "saunter", + "displayName": "Saunter", + "description": "Saunter is an AsyncAPI documentation generator for dotnet. Generates (and hosts) an AsyncAPI schema document from your code.", + "language": "C#", + "link": "https://github.com/tehmantra/saunter", + "color": "border-pink-500 text-pink-600 dark:text-pink-400 hover:bg-pink-50 dark:hover:bg-pink-900/20", + "badge": "Open Source" + } +} diff --git a/pages/_document.tsx b/pages/_document.tsx index fecd327edff1..1b8cb71f5e04 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -17,6 +17,24 @@ class MyDocument extends Document { return ( + {/* Blocking script to apply dark mode before React hydrates - prevents flash */} +