Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ FROM mcr.microsoft.com/playwright:v1.57.0-noble AS base

WORKDIR /app
ENV CI=true
RUN npm install -g pnpm
ENV HUSKY=0
RUN npm install -g pnpm@9.15.9

COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
Expand All @@ -20,4 +21,4 @@ CMD ["pnpm", "next", "start"]
# ---- Playwright stage ----
FROM base AS playwright
COPY playwright-tests ./playwright-tests
USER pwuser
USER pwuser
2 changes: 1 addition & 1 deletion playwright-tests/tests/home.page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ test.describe('Validate Home Page', () => {

await basePage.verifyURL('/mentorship/mentor-registration');
await basePage.verifyPageContainsText(
'Welcome to the MentorRegistrationPage',
'WCC: Registration Form for Mentors',
);
});

Expand Down
27 changes: 26 additions & 1 deletion src/components/ResourcesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@
buttonIcon?: React.ReactNode;
}

const normalizeGoogleDriveImageUrl = (imageUrl: string) => {
try {
const url = new URL(imageUrl);

if (url.hostname !== 'drive.google.com') {
return imageUrl;
}

const fileId =
url.pathname.match(/\/file\/d\/([^/]+)/)?.[1] ??

Check warning on line 31 in src/components/ResourcesCard.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=Women-Coding-Community_wcc-frontend&issues=AZ6E3Lzp5PEKNsfm8H3M&open=AZ6E3Lzp5PEKNsfm8H3M&pullRequest=283
url.searchParams.get('id');

if (!fileId) {
return imageUrl;
}

return `https://drive.google.com/thumbnail?id=${fileId}&sz=w1000`;
} catch {
return imageUrl;
}
};

