Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
12ab911
fix: use MemberDto instead of Member in CollaboratorPage
dricazenck May 7, 2026
61adb44
refactor: harden Member and MemberDto validation with messages
dricazenck May 7, 2026
e30f366
feat: make Mentor memberTypes dynamic instead of hardcoded
dricazenck May 7, 2026
71d47ad
refactor: improve Mentee and Skills validation messages
dricazenck May 7, 2026
6cd3a4b
fix: restore builder defaults for EmailRequest and add @Validated
dricazenck May 7, 2026
c8a09bf
Merge branch 'main' of github.com:Women-Coding-Community/wcc-backend …
dricazenck May 7, 2026
34884f2
Merge branch 'main' of github.com:Women-Coding-Community/wcc-backend …
dricazenck May 9, 2026
ada8976
test: Fix integration tests
dricazenck May 9, 2026
fac334f
refactor: return MentorDto instead of Mentor in MentorshipService and…
dricazenck May 9, 2026
24fecb1
test: adjust test
dricazenck May 9, 2026
947b6b8
Merge remote-tracking branch 'origin/feat/member-type-handling-and-va…
dricazenck May 9, 2026
073de94
Merge branch 'main' of github.com:dricazenck/wcc-backend into feat/me…
dricazenck May 19, 2026
7102ac0
Merge branch 'main' of github.com:Women-Coding-Community/wcc-backend …
dricazenck Jun 13, 2026
401c060
Merge branch 'main' of github.com:Women-Coding-Community/wcc-backend …
dricazenck Jun 15, 2026
9b9adb6
refactor: Simplify mentor creation handling, remove unnecessary DTO c…
dricazenck Jun 15, 2026
4c66025
refactor: Remove validation annotations in PaginationUtil and replace…
dricazenck Jun 15, 2026
d041148
refactor: Introduce MemberBase class to reduce duplication in Member …
dricazenck Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions admin-wcc-app/__tests__/services/mentorshipService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
getMenteeApplications,
getMentorshipRecommendations,
} from '@/services/mentorshipService';
import { apiFetch } from '@/lib/api';
import {apiFetch} from '@/lib/api';

