diff --git a/frontend/__tests__/a11y/components/TopContributorsList.a11y.test.tsx b/frontend/__tests__/a11y/components/ContributorsList.a11y.test.tsx similarity index 82% rename from frontend/__tests__/a11y/components/TopContributorsList.a11y.test.tsx rename to frontend/__tests__/a11y/components/ContributorsList.a11y.test.tsx index 093b44acfa..4cccdc05e6 100644 --- a/frontend/__tests__/a11y/components/TopContributorsList.a11y.test.tsx +++ b/frontend/__tests__/a11y/components/ContributorsList.a11y.test.tsx @@ -2,7 +2,8 @@ import { render } from '@testing-library/react' import { axe, toHaveNoViolations } from 'jest-axe' import { ReactNode } from 'react' import { Contributor } from 'types/contributor' -import TopContributorsList from 'components/TopContributorsList' +import { getMemberUrl } from 'utils/urlFormatter' +import ContributorsList from 'components/ContributorsList' expect.extend(toHaveNoViolations) @@ -53,9 +54,11 @@ const mockContributors: Contributor[] = [ }, ] -describe('TopContributorsList Accessibility', () => { +describe('ContributorsList Accessibility', () => { it('should not have any accessibility violations', async () => { - const { container } = render() + const { container } = render( + + ) const results = await axe(container) diff --git a/frontend/__tests__/unit/components/CardDetailsPage.test.tsx b/frontend/__tests__/unit/components/CardDetailsPage.test.tsx index 4f44c9ec05..b779fa3623 100644 --- a/frontend/__tests__/unit/components/CardDetailsPage.test.tsx +++ b/frontend/__tests__/unit/components/CardDetailsPage.test.tsx @@ -359,25 +359,27 @@ jest.mock('components/ToggleableList', () => ({ ), })) -jest.mock('components/TopContributorsList', () => ({ +jest.mock('components/ContributorsList', () => ({ __esModule: true, default: ({ contributors, maxInitialDisplay, // eslint-disable-next-line @typescript-eslint/no-unused-vars icon, + label = 'Contributors', // eslint-disable-next-line @typescript-eslint/no-unused-vars - label, + getUrl, ...props }: { contributors: unknown[] icon?: unknown label?: string maxInitialDisplay: number + getUrl: (login: string) => string [key: string]: unknown }) => ( -
- Top Contributors ({contributors.length} items, max display: {maxInitialDisplay}) +
+ {label} ({contributors.length} items, max display: {maxInitialDisplay})
), })) @@ -964,7 +966,7 @@ describe('CardDetailsPage', () => { it('passes correct props to child components', () => { render() - expect(screen.getByTestId('top-contributors-list')).toHaveTextContent( + expect(screen.getByTestId('contributors-list')).toHaveTextContent( 'Top Contributors (2 items, max display: 12)' ) }) @@ -1054,9 +1056,7 @@ describe('CardDetailsPage', () => { ) expect(screen.getByTestId('health-metrics')).toHaveTextContent('Health Metrics (1 items)') - expect(screen.getByTestId('top-contributors-list')).toHaveTextContent( - 'Top Contributors (2 items' - ) + expect(screen.getByTestId('contributors-list')).toHaveTextContent('Top Contributors (2 items') expect(screen.getByTestId('repositories-card')).toHaveTextContent('Repositories (2 items)') }) @@ -1165,7 +1165,7 @@ describe('CardDetailsPage', () => { render() - expect(screen.getByTestId('top-contributors-list')).toHaveTextContent( + expect(screen.getByTestId('contributors-list')).toHaveTextContent( 'Top Contributors (50 items, max display: 12)' ) }) @@ -1225,7 +1225,7 @@ describe('CardDetailsPage', () => { render() - expect(screen.getByTestId('top-contributors-list')).toHaveTextContent( + expect(screen.getByTestId('contributors-list')).toHaveTextContent( 'Top Contributors (1000 items, max display: 12)' ) }) @@ -1375,7 +1375,7 @@ describe('CardDetailsPage', () => { expect(screen.getByText('Project summary text')).toBeInTheDocument() expect(screen.getByText('User summary content')).toBeInTheDocument() expect(screen.getByTestId('health-metrics')).toBeInTheDocument() - expect(screen.getByTestId('top-contributors-list')).toBeInTheDocument() + expect(screen.getByTestId('contributors-list')).toBeInTheDocument() expect(screen.getByTestId('repositories-card')).toBeInTheDocument() expect(screen.getByTestId('sponsor-card')).toBeInTheDocument() }) diff --git a/frontend/__tests__/unit/components/TopContributorsList.test.tsx b/frontend/__tests__/unit/components/ContributorsList.test.tsx similarity index 82% rename from frontend/__tests__/unit/components/TopContributorsList.test.tsx rename to frontend/__tests__/unit/components/ContributorsList.test.tsx index 1ac8d2ec00..5e8a3185dc 100644 --- a/frontend/__tests__/unit/components/TopContributorsList.test.tsx +++ b/frontend/__tests__/unit/components/ContributorsList.test.tsx @@ -3,7 +3,8 @@ import React from 'react' import { FaUsers } from 'react-icons/fa6' import { render } from 'wrappers/testUtil' import type { Contributor } from 'types/contributor' -import TopContributorsList from 'components/TopContributorsList' +import { getMemberUrl } from 'utils/urlFormatter' +import ContributorsList from 'components/ContributorsList' jest.mock('next/link', () => ({ __esModule: true, @@ -181,9 +182,10 @@ const mockContributors: Contributor[] = [ }, ] -describe('TopContributorsList Component', () => { +describe('ContributorsList Component', () => { const defaultProps = { contributors: mockContributors, + getUrl: getMemberUrl, } afterEach(() => { @@ -191,8 +193,8 @@ describe('TopContributorsList Component', () => { }) describe('Renders successfully with minimal required props', () => { - it('renders with minimal props (only contributors)', () => { - render() + it('renders with minimal props (only contributors and getUrl)', () => { + render() expect(screen.getByTestId('secondary-card')).toBeInTheDocument() expect(screen.getByTestId('anchor-title')).toBeInTheDocument() @@ -201,12 +203,12 @@ describe('TopContributorsList Component', () => { }) it('renders component without crashing', () => { - const { container } = render() + const { container } = render() expect(container).toBeInTheDocument() }) it('renders without crashing with empty contributors array', () => { - render() + render() // Component returns early for empty array, so no secondary card should be rendered expect(screen.queryByTestId('secondary-card')).not.toBeInTheDocument() }) @@ -214,7 +216,7 @@ describe('TopContributorsList Component', () => { describe('Conditional rendering logic', () => { it('does not render anything when contributors array is empty', () => { - render() + render() // Component returns early for empty array, so no secondary card should be rendered expect(screen.queryByTestId('secondary-card')).not.toBeInTheDocument() }) @@ -222,7 +224,11 @@ describe('TopContributorsList Component', () => { it('renders show more/less button only when contributors exceed maxInitialDisplay', () => { // Test with few contributors (should not show button) const { rerender } = render( - + ) expect(screen.queryByRole('button')).not.toBeInTheDocument() @@ -235,7 +241,13 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - rerender() + rerender( + + ) expect(screen.getByRole('button')).toBeInTheDocument() expect(screen.getByText('Show more')).toBeInTheDocument() }) @@ -249,7 +261,13 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) // Should only show 5 initially expect(screen.getAllByTestId('contributor-avatar')).toHaveLength(5) @@ -274,7 +292,7 @@ describe('TopContributorsList Component', () => { }, ] - render() + render() expect(screen.getByText('Alex Developer')).toBeInTheDocument() expect(screen.getByText('Contributor2')).toBeInTheDocument() // capitalize utility should be applied @@ -284,15 +302,15 @@ describe('TopContributorsList Component', () => { describe('Prop-based behavior', () => { it('uses custom label when provided', () => { const customLabel = 'Featured Contributors' - render() + render() expect(screen.getByText(customLabel)).toBeInTheDocument() }) it('uses default label when not provided', () => { - render() + render() - expect(screen.getByText('Top Contributors')).toBeInTheDocument() + expect(screen.getByText('Contributors')).toBeInTheDocument() }) it('respects custom maxInitialDisplay prop', () => { @@ -304,19 +322,25 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) expect(screen.getAllByTestId('contributor-avatar')).toHaveLength(3) expect(screen.getByRole('button')).toBeInTheDocument() }) it('displays icon when provided', () => { - render() + render() expect(screen.getByTestId('card-icon')).toBeInTheDocument() }) it('does not display icon when not provided', () => { - render() + render() expect(screen.queryByTestId('card-icon')).not.toBeInTheDocument() }) }) @@ -331,7 +355,13 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) // Initially shows 5 contributors expect(screen.getAllByTestId('contributor-avatar')).toHaveLength(5) @@ -364,7 +394,13 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) const toggleButton = screen.getByRole('button') @@ -390,7 +426,13 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) // Initial state - collapsed expect(screen.getAllByTestId('contributor-avatar')).toHaveLength(5) @@ -413,7 +455,9 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) // Should show first 3 contributors expect(screen.getByText('User 0')).toBeInTheDocument() @@ -433,7 +477,7 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render() // Should show 12 by default (based on component default) expect(screen.getAllByTestId('contributor-avatar')).toHaveLength(12) @@ -451,7 +495,7 @@ describe('TopContributorsList Component', () => { }, ] - render() + render() expect(screen.getByText('Testuser')).toBeInTheDocument() }) @@ -467,16 +511,16 @@ describe('TopContributorsList Component', () => { }, ] - render() + render() const avatar = screen.getByTestId('contributor-avatar') - expect(avatar).toHaveAttribute('src', '&s=60') // Should append size parameter even with empty URL + expect(avatar).toHaveAttribute('src', '?s=60') // Should append size parameter even with empty URL }) }) describe('Text and content rendering', () => { it('renders contributor names correctly', () => { - render() + render() expect(screen.getByText('Alex Developer')).toBeInTheDocument() expect(screen.getByText('Jane Developer')).toBeInTheDocument() @@ -492,7 +536,13 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) expect(screen.getByText('Show more')).toBeInTheDocument() @@ -501,7 +551,7 @@ describe('TopContributorsList Component', () => { }) it('renders title with proper structure', () => { - render() + render() expect(screen.getByTestId('anchor-title')).toBeInTheDocument() expect(screen.getByText('Custom Title')).toBeInTheDocument() @@ -527,14 +577,20 @@ describe('TopContributorsList Component', () => { }, ] - render() + render() // Should still render without crashing expect(screen.getByTestId('secondary-card')).toBeInTheDocument() }) it('handles zero maxInitialDisplay value', () => { - render() + render( + + ) // Should still show button since contributors length > 0 expect(screen.getByRole('button')).toBeInTheDocument() @@ -542,14 +598,26 @@ describe('TopContributorsList Component', () => { }) it('handles negative maxInitialDisplay value', () => { - render() + render( + + ) // slice with negative number should still work expect(screen.getByTestId('secondary-card')).toBeInTheDocument() }) it('handles very large maxInitialDisplay value', () => { - render() + render( + + ) // Should show all contributors and no button expect(screen.getAllByTestId('contributor-avatar')).toHaveLength(3) @@ -559,7 +627,7 @@ describe('TopContributorsList Component', () => { describe('Accessibility roles and labels', () => { it('renders proper image alt text and titles', () => { - render() + render() const avatars = screen.getAllByTestId('contributor-avatar') expect(avatars[0]).toHaveAttribute('alt', "Alex Developer's avatar") @@ -571,7 +639,7 @@ describe('TopContributorsList Component', () => { }) it('renders proper link titles and hrefs', () => { - render() + render() const links = screen.getAllByTestId('contributor-link') expect(links[0]).toHaveAttribute('href', '/members/developer1') @@ -591,7 +659,13 @@ describe('TopContributorsList Component', () => { name: `User ${index}`, })) - render() + render( + + ) const button = screen.getByRole('button') expect(button).toBeInTheDocument() @@ -601,7 +675,7 @@ describe('TopContributorsList Component', () => { describe('DOM structure / classNames / styles', () => { it('renders correct CSS classes on components', () => { - render() + render() const contributorItems = screen .getAllByTestId('contributor-avatar') @@ -619,7 +693,7 @@ describe('TopContributorsList Component', () => { }) it('renders proper grid structure', () => { - const { container } = render() + const { container } = render() const gridContainer = container.querySelector('.grid') expect(gridContainer).toHaveClass( @@ -631,7 +705,7 @@ describe('TopContributorsList Component', () => { }) it('renders avatar with correct dimensions and styling', () => { - render() + render() const avatars = screen.getAllByTestId('contributor-avatar') for (const avatar of avatars) { @@ -642,7 +716,7 @@ describe('TopContributorsList Component', () => { }) it('renders contributor links with proper styling', () => { - render() + render() const links = screen.getAllByTestId('contributor-link') diff --git a/frontend/__tests__/unit/components/SingleModuleCard.test.tsx b/frontend/__tests__/unit/components/SingleModuleCard.test.tsx index 91e1adc393..9eaba76be3 100644 --- a/frontend/__tests__/unit/components/SingleModuleCard.test.tsx +++ b/frontend/__tests__/unit/components/SingleModuleCard.test.tsx @@ -67,10 +67,17 @@ jest.mock('components/ModuleCard', () => ({ }), })) -jest.mock('components/TopContributorsList', () => ({ +jest.mock('components/ContributorsList', () => ({ __esModule: true, - default: ({ contributors, label }: { contributors: unknown[]; label: string }) => ( -
+ default: ({ + contributors, + label, + }: { + contributors: unknown[] + label: string + getUrl: (login: string) => string + }) => ( +
{label}: {contributors.length} contributors @@ -155,7 +162,7 @@ describe('SingleModuleCard', () => { it('renders mentors list when mentors exist', () => { render() - expect(screen.getByTestId('top-contributors-list')).toBeInTheDocument() + expect(screen.getByTestId('contributors-list')).toBeInTheDocument() expect(screen.getByText('Mentors: 2 contributors')).toBeInTheDocument() }) @@ -163,7 +170,7 @@ describe('SingleModuleCard', () => { const moduleWithoutMentors = { ...mockModule, mentors: [] } render() - expect(screen.queryByTestId('top-contributors-list')).not.toBeInTheDocument() + expect(screen.queryByTestId('contributors-list')).not.toBeInTheDocument() }) it('renders module link with correct href', () => { @@ -224,7 +231,7 @@ describe('SingleModuleCard', () => { render() expect(screen.getByText('Test Module')).toBeInTheDocument() - expect(screen.queryByTestId('top-contributors-list')).not.toBeInTheDocument() + expect(screen.queryByTestId('contributors-list')).not.toBeInTheDocument() }) it('handles undefined admins array gracefully', () => { diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index 122c96b8e6..f447e70caa 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -24,13 +24,14 @@ import { projectTimeline, projectStory, } from 'utils/aboutData' +import { getMemberUrl } from 'utils/urlFormatter' import AnchorTitle from 'components/AnchorTitle' +import ContributorsList from 'components/ContributorsList' import Leaders from 'components/Leaders' import Markdown from 'components/MarkdownWrapper' import SecondaryCard from 'components/SecondaryCard' import ShowMoreButton from 'components/ShowMoreButton' import AboutSkeleton from 'components/skeletons/AboutSkeleton' -import TopContributorsList from 'components/TopContributorsList' const leaders = { arkid15r: 'CCSP, CISSP, CSSLP', @@ -150,11 +151,12 @@ const About = () => { {topContributors && ( - )} diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 5b92c37f9d..944c13f435 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -27,9 +27,11 @@ import type { Chapter } from 'types/chapter' import type { Event } from 'types/event' import { formatDate, formatDateRange } from 'utils/dateFormatter' +import { getMemberUrl } from 'utils/urlFormatter' import AnchorTitle from 'components/AnchorTitle' import CalendarButton from 'components/CalendarButton' import ChapterMapWrapper from 'components/ChapterMapWrapper' +import ContributorsList from 'components/ContributorsList' import LeadersList from 'components/LeadersList' import LoadingSpinner from 'components/LoadingSpinner' import MovingLogos from 'components/LogoCarousel' @@ -40,7 +42,6 @@ import RecentIssues from 'components/RecentIssues' import RecentPullRequests from 'components/RecentPullRequests' import RecentReleases from 'components/RecentReleases' import SecondaryCard from 'components/SecondaryCard' -import TopContributorsList from 'components/TopContributorsList' import { TruncatedText } from 'components/TruncatedText' export default function Home() { @@ -320,10 +321,12 @@ export default function Home() { }} />
-
diff --git a/frontend/src/components/CardDetailsPage.tsx b/frontend/src/components/CardDetailsPage.tsx index ad9178d15a..c3bdabdba5 100644 --- a/frontend/src/components/CardDetailsPage.tsx +++ b/frontend/src/components/CardDetailsPage.tsx @@ -13,18 +13,19 @@ import type { ExtendedSession } from 'types/auth' import type { DetailsCardProps } from 'types/card' import { IS_PROJECT_HEALTH_ENABLED } from 'utils/env.client' import { scrollToAnchor } from 'utils/scrollToAnchor' +import { getMemberUrl, getMenteeUrl } from 'utils/urlFormatter' import { getSocialIcon } from 'utils/urlIconMappings' import AnchorTitle from 'components/AnchorTitle' import ChapterMapWrapper from 'components/ChapterMapWrapper' import ContributionHeatmap from 'components/ContributionHeatmap' import ContributionStats from 'components/ContributionStats' +import ContributorsList from 'components/ContributorsList' import EntityActions from 'components/EntityActions' import HealthMetrics from 'components/HealthMetrics' import InfoBlock from 'components/InfoBlock' import Leaders from 'components/Leaders' import LeadersList from 'components/LeadersList' import Markdown from 'components/MarkdownWrapper' -import MenteeContributorsList from 'components/MenteeContributorsList' import MetricsScoreCircle from 'components/MetricsScoreCircle' import Milestones from 'components/Milestones' import ModuleCard from 'components/ModuleCard' @@ -36,7 +37,6 @@ import SecondaryCard from 'components/SecondaryCard' import SponsorCard from 'components/SponsorCard' import StatusBadge from 'components/StatusBadge' import ToggleableList from 'components/ToggleableList' -import TopContributorsList from 'components/TopContributorsList' export type CardType = | 'chapter' @@ -305,36 +305,39 @@ const DetailsCard = ({
)} {topContributors && ( - )} {admins && admins.length > 0 && type === 'program' && ( - )} {mentors && mentors.length > 0 && ( - )} {mentees && mentees.length > 0 && ( - getMenteeUrl(programKey || '', entityKey || '', login)} /> )} {showIssuesAndMilestones(type) && ( diff --git a/frontend/src/components/TopContributorsList.tsx b/frontend/src/components/ContributorsList.tsx similarity index 85% rename from frontend/src/components/TopContributorsList.tsx rename to frontend/src/components/ContributorsList.tsx index d3bc34ca7f..ac2d9fa56e 100644 --- a/frontend/src/components/TopContributorsList.tsx +++ b/frontend/src/components/ContributorsList.tsx @@ -4,22 +4,25 @@ import Link from 'next/link' import { useState } from 'react' import type { IconType } from 'react-icons' import type { Contributor } from 'types/contributor' -import { getMemberUrl } from 'utils/urlFormatter' import AnchorTitle from 'components/AnchorTitle' import SecondaryCard from 'components/SecondaryCard' import ShowMoreButton from 'components/ShowMoreButton' -const TopContributorsList = ({ - contributors, - label = 'Top Contributors', - maxInitialDisplay = 12, - icon, -}: { +interface ContributorsListProps { contributors: Contributor[] label?: string maxInitialDisplay?: number icon?: IconType -}) => { + getUrl: (login: string) => string +} + +const ContributorsList = ({ + contributors, + label = 'Contributors', + maxInitialDisplay = 12, + icon, + getUrl, +}: ContributorsListProps) => { const [showAllContributors, setShowAllContributors] = useState(false) const toggleContributors = () => setShowAllContributors(!showAllContributors) @@ -29,7 +32,7 @@ const TopContributorsList = ({ : contributors.slice(0, maxInitialDisplay) if (contributors.length === 0) { - return + return null } return ( @@ -52,13 +55,13 @@ const TopContributorsList = ({ alt={item?.name ? `${item.name}'s avatar` : 'Contributor avatar'} className="rounded-full" height={24} - src={`${item?.avatarUrl}&s=60`} + src={`${item?.avatarUrl}${item?.avatarUrl?.includes('?') ? '&' : '?'}s=60`} title={item?.name || item?.login} width={24} /> {upperFirst(item.name) || upperFirst(item.login)} @@ -72,4 +75,4 @@ const TopContributorsList = ({ ) } -export default TopContributorsList +export default ContributorsList diff --git a/frontend/src/components/MenteeContributorsList.tsx b/frontend/src/components/MenteeContributorsList.tsx deleted file mode 100644 index f9cdf00cc8..0000000000 --- a/frontend/src/components/MenteeContributorsList.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import upperFirst from 'lodash/upperFirst' -import Image from 'next/image' -import Link from 'next/link' -import { useState } from 'react' -import type { IconType } from 'react-icons' -import type { Contributor } from 'types/contributor' -import AnchorTitle from 'components/AnchorTitle' -import SecondaryCard from 'components/SecondaryCard' -import ShowMoreButton from 'components/ShowMoreButton' - -interface MenteeContributorsListProps { - contributors: Contributor[] - label?: string - maxInitialDisplay?: number - icon?: IconType - programKey: string - moduleKey: string -} - -const MenteeContributorsList = ({ - contributors, - label = 'Mentees', - maxInitialDisplay = 12, - icon, - programKey, - moduleKey, -}: MenteeContributorsListProps) => { - const [showAllContributors, setShowAllContributors] = useState(false) - - const toggleContributors = () => setShowAllContributors(!showAllContributors) - - const displayContributors = showAllContributors - ? contributors - : contributors.slice(0, maxInitialDisplay) - - if (contributors.length === 0) { - return null - } - const getMenteeUrl = (login: string) => - `/my/mentorship/programs/${programKey}/modules/${moduleKey}/mentees/${login}` - - return ( - - -
- } - > -
- {displayContributors.map((item) => ( -
-
- {item?.name - - {upperFirst(item.name) || upperFirst(item.login)} - -
-
- ))} -
- {contributors.length > maxInitialDisplay && } - - ) -} - -export default MenteeContributorsList diff --git a/frontend/src/components/SingleModuleCard.tsx b/frontend/src/components/SingleModuleCard.tsx index 68c76f4358..68e5f7ee8c 100644 --- a/frontend/src/components/SingleModuleCard.tsx +++ b/frontend/src/components/SingleModuleCard.tsx @@ -9,11 +9,11 @@ import { HiUserGroup } from 'react-icons/hi' import { ExtendedSession } from 'types/auth' import type { Module } from 'types/mentorship' import { formatDate } from 'utils/dateFormatter' +import { getMemberUrl, getMenteeUrl } from 'utils/urlFormatter' +import ContributorsList from 'components/ContributorsList' import EntityActions from 'components/EntityActions' import Markdown from 'components/MarkdownWrapper' -import MenteeContributorsList from 'components/MenteeContributorsList' import { getSimpleDuration } from 'components/ModuleCard' -import TopContributorsList from 'components/TopContributorsList' interface SingleModuleCardProps { module: Module @@ -82,29 +82,30 @@ const SingleModuleCard: React.FC = ({ module, accessLevel {/* Mentors */} {module.mentors?.length > 0 && ( - )} {module.mentees?.length > 0 && (pathname?.startsWith('/my/mentorship') ? ( - getMenteeUrl(programKey, module.key, login)} /> ) : ( - ))}
diff --git a/frontend/src/utils/urlFormatter.ts b/frontend/src/utils/urlFormatter.ts index e801487ae2..19218eb479 100644 --- a/frontend/src/utils/urlFormatter.ts +++ b/frontend/src/utils/urlFormatter.ts @@ -1,2 +1,4 @@ export const getMemberUrl = (login: string): string => `/members/${login}` export const getProjectUrl = (projectKey: string): string => `/projects/${projectKey}` +export const getMenteeUrl = (programKey: string, moduleKey: string, login: string): string => + `/my/mentorship/programs/${programKey}/modules/${moduleKey}/mentees/${login}`