export const ResourcesCard: React.FC<ResourcesCardProps> = ({
image,
title,
Expand All @@ -28,6 +50,7 @@
buttonIcon,
}) => {
const theme = useTheme();
const imageSrc = normalizeGoogleDriveImageUrl(image);

return (
<Card
Expand All @@ -48,7 +71,7 @@
<CardMedia
component="img"
height="180"
image={image}
image={imageSrc}
alt={title}
sx={{ objectFit: 'cover' }}
/>
Expand All @@ -67,6 +90,8 @@
variant="contained"
color="primary"
href={link}
target="_blank"
rel="noopener noreferrer"
sx={{
textTransform: 'none',
borderRadius: 2,
Expand Down
41 changes: 38 additions & 3 deletions src/components/__tests__/ResourcesCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,52 @@ describe('ResourcesCard', () => {
title: 'Test Card',
description: 'This is a test description',
buttonText: 'Click Me',
link: '#',
link: 'https://drive.google.com/file/d/1xPbW8BlQoLXkuAJ7m0RuvOV02Opyr445',
};

it('renders the title, description, button, and image', () => {
it('renders the title, description, button, link, and image', () => {
render(<ResourcesCard {...props} />);

expect(screen.getByText('Test Card')).toBeInTheDocument();
expect(screen.getByText('This is a test description')).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Click Me' })).toBeInTheDocument();
const link = screen.getByRole('link', { name: 'Click Me' });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute(
'href',
'https://drive.google.com/file/d/1xPbW8BlQoLXkuAJ7m0RuvOV02Opyr445',
);

const img = screen.getByRole('img');
expect(img).toHaveAttribute('src', '/test.jpg');
});

it('normalizes Google Drive file preview URLs for the image source', () => {
render(
<ResourcesCard
{...props}
image="https://drive.google.com/file/d/1AQKAp76gjk1kMX7pB7pnY5G5TMnxmDVk"
/>,
);

const img = screen.getByRole('img');
expect(img).toHaveAttribute(
'src',
'https://drive.google.com/thumbnail?id=1AQKAp76gjk1kMX7pB7pnY5G5TMnxmDVk&sz=w1000',
);
});

it('normalizes Google Drive download URLs for the image source', () => {
render(
<ResourcesCard
{...props}
image="https://drive.google.com/uc?id=1AQKAp76gjk1kMX7pB7pnY5G5TMnxmDVk&export=download"
/>,
);

const img = screen.getByRole('img');
expect(img).toHaveAttribute(
'src',
'https://drive.google.com/thumbnail?id=1AQKAp76gjk1kMX7pB7pnY5G5TMnxmDVk&sz=w1000',
);
});
});
2 changes: 2 additions & 0 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import mentors from './responses/mentors.json';
import mentorShipPage from './responses/mentorship.json';
import mentorShipCodeOfConduct from './responses/mentorshipCodeOfConduct.json';
import mentorshipFaqPageData from './responses/mentorshipFaqPage.json';
import mentorshipResourcesPage from './responses/mentorshipResources.json';
import studyGroupsPage from './responses/mentorshipStudyGroupsPage.json';

const apiBaseUrl = process.env.API_BASE_URL;
Expand Down Expand Up @@ -67,6 +68,7 @@ const pageData = {
'mentorship/code-of-conduct': mentorShipCodeOfConduct,
team: aboutUsTeam,
'mentorship/faq': mentorshipFaqPageData,
'mentorship/resources': mentorshipResourcesPage,
'mentorship/study-groups': studyGroupsPage,
};

Expand Down
78 changes: 52 additions & 26 deletions src/lib/responses/mentorshipResources.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,54 @@
{
"heroTitle": "Mentorship Resources",
"heroDescription": "Whether you’re a mentee looking to navigate your journey, a mentor aiming to provide the best guidance, or a seasoned mentor seeking quick tips, we have the tools you need. Explore our guides for insightful mentorship advice and strategies.",
"resources": [
{
"image": "/mentee_guideMedia.jpg",
"title": "Mentee’s Guide",
"description": "",
"buttonText": "View Guide",
"link": "#"
},
{
"image": "/mentor_pocketbookMedia.jpg",
"title": "Mentor’s Pocketbook",
"description": "",
"buttonText": "View Pocketbook",
"link": "#"
},
{
"image": "/mentor_guideeMedia.jpg",
"title": "Mentor’s Guide",
"description": "",
"buttonText": "View Guide",
"link": "#"
}
],
"footer": "footerData.json content"
"id": "page:MENTORSHIP_RESOURCES",
"heroSection": {
"title": "Mentorship Resources"
},
"section": {
"description": "Whether you’re a mentee looking to navigate your journey, a mentor aiming to provide the best guidance, or a seasoned mentor seeking quick tips, we have the tools you need. Explore our guides for insightful mentorship advice and strategies."
},
"resourcesSection": {
"title": "Available Resources",
"description": "Download and explore our curated mentorship materials",
"items": [
{
"title": "Mentees Guide",
"description": "A practical guide for mentees to get the most out of mentorship.",
"link": {
"label": "Download PDF",
"uri": "https://drive.google.com/file/d/1xPbW8BlQoLXkuAJ7m0RuvOV02Opyr445"
},
"image": {
"path": "/mentee_guideMedia.jpg",
"alt": "woman working on a laptop",
"type": "desktop"
}
},
{
"title": "Mentor's Pocketbook",
"description": "Quick mentor reference notes and useful mentorship tips.",
"link": {
"label": "Download PDF",
"uri": "https://drive.google.com/file/d/1rgoOTqqG4Gu6e4tw45efFspDnoYRgYd9"
},
"image": {
"path": "/mentor_pocketbookMedia.jpg",
"alt": "Image decorative",
"type": "desktop"
}
},
{
"title": "Mentor's Guide",
"description": "Guidance and best practices for mentors to support their mentees effectively.",
"link": {
"label": "Download PDF",
"uri": "https://drive.google.com/file/d/1wTkCSG95BVg-0XX4r7FnFiV24_SXlbM_"
},
"image": {
"path": "/mentor_guideeMedia.jpg",
"alt": "two women sitting by a desk and talking together",
"type": "desktop"
}
}
]
}
}
50 changes: 31 additions & 19 deletions src/pages/mentorship/resources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ import { Title, ResourcesCard, Footer, BreadCrumbsDynamic } from '@components';
import { useIsMobile } from '@utils/theme-utils';
import { FooterResponse, MentorshipResourcesResponse } from '@utils/types';
import { fetchData } from 'lib/api';
import footerData from 'lib/responses/footer.json';
import pageData from 'lib/responses/mentorshipResources.json';

type CombinedResponse = {
data: MentorshipResourcesResponse;
footer: FooterResponse;
};

const MentorshipResourcesPage: React.FC = () => {
type MentorshipResourcesPageProps = {
data?: MentorshipResourcesResponse;
footer: FooterResponse;
};

const MentorshipResourcesPage: React.FC<MentorshipResourcesPageProps> = ({
data,
footer,
}) => {
const isMobile = useIsMobile();
const { heroTitle, heroDescription, resources } = pageData;
const page = data as MentorshipResourcesResponse;
const { heroSection, section, resourcesSection } = page;

return (
<>
Expand All @@ -27,7 +34,7 @@ const MentorshipResourcesPage: React.FC = () => {
sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}
>
<Box sx={{ flexGrow: 1 }}>
<Title title={heroTitle} />
<Title title={heroSection.title} />

<Box
sx={{
Expand All @@ -44,7 +51,7 @@ const MentorshipResourcesPage: React.FC = () => {
lineHeight: 1.5,
}}
>
{heroDescription}
{section.description}
</Typography>
</Box>

Expand All @@ -57,22 +64,27 @@ const MentorshipResourcesPage: React.FC = () => {
}}
>
<Grid container spacing={4}>
{resources.map((res, index) => (
<Grid item xs={12} sm={6} md={6} lg={4} key={index}>
<ResourcesCard
image={res.image}
title={res.title}
description={res.description}
buttonText={res.buttonText}
link={res.link}
buttonIcon={<OpenInNewIcon />}
/>
</Grid>
))}
{resourcesSection.items.map((res, index) => {
const image =
typeof res.image === 'string' ? res.image : res.image.path;

return (
<Grid item xs={12} sm={6} md={6} lg={4} key={index}>
<ResourcesCard
image={image}
title={res.title}
description={res.description ?? ''}
buttonText={res.link.label}
link={res.link.uri}
buttonIcon={<OpenInNewIcon />}
/>
</Grid>
);
})}
</Grid>
</Box>
</Box>
<Footer {...footerData} />
<Footer {...footer} />
</Box>
</>
);
Expand Down
3 changes: 2 additions & 1 deletion src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,9 @@ export type LongTermTimeLineResponse = {

export type ResourceItem = {
title: string;
description?: string;
link: Link;
image: Image;
image: string | Image;
};

export type ResourcesSection = {
Expand Down
Loading