From febf54ccfd12b3b21f5c3398e9061ad3e36b6d5d Mon Sep 17 00:00:00 2001 From: Anurag Yadav Date: Wed, 11 Feb 2026 23:34:36 +0530 Subject: [PATCH 1/7] Implemented Community Page --- frontend/src/app/community/layout.tsx | 7 ++ frontend/src/app/community/page.tsx | 174 ++++++++++++++++++++++++++ frontend/src/utils/communityData.ts | 62 +++++++++ frontend/src/utils/metadata.ts | 6 + 4 files changed, 249 insertions(+) create mode 100644 frontend/src/app/community/layout.tsx create mode 100644 frontend/src/app/community/page.tsx create mode 100644 frontend/src/utils/communityData.ts diff --git a/frontend/src/app/community/layout.tsx b/frontend/src/app/community/layout.tsx new file mode 100644 index 0000000000..98646b3b4f --- /dev/null +++ b/frontend/src/app/community/layout.tsx @@ -0,0 +1,7 @@ +import { getStaticMetadata } from 'utils/metaconfig' + +export const metadata = getStaticMetadata('community', '/community') + +export default function CommunityLayout({ children }: { children: React.ReactNode }) { + return children +} diff --git a/frontend/src/app/community/page.tsx b/frontend/src/app/community/page.tsx new file mode 100644 index 0000000000..99921e9eff --- /dev/null +++ b/frontend/src/app/community/page.tsx @@ -0,0 +1,174 @@ +'use client' +import Link from 'next/link' +import { FaArrowRight, FaSlack } from 'react-icons/fa' +import SecondaryCard from 'components/SecondaryCard' +import { exploreCards, engagementWays, journeySteps } from 'utils/communityData' + +export default function CommunityPage() { + + return ( +
+
+
+

+ OWASP Community +

+

+ Explore the vibrant OWASP community. Connect with chapters, members, and organizations + around the world. Discover opportunities to engage, learn, and contribute. +

+
+ + Explore Chapters + + + + Meet Members + + +
+
+ + {/* Explore Community Section */} + +
+ {exploreCards.map((card) => ( + +
+ +
+

+ {card.title} +

+

{card.description}

+ + ))} +
+
+ + {/* Ways to Engage Section */} + +
+ {engagementWays.map((way, index) => ( +
+

+ {way.title} +

+

{way.description}

+
+ ))} +
+
+ + {/* Community Journey Section */} + +
+ {/* Desktop view - horizontal */} +
+ {journeySteps.map((step, index) => ( +
+
+
+ {index + 1} +
+

{step.label}

+

{step.description}

+
+ {index < journeySteps.length - 1 && ( +
+ )} +
+ ))} +
+ + {/* Mobile view - vertical */} +
+ {journeySteps.map((step, index) => ( +
+
+ {index + 1} +
+
+

{step.label}

+

{step.description}

+
+
+ ))} +
+
+ + + {/* Join the Community Section */} + +
+
+
+

+ Connect with fellow security professionals, ask questions, share ideas, and collaborate + with the global OWASP community on Slack. +

+ + Join Slack + +
+
+ + {/* Final Call to Action */} + +

Ready to Get Involved?

+

+ Join thousands of security professionals making a difference in the OWASP community. + Whether you're just starting out or you're an experienced contributor, + there's a place for you here. +

+
+ + Find Your Chapter + + + + Connect with Members + + + + Start Contributing + + +
+
+
+
+ ) +} diff --git a/frontend/src/utils/communityData.ts b/frontend/src/utils/communityData.ts new file mode 100644 index 0000000000..f2e02802a9 --- /dev/null +++ b/frontend/src/utils/communityData.ts @@ -0,0 +1,62 @@ +import { FaMapMarkerAlt, FaUsers, FaBuilding, FaCamera } from 'react-icons/fa' + +export const exploreCards = [ + { + icon: FaMapMarkerAlt, + title: 'Chapters', + description: 'Discover local OWASP chapters worldwide', + href: '/community/chapters', + color: 'text-blue-500', + bgColor: 'bg-blue-50 dark:bg-blue-900/20', + }, + { + icon: FaUsers, + title: 'Members', + description: 'Connect with community members', + href: '/community/members', + color: 'text-green-500', + bgColor: 'bg-green-50 dark:bg-green-900/20', + }, + { + icon: FaBuilding, + title: 'Organizations', + description: 'Explore OWASP organizations', + href: '/community/organizations', + color: 'text-purple-500', + bgColor: 'bg-purple-50 dark:bg-purple-900/20', + }, + { + icon: FaCamera, + title: 'Snapshots', + description: 'View community snapshots and highlights', + href: '/community/snapshots', + color: 'text-orange-500', + bgColor: 'bg-orange-50 dark:bg-orange-900/20', + }, +] + +export const engagementWays = [ + { + title: 'Join Local Chapters', + description: 'Find and connect with OWASP chapters in your area to participate in local events and meetups.', + }, + { + title: 'Connect with Members', + description: 'Network with security professionals, developers, and enthusiasts from around the world.', + }, + { + title: 'Contribute to Projects', + description: 'Share your expertise by contributing to open-source security projects and initiatives.', + }, + { + title: 'Participate in Discussions', + description: 'Join conversations, share knowledge, and learn from the global cybersecurity community.', + }, +] + +export const journeySteps = [ + { label: 'Discover', description: 'Explore chapters, members, and organizations' }, + { label: 'Connect', description: 'Build relationships within the community' }, + { label: 'Participate', description: 'Join events and contribute to discussions' }, + { label: 'Contribute', description: 'Share your knowledge and expertise' }, +] diff --git a/frontend/src/utils/metadata.ts b/frontend/src/utils/metadata.ts index d174adf440..503c39d48e 100644 --- a/frontend/src/utils/metadata.ts +++ b/frontend/src/utils/metadata.ts @@ -36,6 +36,12 @@ export const METADATA_CONFIG = { pageTitle: 'Contribute', type: 'website', }, + community: { + description: 'Explore the OWASP community: chapters, members, organizations, and snapshots.', + keywords: ['OWASP community', 'chapters', 'members', 'organizations', 'security community'], + pageTitle: 'Community', + type: 'website', + }, snapshots: { description: 'Snapshots of OWASP', keywords: ['OWASP snapshot', 'contribute'], From 0af9e23a9a140d0c2a76ee4ff45d105d47fe5b32 Mon Sep 17 00:00:00 2001 From: Anurag Yadav Date: Wed, 11 Feb 2026 23:43:32 +0530 Subject: [PATCH 2/7] update --- frontend/src/app/community/layout.tsx | 3 ++- frontend/src/app/community/page.tsx | 32 ++++++++++++--------------- frontend/src/utils/communityData.ts | 12 ++++++---- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/community/layout.tsx b/frontend/src/app/community/layout.tsx index 98646b3b4f..6d92d0ddc0 100644 --- a/frontend/src/app/community/layout.tsx +++ b/frontend/src/app/community/layout.tsx @@ -1,7 +1,8 @@ +import type { ReactNode } from 'react' import { getStaticMetadata } from 'utils/metaconfig' export const metadata = getStaticMetadata('community', '/community') -export default function CommunityLayout({ children }: { children: React.ReactNode }) { +export default function CommunityLayout({ children }: { children: ReactNode }) { return children } diff --git a/frontend/src/app/community/page.tsx b/frontend/src/app/community/page.tsx index 99921e9eff..54a8bc569e 100644 --- a/frontend/src/app/community/page.tsx +++ b/frontend/src/app/community/page.tsx @@ -1,13 +1,12 @@ 'use client' import Link from 'next/link' import { FaArrowRight, FaSlack } from 'react-icons/fa' -import SecondaryCard from 'components/SecondaryCard' import { exploreCards, engagementWays, journeySteps } from 'utils/communityData' +import SecondaryCard from 'components/SecondaryCard' export default function CommunityPage() { - return ( -
+

@@ -61,11 +60,8 @@ export default function CommunityPage() { {/* Ways to Engage Section */}
- {engagementWays.map((way, index) => ( -
+ {engagementWays.map((way) => ( +

{way.title}

@@ -80,16 +76,16 @@ export default function CommunityPage() {
{/* Desktop view - horizontal */}
- {journeySteps.map((step, index) => ( -
+ {journeySteps.map((step, idx) => ( +
- {index + 1} + {idx + 1}

{step.label}

{step.description}

- {index < journeySteps.length - 1 && ( + {idx < journeySteps.length - 1 && (
)}
@@ -98,10 +94,10 @@ export default function CommunityPage() { {/* Mobile view - vertical */}
- {journeySteps.map((step, index) => ( -
+ {journeySteps.map((step, idx) => ( +
- {index + 1} + {idx + 1}

{step.label}

@@ -120,14 +116,14 @@ export default function CommunityPage() {

- Connect with fellow security professionals, ask questions, share ideas, and collaborate - with the global OWASP community on Slack. + Connect with fellow security professionals, ask questions, share ideas, and + collaborate with the global OWASP community on Slack.

Join Slack diff --git a/frontend/src/utils/communityData.ts b/frontend/src/utils/communityData.ts index f2e02802a9..7da1e9dc38 100644 --- a/frontend/src/utils/communityData.ts +++ b/frontend/src/utils/communityData.ts @@ -38,19 +38,23 @@ export const exploreCards = [ export const engagementWays = [ { title: 'Join Local Chapters', - description: 'Find and connect with OWASP chapters in your area to participate in local events and meetups.', + description: + 'Find and connect with OWASP chapters in your area to participate in local events and meetups.', }, { title: 'Connect with Members', - description: 'Network with security professionals, developers, and enthusiasts from around the world.', + description: + 'Network with security professionals, developers, and enthusiasts from around the world.', }, { title: 'Contribute to Projects', - description: 'Share your expertise by contributing to open-source security projects and initiatives.', + description: + 'Share your expertise by contributing to open-source security projects and initiatives.', }, { title: 'Participate in Discussions', - description: 'Join conversations, share knowledge, and learn from the global cybersecurity community.', + description: + 'Join conversations, share knowledge, and learn from the global cybersecurity community.', }, ] From 9666938efbf67eb289b235b506893628f1056f28 Mon Sep 17 00:00:00 2001 From: Anurag Yadav Date: Thu, 12 Feb 2026 00:03:18 +0530 Subject: [PATCH 3/7] update coderabbit review --- frontend/src/utils/communityData.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/utils/communityData.ts b/frontend/src/utils/communityData.ts index 7da1e9dc38..1f244d2184 100644 --- a/frontend/src/utils/communityData.ts +++ b/frontend/src/utils/communityData.ts @@ -5,7 +5,7 @@ export const exploreCards = [ icon: FaMapMarkerAlt, title: 'Chapters', description: 'Discover local OWASP chapters worldwide', - href: '/community/chapters', + href: '/chapters', color: 'text-blue-500', bgColor: 'bg-blue-50 dark:bg-blue-900/20', }, @@ -13,7 +13,7 @@ export const exploreCards = [ icon: FaUsers, title: 'Members', description: 'Connect with community members', - href: '/community/members', + href: '/members', color: 'text-green-500', bgColor: 'bg-green-50 dark:bg-green-900/20', }, @@ -21,7 +21,7 @@ export const exploreCards = [ icon: FaBuilding, title: 'Organizations', description: 'Explore OWASP organizations', - href: '/community/organizations', + href: '/organizations', color: 'text-purple-500', bgColor: 'bg-purple-50 dark:bg-purple-900/20', }, @@ -29,7 +29,7 @@ export const exploreCards = [ icon: FaCamera, title: 'Snapshots', description: 'View community snapshots and highlights', - href: '/community/snapshots', + href: '/snapshots', color: 'text-orange-500', bgColor: 'bg-orange-50 dark:bg-orange-900/20', }, From 4f7e9436beeca37fd794bf2c2dfb128c29544cb3 Mon Sep 17 00:00:00 2001 From: Anurag Yadav Date: Thu, 12 Feb 2026 00:09:16 +0530 Subject: [PATCH 4/7] fixed snapshots routes --- frontend/src/utils/communityData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/utils/communityData.ts b/frontend/src/utils/communityData.ts index 1f244d2184..40f4ac9130 100644 --- a/frontend/src/utils/communityData.ts +++ b/frontend/src/utils/communityData.ts @@ -29,7 +29,7 @@ export const exploreCards = [ icon: FaCamera, title: 'Snapshots', description: 'View community snapshots and highlights', - href: '/snapshots', + href: '/community/snapshots', color: 'text-orange-500', bgColor: 'bg-orange-50 dark:bg-orange-900/20', }, From 6e9c99d9f3e21c5aaeec3e85ec9c99b9029e2569 Mon Sep 17 00:00:00 2001 From: Anurag Yadav Date: Mon, 16 Feb 2026 17:04:33 +0530 Subject: [PATCH 5/7] Removed excessive call toa ctions --- frontend/src/app/community/page.tsx | 103 +++++++++++++--------------- frontend/src/utils/communityData.ts | 59 ++++++++++------ 2 files changed, 84 insertions(+), 78 deletions(-) diff --git a/frontend/src/app/community/page.tsx b/frontend/src/app/community/page.tsx index 54a8bc569e..c32151d027 100644 --- a/frontend/src/app/community/page.tsx +++ b/frontend/src/app/community/page.tsx @@ -1,8 +1,10 @@ 'use client' import Link from 'next/link' -import { FaArrowRight, FaSlack } from 'react-icons/fa' -import { exploreCards, engagementWays, journeySteps } from 'utils/communityData' +import { FaArrowRight, FaSlack, FaChevronRight } from 'react-icons/fa' +import { exploreCards as NAV_SECTIONS, engagementWays, journeySteps } from 'utils/communityData' import SecondaryCard from 'components/SecondaryCard' +import AnchorTitle from 'components/AnchorTitle' +import { IconWrapper } from 'wrappers/IconWrapper' export default function CommunityPage() { return ( @@ -13,45 +15,51 @@ export default function CommunityPage() { OWASP Community

- Explore the vibrant OWASP community. Connect with chapters, members, and organizations - around the world. Discover opportunities to engage, learn, and contribute. -

-
- - Explore Chapters - + Explore the vibrant OWASP community. Connect with{' '} + + Chapters - - Meet Members - + ,{' '} + + Members -
+ , and{' '} + + Organizations + {' '} + around the world at your fingertips. Discover opportunities to engage, learn, and + contribute. +

{/* Explore Community Section */} - -
- {exploreCards.map((card) => ( + + +
+ } + className="overflow-hidden" + > +
+ {NAV_SECTIONS.map((section) => ( -
- +
+ +
+
+

+ {section.title} + +

+

+ {section.description} +

-

- {card.title} -

-

{card.description}

))}
@@ -138,31 +146,12 @@ export default function CommunityPage() {

Join thousands of security professionals making a difference in the OWASP community. Whether you're just starting out or you're an experienced contributor, - there's a place for you here. -

-
- - Find Your Chapter - - - - Connect with Members - + there's a place for you here. Start by{' '} + + contributing to a project - - Start Contributing - - -
+ . +

diff --git a/frontend/src/utils/communityData.ts b/frontend/src/utils/communityData.ts index 40f4ac9130..e23bfbe6ef 100644 --- a/frontend/src/utils/communityData.ts +++ b/frontend/src/utils/communityData.ts @@ -1,37 +1,54 @@ -import { FaMapMarkerAlt, FaUsers, FaBuilding, FaCamera } from 'react-icons/fa' +import { + FaBuilding, + FaFolder, + FaHandshakeAngle, + FaLocationDot, + FaPeopleGroup, + FaUsers, +} from 'react-icons/fa6' export const exploreCards = [ { - icon: FaMapMarkerAlt, title: 'Chapters', - description: 'Discover local OWASP chapters worldwide', + description: 'Find local OWASP chapters and connect with your community.', href: '/chapters', - color: 'text-blue-500', - bgColor: 'bg-blue-50 dark:bg-blue-900/20', + icon: FaLocationDot, + color: 'text-gray-900 dark:text-gray-100', }, { - icon: FaUsers, - title: 'Members', - description: 'Connect with community members', - href: '/members', - color: 'text-green-500', - bgColor: 'bg-green-50 dark:bg-green-900/20', + title: 'Projects', + description: 'Explore open-source security projects and contribute.', + href: '/projects', + icon: FaFolder, + color: 'text-gray-900 dark:text-gray-100', + }, + { + title: 'Committees', + description: 'OWASP committees driving security governance.', + href: '/committees', + icon: FaPeopleGroup, + color: 'text-gray-900 dark:text-gray-100', }, { - icon: FaBuilding, title: 'Organizations', - description: 'Explore OWASP organizations', + description: 'Browse OWASP organizations and their work.', href: '/organizations', - color: 'text-purple-500', - bgColor: 'bg-purple-50 dark:bg-purple-900/20', + icon: FaBuilding, + color: 'text-gray-900 dark:text-gray-100', + }, + { + title: 'Members', + description: 'Meet the people behind OWASP.', + href: '/members', + icon: FaUsers, + color: 'text-gray-900 dark:text-gray-100', }, { - icon: FaCamera, - title: 'Snapshots', - description: 'View community snapshots and highlights', - href: '/community/snapshots', - color: 'text-orange-500', - bgColor: 'bg-orange-50 dark:bg-orange-900/20', + title: 'Contribute', + description: 'Find issues and start contributing today.', + href: '/contribute', + icon: FaHandshakeAngle, + color: 'text-gray-900 dark:text-gray-100', }, ] From ac9da44e04a7a5392a6b121cf7180c2550898c11 Mon Sep 17 00:00:00 2001 From: Anurag Yadav Date: Wed, 18 Feb 2026 01:10:36 +0530 Subject: [PATCH 6/7] added test --- .../__tests__/unit/pages/Community.test.tsx | 155 ++++++++++++++++++ frontend/src/app/community/page.tsx | 4 +- 2 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 frontend/__tests__/unit/pages/Community.test.tsx diff --git a/frontend/__tests__/unit/pages/Community.test.tsx b/frontend/__tests__/unit/pages/Community.test.tsx new file mode 100644 index 0000000000..e89f4ee23c --- /dev/null +++ b/frontend/__tests__/unit/pages/Community.test.tsx @@ -0,0 +1,155 @@ +import { render, screen } from '@testing-library/react' +import React from 'react' +import CommunityPage from 'app/community/page' + +jest.mock('react-icons/fa', () => ({ + FaArrowRight: () => , + FaSlack: () => , + FaChevronRight: () => , +})) + +jest.mock('react-icons/fa6', () => ({ + FaLocationDot: () => , + FaFolder: () => , + FaPeopleGroup: () => , + FaBuilding: () => , + FaUsers: () => , + FaHandshakeAngle: () => , +})) + +jest.mock('components/SecondaryCard', () => ({ + __esModule: true, + default: ({ + title, + children, + className, + }: { + title?: React.ReactNode + children: React.ReactNode + className?: string + }) => ( +
+ {title &&
{title}
} + {children} +
+ ), +})) + +jest.mock('components/AnchorTitle', () => ({ + __esModule: true, + default: ({ title }: { title: string }) => {title}, +})) + +jest.mock('wrappers/IconWrapper', () => ({ + IconWrapper: ({ icon: Icon, className }: { icon: React.ElementType; className: string }) => ( +
+ +
+ ), +})) + +jest.mock('utils/communityData', () => ({ + exploreCards: [ + { + title: 'Test Chapter', + description: 'Test Description', + href: '/test-chapter', + icon: () => , + color: 'text-red-500', + }, + ], + engagementWays: [ + { + title: 'Test Engagement', + description: 'Test Engagement Description', + }, + ], + journeySteps: [ + { + label: 'Test Step 1', + description: 'Test Step 1 Description', + }, + { + label: 'Test Step 2', + description: 'Test Step 2 Description', + }, + ], +})) + +describe('CommunityPage', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + test('renders the main page title and intro', () => { + render() + + expect(screen.getByRole('heading', { level: 1, name: /OWASP Community/i })).toBeInTheDocument() + expect(screen.getByText(/Explore the vibrant OWASP community/i)).toBeInTheDocument() + expect(screen.getByRole('link', { name: /Chapters/i })).toHaveAttribute('href', '/chapters') + expect(screen.getByRole('link', { name: /Members/i })).toHaveAttribute('href', '/members') + expect(screen.getByRole('link', { name: /Organizations/i })).toHaveAttribute( + 'href', + '/organizations' + ) + }) + + test('renders Explore Resources section correctly', () => { + render() + + expect(screen.getByTestId('anchor-title')).toHaveTextContent('Explore Resources') + expect(screen.getByText('Test Chapter')).toBeInTheDocument() + expect(screen.getByText('Test Description')).toBeInTheDocument() + const link = screen.getByRole('link', { name: /Test Chapter/i }) + expect(link).toHaveAttribute('href', '/test-chapter') + expect(screen.getByTestId('icon-chevron-right')).toBeInTheDocument() + }) + + test('renders Ways to Engage section correctly', () => { + render() + + expect(screen.getByText('Ways to Engage')).toBeInTheDocument() + expect(screen.getByText('Test Engagement')).toBeInTheDocument() + expect(screen.getByText('Test Engagement Description')).toBeInTheDocument() + }) + + test('renders Community Journey section correctly', () => { + render() + + expect(screen.getByText('Your Community Journey')).toBeInTheDocument() + + expect(screen.getAllByText('Test Step 1')).toHaveLength(2) + expect(screen.getAllByText('Test Step 1 Description')).toHaveLength(2) + expect(screen.getAllByText('Test Step 2')).toHaveLength(2) + + expect(screen.getAllByText('1')).toHaveLength(2) + expect(screen.getAllByText('2')).toHaveLength(2) + }) + + test('renders Join the Community section correctly', () => { + render() + + expect(screen.getByText('Join the Community')).toBeInTheDocument() + expect(screen.getByTestId('icon-slack')).toBeInTheDocument() + expect(screen.getByText(/Connect with fellow security professionals/i)).toBeInTheDocument() + + const slackLink = screen.getByRole('link', { + name: /Join OWASP Community Slack workspace/i, + }) + expect(slackLink).toHaveAttribute('href', 'https://owasp.org/slack/invite') + expect(screen.getByTestId('icon-arrow-right')).toBeInTheDocument() + }) + + test('renders Final Call to Action section correctly', () => { + render() + + expect( + screen.getByRole('heading', { level: 2, name: /Ready to Get Involved\?/i }) + ).toBeInTheDocument() + expect(screen.getByText(/Join thousands of security professionals/i)).toBeInTheDocument() + expect(screen.getByRole('link', { name: /contributing to a project/i })).toHaveAttribute( + 'href', + '/contribute' + ) + }) +}) diff --git a/frontend/src/app/community/page.tsx b/frontend/src/app/community/page.tsx index c32151d027..04f8cec6a6 100644 --- a/frontend/src/app/community/page.tsx +++ b/frontend/src/app/community/page.tsx @@ -1,10 +1,10 @@ 'use client' import Link from 'next/link' import { FaArrowRight, FaSlack, FaChevronRight } from 'react-icons/fa' +import { IconWrapper } from 'wrappers/IconWrapper' import { exploreCards as NAV_SECTIONS, engagementWays, journeySteps } from 'utils/communityData' -import SecondaryCard from 'components/SecondaryCard' import AnchorTitle from 'components/AnchorTitle' -import { IconWrapper } from 'wrappers/IconWrapper' +import SecondaryCard from 'components/SecondaryCard' export default function CommunityPage() { return ( From 57fe0a7dc67832c77cd8234583f478693ce6f4a4 Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Thu, 19 Feb 2026 16:42:44 -0800 Subject: [PATCH 7/7] Update code --- frontend/src/app/community/page.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/community/page.tsx b/frontend/src/app/community/page.tsx index 04f8cec6a6..b0d18e3ec9 100644 --- a/frontend/src/app/community/page.tsx +++ b/frontend/src/app/community/page.tsx @@ -1,4 +1,5 @@ 'use client' + import Link from 'next/link' import { FaArrowRight, FaSlack, FaChevronRight } from 'react-icons/fa' import { IconWrapper } from 'wrappers/IconWrapper'