Skip to content
Merged
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
17 changes: 17 additions & 0 deletions web-ui/src/api/certification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { resolve } from './api.js';

const certificationUrl = '/services/certification';

export const getCertifications = async cookie => {
return resolve({
url: certificationUrl,
headers: { 'X-CSRF-Header': cookie, Accept: 'application/json' }
});
};

export const getCertification = async (id, cookie) => {
return resolve({
url: `${certificationUrl}/${id}`,
headers: { 'X-CSRF-Header': cookie, Accept: 'application/json' }
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.earned-certification-badges {
img {
max-height: 5rem;
}

.MuiCardContent-root {
display: flex;
gap: 1rem;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import PropTypes from 'prop-types';
import React, { useCallback, useContext, useEffect, useState } from 'react';

import {Card, CardContent, CardHeader, Chip, Tooltip} from '@mui/material';

import { resolve } from '../../api/api.js';
import { AppContext } from '../../context/AppContext';
import { selectCsrfToken } from '../../context/selectors';

import './EarnedCertificationBadges.css';
import certifications from "../certifications/Certifications.jsx";

const earnedCertificationBaseUrl = '/services/earned-certification';

const propTypes = {
memberId: PropTypes.string, certifications: PropTypes.array,
};
const EarnedCertificationBadges = ({ memberId, certifications }) => {
const [earnedCertifications, setEarnedCertifications] = useState([]);

const { state } = useContext(AppContext);
const csrf = selectCsrfToken(state);

const loadCertifications = useCallback(async () => {
const res = await resolve({
method: 'GET',
url: earnedCertificationBaseUrl + '?memberId=' + memberId,
headers: {
'X-CSRF-Header': csrf,
Accept: 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
}
});
if (res.error) return;

const certifications = res.payload.data;
setEarnedCertifications(certifications);
}, [csrf]);

useEffect(() => {
if (csrf) loadCertifications();
}, [csrf]);

if (earnedCertifications.length === 0 || !certifications) return null;
return (
<Card className="earned-certification-badges">
<CardHeader
title="Earned Certifications"
titleTypographyProps={{ variant: 'h5', component: 'h1' }}
/>
<CardContent>
{earnedCertifications.map(earnedCert => {
// Find the corresponding certification using earnedCert.certificationId
const cert = certifications.find(c => c.id === earnedCert.certificationId);
// If no matching cert is found, skip rendering for that earnedCert
if (!cert) return null;
if (cert.badgeUrl && cert.badgeUrl.trim().length > 0) {
return (
<Tooltip
key={earnedCert.id}
title={
<>
{cert.name} <br />
Issued on: {earnedCert.earnedDate} <br />
Expires on: {earnedCert.expirationDate}
</>
}
>
{earnedCert.validationUrl ? (
<a href={earnedCert.validationUrl} target="_blank" rel="noopener noreferrer">
<img
alt={`${cert.name}, Issued on: ${earnedCert.earnedDate}, Expires on: ${earnedCert.expirationDate}`}
src={cert.badgeUrl}
/>
</a>
) : (
<img
alt={`${cert.name}, Issued on: ${earnedCert.earnedDate}, Expires on: ${earnedCert.expirationDate}`}
src={cert.badgeUrl}
/>
)}
</Tooltip>
);
} else {
return (
<>
{earnedCert.validationUrl ? (
<a href={earnedCert.validationUrl} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none' }}>
<Chip
sx={{
height: 'auto',
'& .MuiChip-label': {
display: 'block',
whiteSpace: 'normal',
},
}}
className="chip"
color="primary"
key={earnedCert.id}
label={
<>
{cert.name} <br />
Issued on: {earnedCert.earnedDate}<br />
Expires on: {earnedCert.expirationDate}
</>
}
/>
</a>
) : (
<Chip
sx={{
height: 'auto',
'& .MuiChip-label': {
display: 'block',
whiteSpace: 'normal',
},
}}
className="chip"
color="primary"
key={earnedCert.id}
label={
<>
{cert.name} <br />
Issued on: {earnedCert.earnedDate}<br />
Expires on: {earnedCert.expirationDate}
</>
}
/>
)}
</>
);
}
})}
</CardContent>
</Card>
);

};

EarnedCertificationBadges.propTypes = propTypes;

export default EarnedCertificationBadges;
23 changes: 23 additions & 0 deletions web-ui/src/context/AppContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
UPDATE_MEMBER_PROFILES,
UPDATE_TERMINATED_MEMBERS,
UPDATE_SKILLS,
UPDATE_CERTIFICATIONS,
UPDATE_TEAMS,
UPDATE_PEOPLE_LOADING,
UPDATE_TEAMS_LOADING
Expand All @@ -26,6 +27,7 @@ import { BASE_API_URL } from '../api/api';
import { getAllGuilds } from '../api/guild';
import { getSkills } from '../api/skill';
import { getAllTeams } from '../api/team';
import {getCertifications} from "../api/certification.js";

const AppContext = React.createContext();

Expand All @@ -51,6 +53,7 @@ const AppContextProvider = props => {
memberProfiles,
checkins,
skills,
certifications,
roles,
userRoles
} = state;
Expand Down Expand Up @@ -214,6 +217,26 @@ const AppContextProvider = props => {
}
}, [csrf, skills]);

useEffect(() => {
const getAllCertifications = async () => {
const res = await getCertifications(csrf);
const data =
res &&
res.payload &&
res.payload.data &&
res.payload.status === 200 &&
!res.error
? res.payload.data
: null;
if (data && data.length > 0) {
dispatch({ type: UPDATE_CERTIFICATIONS, payload: data });
}
};
if (csrf && !certifications) {
getAllCertifications();
}
}, [csrf, certifications]);

useEffect(() => {
const getRoles = async () => {
const res = await getAllRoles(csrf);
Expand Down
1 change: 1 addition & 0 deletions web-ui/src/context/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const UPDATE_MEMBER_SKILLS = '@@check-ins/update_member_skills';
export const ADD_ROLE = '@@check-ins/add_role';
export const UPDATE_SKILL = '@@check-ins/update_skill';
export const UPDATE_SKILLS = '@@check-ins/update_skills';
export const UPDATE_CERTIFICATIONS = '@@check-ins/update_certifications';
export const UPDATE_TEAM_MEMBERS = '@@check-ins/update_team_members';
export const UPDATE_TEAMS = '@@check-ins/update_teams';
export const UPDATE_TERMINATED_MEMBERS =
Expand Down
4 changes: 4 additions & 0 deletions web-ui/src/context/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
UPDATE_MEMBER_SKILLS,
UPDATE_SKILL,
UPDATE_SKILLS,
UPDATE_CERTIFICATIONS,
UPDATE_GUILD,
UPDATE_GUILDS,
ADD_ROLE,
Expand Down Expand Up @@ -113,6 +114,9 @@ export const reducer = (state, action) => {
case UPDATE_SKILLS:
state.skills = action.payload;
break;
case UPDATE_CERTIFICATIONS:
state.certifications = action.payload;
break;
case SET_CSRF:
state.csrf = action.payload;
break;
Expand Down
6 changes: 3 additions & 3 deletions web-ui/src/pages/MemberProfilePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import { getTeamByMember } from '../api/team';
import { getGuildsForMember } from '../api/guild';
import { getAvatarURL } from '../api/api.js';
import ProfilePage from './ProfilePage';
import CertificationBadges from '../components/certifications/CertificationBadges';
import VolunteerBadges from '../components/volunteer/VolunteerBadges';
import { levelList } from '../context/util';
import StarIcon from '@mui/icons-material/Star';
import KudosDialog from '../components/kudos_dialog/KudosDialog';
import EarnedCertificationBadges from "../components/earned-certifications/EarnedCertificationBadges.jsx";

import {
Avatar,
Expand All @@ -40,7 +40,7 @@ import './MemberProfilePage.css';
const MemberProfilePage = () => {
const { state } = useContext(AppContext);
const history = useHistory();
const { csrf, skills, userProfile } = state;
const { csrf, skills, certifications, userProfile } = state;
const { memberId } = useParams();
const [selectedMember, setSelectedMember] = useState(null);
const [kudosDialogOpen, setKudosDialogOpen] = useState(false);
Expand Down Expand Up @@ -330,7 +330,7 @@ const MemberProfilePage = () => {
</div>
</CardContent>
</Card>
<CertificationBadges memberId={memberId} />
<EarnedCertificationBadges memberId={memberId} certifications={certifications} />
<VolunteerBadges memberId={memberId} />
</Grid>
</Grid>
Expand Down