Skip to content

Commit

Permalink
Merge branch 'develop' into bugfix-2550/no-review-link
Browse files Browse the repository at this point in the history
  • Loading branch information
mkimberlin authored Oct 16, 2024
2 parents 0e1d988 + 36861c7 commit 799aa4f
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 3 deletions.
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

0 comments on commit 799aa4f

Please sign in to comment.