jest.mock('../../lib/api', () => ({
apiFetch: jest.fn(),
Expand All @@ -28,23 +28,23 @@ describe('mentorshipService', () => {
const result = await getMentorshipRecommendations(token);

expect(apiFetch).toHaveBeenCalledWith(
'/api/platform/v1/admin/mentorship/matches/recommendations',
{ token }
'/api/platform/v1/admin/mentorship/matches/recommendations',
{token}
);
expect(result).toEqual(mockResponse);
});
});

describe('getMenteeApplications', () => {
it('should fetch mentee applications without mentor filter', async () => {
const mockApps = [{ menteeId: 1, status: 'PENDING' }];
const mockApps = [{menteeId: 1, status: 'PENDING'}];
(apiFetch as jest.Mock).mockResolvedValue(mockApps);

const result = await getMenteeApplications(5, ['PENDING', 'ACCEPTED'], token);

expect(apiFetch).toHaveBeenCalledWith(
'/api/platform/v1/admin/mentorship/cycles/5/applications?status=PENDING%2CACCEPTED',
{ token }
'/api/platform/v1/admin/mentorship/cycles/5/applications?status=PENDING%2CACCEPTED',
{token}
);
expect(result).toEqual(mockApps);
});
Expand All @@ -55,8 +55,8 @@ describe('mentorshipService', () => {
await getMenteeApplications(5, ['PENDING'], token, 10);

expect(apiFetch).toHaveBeenCalledWith(
'/api/platform/v1/admin/mentorship/cycles/5/applications?status=PENDING&mentorId=10',
{ token }
'/api/platform/v1/admin/mentorship/cycles/5/applications?status=PENDING&mentorId=10',
{token}
);
});
});
Expand All @@ -69,7 +69,7 @@ describe('mentorshipService', () => {

expect(apiFetch).toHaveBeenCalledWith('/api/platform/v1/mentees/20/cycles/5/assign-mentor', {
method: 'POST',
body: { mentorId: 10, notes: undefined },
body: {mentorId: 10, notes: undefined},
token,
});
});
Expand All @@ -81,7 +81,7 @@ describe('mentorshipService', () => {

expect(apiFetch).toHaveBeenCalledWith('/api/platform/v1/mentees/20/cycles/5/assign-mentor', {
method: 'POST',
body: { mentorId: 10, notes: 'Good match for React skills' },
body: {mentorId: 10, notes: 'Good match for React skills'},
token,
});
});
Expand Down
100 changes: 50 additions & 50 deletions admin-wcc-app/components/AdminLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,64 @@
import React from 'react';
import { AppBar, Button, Container, Stack, Toolbar, Typography } from '@mui/material';
import {AppBar, Button, Container, Stack, Toolbar, Typography} from '@mui/material';
import Link from 'next/link';
import { useAuth } from '@/components/AuthProvider';
import {useAuth} from '@/components/AuthProvider';

export default function AdminLayout({ children }: { children: React.ReactNode }) {
const { logout, roles } = useAuth();
export default function AdminLayout({children}: { children: React.ReactNode }) {
const {logout, roles} = useAuth();

const isAdmin = roles.includes('ADMIN');
const isMentorshipAdmin = roles.includes('MENTORSHIP_ADMIN');
const isLeader = roles.includes('LEADER');
const isMentor = roles.includes('MENTOR');

return (
<>
<AppBar position="static" color="primary">
<Toolbar>
<Typography variant="h6" sx={{ flexGrow: 1 }}>
WCC Admin
</Typography>
<Stack direction="row" spacing={2}>
<Button component={Link} href="/admin" color="inherit">
Dashboard
</Button>
{(isAdmin || isMentor) && (
<Button component={Link} href="/admin/mentor" color="inherit">
Mentor Dashboard
<>
<AppBar position="static" color="primary">
<Toolbar>
<Typography variant="h6" sx={{flexGrow: 1}}>
WCC Admin
</Typography>
<Stack direction="row" spacing={2}>
<Button component={Link} href="/admin" color="inherit">
Dashboard
</Button>
)}
{(isAdmin || isMentorshipAdmin || isLeader) && (
<Button component={Link} href="/admin/mentors" color="inherit">
Mentors
{(isAdmin || isMentor) && (
<Button component={Link} href="/admin/mentor" color="inherit">
Mentor Dashboard
</Button>
)}
{(isAdmin || isMentorshipAdmin || isLeader) && (
<Button component={Link} href="/admin/mentors" color="inherit">
Mentors
</Button>
)}
{(isAdmin || isMentorshipAdmin || isLeader) && (
<Button component={Link} href="/admin/members" color="inherit">
Members
</Button>
)}
{(isAdmin || isMentorshipAdmin) && (
<Button component={Link} href="/admin/mentees" color="inherit">
Mentees
</Button>
)}
{(isAdmin || isMentorshipAdmin) && (
<Button component={Link} href="/admin/mentorship" color="inherit">
Mentorship
</Button>
)}
{(isAdmin || isLeader) && (
<Button component={Link} href="/admin/users" color="inherit">
Users
</Button>
)}
<Button onClick={logout} color="inherit">
Logout
</Button>
)}
{(isAdmin || isMentorshipAdmin || isLeader) && (
<Button component={Link} href="/admin/members" color="inherit">
Members
</Button>
)}
{(isAdmin || isMentorshipAdmin) && (
<Button component={Link} href="/admin/mentees" color="inherit">
Mentees
</Button>
)}
{(isAdmin || isMentorshipAdmin) && (
<Button component={Link} href="/admin/mentorship" color="inherit">
Mentorship
</Button>
)}
{(isAdmin || isLeader) && (
<Button component={Link} href="/admin/users" color="inherit">
Users
</Button>
)}
<Button onClick={logout} color="inherit">
Logout
</Button>
</Stack>
</Toolbar>
</AppBar>
<Container sx={{ my: 4 }}>{children}</Container>
</>
</Stack>
</Toolbar>
</AppBar>
<Container sx={{my: 4}}>{children}</Container>
</>
);
}
104 changes: 52 additions & 52 deletions admin-wcc-app/components/mentors/SkillsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Box, Chip, Typography } from '@mui/material';
import { MentorSkills } from '@/types/mentor';
import { TECHNICAL_AREAS } from '@/lib/technicalAreas';
import { PROGRAMMING_LANGUAGES } from '@/lib/programmingLanguages';
import { MENTORSHIP_FOCUS_AREAS } from '@/lib/mentorshipFocusAreas';
import { PROFICIENCY_LEVELS } from '@/lib/proficiencyLevels';
import {Box, Chip, Typography} from '@mui/material';
import {MentorSkills} from '@/types/mentor';
import {TECHNICAL_AREAS} from '@/lib/technicalAreas';
import {PROGRAMMING_LANGUAGES} from '@/lib/programmingLanguages';
import {MENTORSHIP_FOCUS_AREAS} from '@/lib/mentorshipFocusAreas';
import {PROFICIENCY_LEVELS} from '@/lib/proficiencyLevels';

interface SkillsSectionProps {
skills: MentorSkills;
Expand All @@ -25,52 +25,52 @@ function focusLabel(value: string): string {
return MENTORSHIP_FOCUS_AREAS.find((f) => f.value === value)?.label ?? value;
}

export default function SkillsSection({ skills }: SkillsSectionProps) {
export default function SkillsSection({skills}: SkillsSectionProps) {
return (
<Box sx={{ mt: 2 }}>
{skills.yearsExperience !== undefined && (
<Typography variant="body2" color="text.secondary">
{skills.yearsExperience} years experience
</Typography>
)}
{skills.languages && skills.languages.length > 0 && (
<Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{skills.languages.map((l) => (
<Chip
key={l.language}
label={
l.proficiencyLevel
? `${langLabel(l.language)} · ${profLabel(l.proficiencyLevel)}`
: langLabel(l.language)
}
size="small"
color="secondary"
/>
))}
</Box>
)}
{skills.areas && skills.areas.length > 0 && (
<Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{skills.areas.map((a) => (
<Chip
key={a.technicalArea}
label={
a.proficiencyLevel
? `${areaLabel(a.technicalArea)} · ${profLabel(a.proficiencyLevel)}`
: areaLabel(a.technicalArea)
}
size="small"
/>
))}
</Box>
)}
{skills.mentorshipFocus && skills.mentorshipFocus.length > 0 && (
<Box sx={{ mt: 2, display: 'flex', flexWrap: 'wrap', gap: 1 }}>
{skills.mentorshipFocus.map((f) => (
<Chip key={f} label={focusLabel(f)} size="small" color="info" />
))}
</Box>
)}
</Box>
<Box sx={{mt: 2}}>
{skills.yearsExperience !== undefined && (
<Typography variant="body2" color="text.secondary">
{skills.yearsExperience} years experience
</Typography>
)}
{skills.languages && skills.languages.length > 0 && (
<Box sx={{mt: 2, display: 'flex', flexWrap: 'wrap', gap: 1}}>
{skills.languages.map((l) => (
<Chip
key={l.language}
label={
l.proficiencyLevel
? `${langLabel(l.language)} · ${profLabel(l.proficiencyLevel)}`
: langLabel(l.language)
}
size="small"
color="secondary"
/>
))}
</Box>
)}
{skills.areas && skills.areas.length > 0 && (
<Box sx={{mt: 2, display: 'flex', flexWrap: 'wrap', gap: 1}}>
{skills.areas.map((a) => (
<Chip
key={a.technicalArea}
label={
a.proficiencyLevel
? `${areaLabel(a.technicalArea)} · ${profLabel(a.proficiencyLevel)}`
: areaLabel(a.technicalArea)
}
size="small"
/>
))}
</Box>
)}
{skills.mentorshipFocus && skills.mentorshipFocus.length > 0 && (
<Box sx={{mt: 2, display: 'flex', flexWrap: 'wrap', gap: 1}}>
{skills.mentorshipFocus.map((f) => (
<Chip key={f} label={focusLabel(f)} size="small" color="info"/>
))}
</Box>
)}
</Box>
);
}
Loading
Loading