From 78b4b3b6c13b3ca7c3516f30137f48470ee7c03d Mon Sep 17 00:00:00 2001 From: berniceu <113672733+berniceu@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:12:57 +0200 Subject: [PATCH] #105 Ft applicant dashboard (#197) * add applicant dashboard page * add dashboard cards * integrate with backend * fetch all data from their respective queries * remove console logs * update cohort types and query * fix routes * fix code climate * add graph * fix chart * delete yarn * fix graph and login * fix * trigger * revert --------- Co-authored-by: uwituzeb --- package.json | 10 +- src/assets/assets/calendar.svg | 9 + src/assets/assets/collaborative-learning.svg | 9 + src/assets/assets/performance.svg | 9 + src/assets/assets/strategy.svg | 9 + src/components/form/SignInForm.tsx | 5 + src/components/sidebar/sidebarItems.tsx | 5 + src/pages/Applicant/ApplicantDashboard.tsx | 295 +++++++++++++++++++ src/redux/actions/TraineeAction.ts | 48 ++- src/redux/actions/attendanceAction.ts | 31 ++ src/redux/actions/cohortActions.ts | 49 ++- src/redux/actions/login.ts | 6 + src/redux/actions/performanceAction.ts | 32 ++ src/redux/actiontypes/TraineeType.ts | 33 +++ src/redux/actiontypes/attendanceTypes.ts | 10 + src/redux/actiontypes/cohortTypes.ts | 14 + src/redux/actiontypes/performanceTypes.ts | 10 + src/redux/index.ts | 4 + src/redux/reducers/attendanceReducer.ts | 30 ++ src/redux/reducers/cohortReducer.ts | 11 +- src/redux/reducers/index.ts | 5 + src/redux/reducers/performanceReducer.ts | 30 ++ src/redux/reducers/traineeReducer.ts | 23 +- src/routes/routes.tsx | 19 +- 24 files changed, 678 insertions(+), 28 deletions(-) create mode 100644 src/assets/assets/calendar.svg create mode 100644 src/assets/assets/collaborative-learning.svg create mode 100644 src/assets/assets/performance.svg create mode 100644 src/assets/assets/strategy.svg create mode 100644 src/pages/Applicant/ApplicantDashboard.tsx create mode 100644 src/redux/actions/attendanceAction.ts create mode 100644 src/redux/actions/performanceAction.ts create mode 100644 src/redux/actiontypes/attendanceTypes.ts create mode 100644 src/redux/actiontypes/cohortTypes.ts create mode 100644 src/redux/actiontypes/performanceTypes.ts create mode 100644 src/redux/reducers/attendanceReducer.ts mode change 100644 => 100755 src/redux/reducers/index.ts create mode 100644 src/redux/reducers/performanceReducer.ts diff --git a/package.json b/package.json index a72961457..f14e538fc 100755 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@babel/preset-env": "^7.19.3", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", - "@expo/webpack-config": "^0.17.2", + "@expo/webpack-config": "^19.0.1", "@iconify/react": "^4.0.0", "@testing-library/dom": "^8.19.0", "@testing-library/jest-dom": "^5.16.5", @@ -53,7 +53,7 @@ "html-webpack-plugin": "^5.6.0", "i": "^0.3.7", "node-polyfill-webpack-plugin": "^2.0.1", - "npm": "^8.19.2", + "npm": "^10.8.3", "postcss": "^8.4.14", "postcss-loader": "^7.0.0", "process": "^0.11.10", @@ -83,7 +83,7 @@ "@mui/x-date-pickers": "^5.0.6", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", - "@tinymce/tinymce-react": "^4.2.0", + "@tinymce/tinymce-react": "^5.1.1", "@types/react-i18next": "^8.1.0", "@types/react-router": "^5.1.19", "@types/react-router-dom": "^5.3.3", @@ -120,14 +120,14 @@ "react-hot-toast": "^2.4.1", "react-i18next": "^11.18.6", "react-icons": "^4.6.0", - "react-js-pagination": "^3.0.3", + "react-js-pagination": "^3.0.2", "react-loader-spinner": "^6.1.6", "react-modal": "^3.16.1", "react-paginate": "^8.1.3", "react-redux": "^8.0.4", "react-render-html": "^0.6.0", "react-router-dom": "^6.4.2", - "react-scripts": "^5.0.1", + "react-scripts": "^3.0.1", "react-select": "^5.7.4", "react-table": "^7.8.0", "react-toastify": "^9.0.8", diff --git a/src/assets/assets/calendar.svg b/src/assets/assets/calendar.svg new file mode 100644 index 000000000..7856f1d1f --- /dev/null +++ b/src/assets/assets/calendar.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/assets/collaborative-learning.svg b/src/assets/assets/collaborative-learning.svg new file mode 100644 index 000000000..467a7d6b8 --- /dev/null +++ b/src/assets/assets/collaborative-learning.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/assets/performance.svg b/src/assets/assets/performance.svg new file mode 100644 index 000000000..0223d35cc --- /dev/null +++ b/src/assets/assets/performance.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/assets/strategy.svg b/src/assets/assets/strategy.svg new file mode 100644 index 000000000..8df00f27a --- /dev/null +++ b/src/assets/assets/strategy.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/form/SignInForm.tsx b/src/components/form/SignInForm.tsx index 0d070bf0b..3c80f7848 100644 --- a/src/components/form/SignInForm.tsx +++ b/src/components/form/SignInForm.tsx @@ -77,8 +77,13 @@ const LoginForm = () => { const response = await loginAction(validatedData.email, validatedData.password); const token = response?.data?.data?.login?.token; + const userId = response?.data?.data?.login?.userId; + if (token) { localStorage.setItem("access_token", token); + if (userId) { + localStorage.setItem("userId", userId); + } await redirectAfterLogin(); } else { toast.error(response?.data?.errors[0].message); diff --git a/src/components/sidebar/sidebarItems.tsx b/src/components/sidebar/sidebarItems.tsx index 5802b939e..1f3752cd5 100644 --- a/src/components/sidebar/sidebarItems.tsx +++ b/src/components/sidebar/sidebarItems.tsx @@ -58,6 +58,11 @@ export const sidebarItems1 = [ ]; export const applicantSidebarItems = [ + { + path: "/applicant", + icon: , + title: "Dashboard", + }, { path: "myApplications", icon: , diff --git a/src/pages/Applicant/ApplicantDashboard.tsx b/src/pages/Applicant/ApplicantDashboard.tsx new file mode 100644 index 000000000..0439cc857 --- /dev/null +++ b/src/pages/Applicant/ApplicantDashboard.tsx @@ -0,0 +1,295 @@ +import React from "react"; +import { useState, useEffect } from "react"; +import NavBar from "components/sidebar/navHeader"; +import { getTraineeApplicant } from "../../redux/actions/TraineeAction"; +import { getTraineeAttendance } from "../../redux/actions/attendanceAction"; +import { getTraineePerformance } from "../../redux/actions/performanceAction"; +import { getTraineeByUserId } from "../../redux/actions/TraineeAction"; +import { getCohort } from "../../redux/actions/cohortActions"; +import { useDispatch, useSelector } from "react-redux"; +import { + Area, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + Scatter, + ComposedChart, + Label, + Line, + AreaChart, + CartesianGrid + } from "recharts"; + +const calendar: string = require("../../assets/assets/calendar.svg").default; +const collaboration: string = + require("../../assets/assets/collaborative-learning.svg").default; +const performanceIcon: string = + require("../../assets/assets/performance.svg").default; +const strategy: string = require("../../assets/assets/strategy.svg").default; + +const DashboardCard = ({ title, value, img }) => { + return ( + <> +
+
+
+ {title} +
+
+

{title}

+

{value}

+
+
+
+ + ); +}; + +const ApplicantChart = ({ performance }) => { + const transformPerformanceData = (performanceData) => { + if (!performanceData?.performances) return []; + + const sortedPerformances = [...performanceData.performances].sort((a, b) => { + return parseInt(a.date) - parseInt(b.date); + }); + + + return sortedPerformances.map((performance, index) => ({ + name: index + 1, + score: performance.score || 0, + + })); + + }; + + const chartData = transformPerformanceData(performance); + + if (chartData.length === 0) { + return
No performance data available
; + } + + + + return ( + + {chartData.length === 1 ? ( + + + + + + + + + + ) : ( + + + + + + + + + + + + + + + + + )} + + ); + } + + +const useTraineeData = () => { + const dispatch = useDispatch(); + const traineeId = useSelector((state: any) => state.traineeApplicant.currentTrainee); + const traineeData = useSelector((state: any) => state.traineeApplicant.data); + const attendance = useSelector((state: any) => state.traineeAttendance); + const performance = useSelector((state: any) => state.traineePerformance); + const cohort = useSelector((state: any) => state.cohorts); + + useEffect(() => { + const userId = localStorage.getItem("userId"); + if (userId) { + dispatch(getTraineeByUserId(userId)); + } + }, [dispatch]); + + useEffect(() => { + const fetchTraineeData = async () => { + if (!traineeId) return; + try { + await dispatch(getTraineeApplicant(traineeId)); + await dispatch(getTraineeAttendance(traineeId)); + await dispatch(getTraineePerformance(traineeId)); + } catch (err) { + console.error("Failed to fetch trainee details:", err); + } + }; + fetchTraineeData(); + }, [dispatch, traineeId]); + + useEffect(() => { + const fetchCohortData = async () => { + if (traineeData && traineeData.cohort) { + try { + await dispatch(getCohort(traineeData.cohort)); + } catch (err) { + console.error("Failed to fetch cohort data:", err); + } + } + }; + fetchCohortData(); + }, [dispatch, traineeData]); + + + return { traineeData, attendance, performance, cohort }; + +} + +const formatters = { + extractCohortNumber: (cohortTitle: string): string => { + if (!cohortTitle) return "N/A"; + const match = cohortTitle.match(/\d+/); + return match ? match[0] : "N/A"; + }, + + formatPercentage: (value: number | string | undefined): string => { + if (typeof value === 'number') { + return value % 1 === 0 ? `${value}%` : `${value.toFixed(1)}%`; + } + return 'N/A'; + }, + + calculateAttendancePercentage: (ratio: string | undefined): string => { + if (!ratio) return 'N/A'; + const [attended, total] = ratio.split('/').map(Number); + if (isNaN(attended) || isNaN(total) || total === 0) return 'N/A'; + return formatters.formatPercentage(attended / total * 100); + } +} + +const ApplicantDashboard = (props: any) => { + + const { cohort, performance, attendance } = useTraineeData(); + const cohortData = cohort?.traineeCohort || {}; + const { extractCohortNumber, formatPercentage, calculateAttendancePercentage } = formatters; + + const dashboardData = [ + { + title: "Cohort", + value: cohortData.title ? extractCohortNumber(cohortData.title) : "N/A", + img: collaboration, + }, + { + title: "Current Phase", + value: cohortData.phase || "N/A", + img: strategy, + }, + { + title: "Performance", + value: formatPercentage(performance?.averageScore), + img: performanceIcon, + }, + { + title: "Attendance", + value: calculateAttendancePercentage(attendance?.attendanceRatio), + img: calendar, + }, + ]; + + return ( + <> +
+
+
+
+
+ {dashboardData.map((item, index) => ( + + ))} +
+
+
+

+ My overall performance +

+

+ As of {new Date().toLocaleDateString('en-US', { + day: 'numeric', + month: 'long', + year: 'numeric' + })} +

+
+ +
+
+
+
+
+ + ); +}; + +export default ApplicantDashboard; diff --git a/src/redux/actions/TraineeAction.ts b/src/redux/actions/TraineeAction.ts index 24a307f43..3618d7614 100644 --- a/src/redux/actions/TraineeAction.ts +++ b/src/redux/actions/TraineeAction.ts @@ -1,5 +1,5 @@ import creator from "./creator"; -import { GET_TRAINEE, CREATE_TRAINEES, CREATE_CYCLE_ERROR } from ".."; +import { GET_TRAINEE, CREATE_TRAINEES, CREATE_CYCLE_ERROR, SET_TRAINEE } from ".."; import { toast } from "react-toastify"; import axios from "axios"; @@ -108,3 +108,49 @@ export const createTrainee = return dispatch(creator(CREATE_CYCLE_ERROR, error)); } }; + + +export const getTraineeApplicant = (traineeId: string) => async(dispatch: any) => { + try{ + const response = await axios.post(`${process.env.BACKEND_URL}`, { + query: ` + query GetOneTrainee($ID: ID!) { + getOneTrainee(ID: $ID) { + _id + applicationPhase + cohort + } + } + `, + variables: { ID: traineeId } + }); + if (response.data.errors) { + console.error('GraphQL Errors:', response.data.errors); + return; + } + const trainee = response.data.data.getOneTrainee; + dispatch(creator(GET_TRAINEE, trainee)); + + }catch (error: any) { + console.error('Error fetching trainee:', error); + console.error('Error response:', error.response?.data); + } +} + +export const getTraineeByUserId = (userId: string) => async (dispatch: any) => { + try { + const response = await axios.post(`${process.env.BACKEND_URL}`, { + query: ` + query GetTraineeByUserId($userId: ID!) { + getTraineeByUserId(userId: $userId) + } + `, + variables: { userId }, + }); + + const traineeData = response.data.data.getTraineeByUserId; + dispatch(creator(SET_TRAINEE, traineeData)); + } catch (error) { + console.error("Error fetching trainee:", error); + } +}; diff --git a/src/redux/actions/attendanceAction.ts b/src/redux/actions/attendanceAction.ts new file mode 100644 index 000000000..d7ceeca62 --- /dev/null +++ b/src/redux/actions/attendanceAction.ts @@ -0,0 +1,31 @@ +import axios from "axios"; +import creator from "./creator"; +import { GET_TRAINEE_ATTENDANCE } from ".."; + +export const getTraineeAttendance = (traineeId: any) => async (dispatch: any) => { + try { + const response = await axios.post(`${process.env.BACKEND_URL}`, { + query: ` + query GetTraineeAttendance($traineeId: ID!) { + getTraineeAttendance(traineeId: $traineeId) { + attendances { + id + date + status + } + attendanceRatio + } + } + `, + variables: { traineeId }, + }); + if (response.data.errors) { + console.error('GraphQL Errors:', response.data.errors); + return; + } + const attendanceData = response.data.data.getTraineeAttendance; + dispatch(creator(GET_TRAINEE_ATTENDANCE, attendanceData)); + } catch (err: any) { + console.error("Error fetching trainee attendance", err); + } +} \ No newline at end of file diff --git a/src/redux/actions/cohortActions.ts b/src/redux/actions/cohortActions.ts index adeb54a6f..1cddbb8be 100644 --- a/src/redux/actions/cohortActions.ts +++ b/src/redux/actions/cohortActions.ts @@ -1,7 +1,7 @@ -import creator from './creator'; -import { GET_COHORTS } from '..'; -import axios from './axiosconfig'; -import { toast } from 'react-toastify'; +import creator from "./creator"; +import { GET_COHORTS, GET_TRAINEE_COHORT } from ".."; +import axios from "./axiosconfig"; +import { toast } from "react-toastify"; export const getAllCohorts = () => async (dispatch: any) => { try { @@ -37,3 +37,44 @@ export const getAllCohorts = () => async (dispatch: any) => { return 0; } }; + +export const getCohort =(getCohortId: any) => async (dispatch: any) => { + try { + const response= await axios.post(`${process.env.BACKEND_URL}`, { + query: ` + query GetCohort($getCohortId: ID!) { + getCohort(id: $getCohortId) { + title + trainees + end + id + phase + start + cycle { + endDate + id + name + startDate + } + program { + _id + description + duration + mainObjective + modeOfExecution + requirements + title + } + } +} + `, + variables: { getCohortId } + }); + const cohortData = await response.data.data.getCohort; + dispatch(creator(GET_TRAINEE_COHORT, cohortData)); + } catch (error) { + if (error) { + return console.log(error); + } + } +}; diff --git a/src/redux/actions/login.ts b/src/redux/actions/login.ts index dc1bcc2bd..1f57dce07 100644 --- a/src/redux/actions/login.ts +++ b/src/redux/actions/login.ts @@ -7,6 +7,7 @@ export const loginAction = async (email, password) => { mutation Mutation($email: String!, $password: String!) { login(email: $email, password: $password) { token + userId } } `, @@ -21,3 +22,8 @@ export const loginAction = async (email, password) => { console.log(error); } }; + +export const setUser = (userData) => ({ + type: 'SET_USER', + payload: userData, +}); \ No newline at end of file diff --git a/src/redux/actions/performanceAction.ts b/src/redux/actions/performanceAction.ts new file mode 100644 index 000000000..6b496a48d --- /dev/null +++ b/src/redux/actions/performanceAction.ts @@ -0,0 +1,32 @@ +import axios from "axios"; +import { GET_TRAINEE_PERFORMANCE } from ".."; +import creator from "./creator"; + +export const getTraineePerformance = (traineeId: any) => async (dispatch: any) => { + try{ + const response = await axios.post(`${process.env.BACKEND_URL}`, { + query: ` + query GetTraineePerformance($traineeId: ID!) { + getTraineePerformance(traineeId: $traineeId) { + performances { + id + score + date + } + averageScore + + } + + } + + `, + variables: { traineeId } + }) + + const performanceData = response.data.data.getTraineePerformance; + dispatch(creator(GET_TRAINEE_PERFORMANCE, performanceData)); + + } catch (error) { + console.error('Error fetching trainee performance:', error); + } +} \ No newline at end of file diff --git a/src/redux/actiontypes/TraineeType.ts b/src/redux/actiontypes/TraineeType.ts index f2f2a6a14..09984cc8f 100755 --- a/src/redux/actiontypes/TraineeType.ts +++ b/src/redux/actiontypes/TraineeType.ts @@ -45,5 +45,38 @@ // export type add = addactionPending | addactionSuccess | addactionFail; // export type view = viewactionPending | viewactionSuccess | viewactionFail; +interface User { + id: string; + email: string; + firstName: string; + lastName: string; + } +export interface Trainee { + id: string; + firstName: string; + lastName: string; + email: string; + status: string; + applicationPhase: string; + user?: User; + cohort?: { + id: string; + title: string; + program: string; + cycle: string; + start: string; + end: string; + phase: string; + }; + } +export const setTrainee = (traineeData) => ({ + type: 'SET_TRAINEE', + payload: traineeData + }); + + export const setUser = (userData) => ({ + type: 'SET_USER', + payload: userData + }); diff --git a/src/redux/actiontypes/attendanceTypes.ts b/src/redux/actiontypes/attendanceTypes.ts new file mode 100644 index 000000000..beed729ee --- /dev/null +++ b/src/redux/actiontypes/attendanceTypes.ts @@ -0,0 +1,10 @@ +export interface Attendance { + id: string; + date: string; + status: string; + } + +export interface AttendanceData { + attendances: Attendance[]; + attendanceRatio: number; +} \ No newline at end of file diff --git a/src/redux/actiontypes/cohortTypes.ts b/src/redux/actiontypes/cohortTypes.ts new file mode 100644 index 000000000..0a0aa74c7 --- /dev/null +++ b/src/redux/actiontypes/cohortTypes.ts @@ -0,0 +1,14 @@ +export interface Cohort { + id: string; + title: string | null; + program: string | null; + cycle: string | null; + start: string | null; + end: string | null; + phase: 'core' | 'team' | 'apprenticeship' | null; + trainees: string[] | null; +} + +export interface CohortData { + cohort: Cohort[]; + } diff --git a/src/redux/actiontypes/performanceTypes.ts b/src/redux/actiontypes/performanceTypes.ts new file mode 100644 index 000000000..e9422916b --- /dev/null +++ b/src/redux/actiontypes/performanceTypes.ts @@ -0,0 +1,10 @@ +export interface Performance { + id: string; + score: number; + date: string; + } + + export interface PerformanceData { + performances: Performance[]; + averageScore: number; + } \ No newline at end of file diff --git a/src/redux/index.ts b/src/redux/index.ts index 39dcafc96..4bb885277 100755 --- a/src/redux/index.ts +++ b/src/redux/index.ts @@ -2,6 +2,7 @@ export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; export const GET_ONE_TRAINEES_ALL_DETAILS = 'GET_ONE_TRAINEES_ALL_DETAILS'; export const GET_TRAINEE = 'GET_TRAINEE'; +export const SET_TRAINEE = 'SET_TRAINEE'; export const CREATE_TRAINEES = 'CREATE_TRAINEES'; export const GET_CYCLES = 'GET_CYCLES'; export const CREATE_CYCLES = 'CREATE_CYCLES'; @@ -56,4 +57,7 @@ export const MY_APPLICATIONS = 'MY_APPLICATIONS'; export const GET_PROGRAMS = 'GET_PROGRAMS'; export const GET_COHORTS = 'GET_COHORTS'; +export const GET_TRAINEE_COHORT = 'GET__TRAINEE_COHORT'; export const GET_ONE_JOB_POST_ALL_DETAILS = 'GET_ONE_JOB_POST_ALL_DETAILS'; +export const GET_TRAINEE_ATTENDANCE = 'GET_TRAINEE_ATTENDANCE'; +export const GET_TRAINEE_PERFORMANCE = 'GET_TRAINEE_PERFORMANCE'; \ No newline at end of file diff --git a/src/redux/reducers/attendanceReducer.ts b/src/redux/reducers/attendanceReducer.ts new file mode 100644 index 000000000..2904605f9 --- /dev/null +++ b/src/redux/reducers/attendanceReducer.ts @@ -0,0 +1,30 @@ +import { GET_TRAINEE_ATTENDANCE } from ".."; + +interface AttendanceState { + attendances: any[]; + attendanceRatio: number | null; + loading: boolean; + error: string | null; + } + + const initialState: AttendanceState = { + attendances: [], + attendanceRatio: null, + loading: false, + error: null + }; + + export const attendanceReducer = (state = initialState, action: any) => { + switch (action.type) { + case GET_TRAINEE_ATTENDANCE: + return { + ...state, + attendances: action.payload.attendances, + attendanceRatio: action.payload.attendanceRatio, + loading: false, + error: null + }; + default: + return state; + } + }; \ No newline at end of file diff --git a/src/redux/reducers/cohortReducer.ts b/src/redux/reducers/cohortReducer.ts index 9b26bee1e..09ca61906 100644 --- a/src/redux/reducers/cohortReducer.ts +++ b/src/redux/reducers/cohortReducer.ts @@ -1,10 +1,11 @@ -import { GET_COHORTS } from '..'; +import { GET_COHORTS, GET_TRAINEE_COHORT } from '..'; const initialState = { isLoading: true, isLoaded: false, errors: null, data: [], + traineeCohort: null, }; export default (state = initialState, { type, payload }: any) => { @@ -16,6 +17,14 @@ export default (state = initialState, { type, payload }: any) => { data: payload, }; + case GET_TRAINEE_COHORT: + return { + ...state, + isLoading: false, + isLoaded: true, + traineeCohort: payload, + }; + default: return state; } diff --git a/src/redux/reducers/index.ts b/src/redux/reducers/index.ts old mode 100644 new mode 100755 index 7a09e0767..cca554ae9 --- a/src/redux/reducers/index.ts +++ b/src/redux/reducers/index.ts @@ -41,6 +41,8 @@ import { singleApplicationReducer, } from './applicationReducer'; import { assessmentsReducer } from './assessmentReducer'; +import { attendanceReducer } from './attendanceReducer'; +import { performanceReducer } from './performanceReducer'; import filterProgramsReducer from './filterProgramsReducer'; import filterRoleReducer from './filterRoleReducer'; import fetchSearchDataReducer from './fetchSearchDataReducer'; @@ -86,6 +88,9 @@ const allReducers = combineReducers({ currentApplication: singleApplicationReducer, assessments: assessmentsReducer, searchData: fetchSearchDataReducer, + traineeApplicant: traineeReducer, + traineeAttendance: attendanceReducer, + traineePerformance: performanceReducer }); export type RootState = ReturnType; diff --git a/src/redux/reducers/performanceReducer.ts b/src/redux/reducers/performanceReducer.ts new file mode 100644 index 000000000..3fadabe60 --- /dev/null +++ b/src/redux/reducers/performanceReducer.ts @@ -0,0 +1,30 @@ +import { GET_TRAINEE_PERFORMANCE } from ".."; + +interface PerformanceState { + performances: any[]; + averageScore: number | null; + loading: boolean; + error: string | null; + } + + const initialState: PerformanceState = { + performances: [], + averageScore: null, + loading: false, + error: null + }; + + export const performanceReducer = (state = initialState, action: any) => { + switch (action.type) { + case GET_TRAINEE_PERFORMANCE: + return { + ...state, + performances: action.payload.performances, + averageScore: action.payload.averageScore, + loading: false, + error: null + }; + default: + return state; + } + }; \ No newline at end of file diff --git a/src/redux/reducers/traineeReducer.ts b/src/redux/reducers/traineeReducer.ts index 11fa0847c..b6744a4b3 100755 --- a/src/redux/reducers/traineeReducer.ts +++ b/src/redux/reducers/traineeReducer.ts @@ -1,10 +1,13 @@ -import { GET_TRAINEE, CREATE_TRAINEES} from ".."; +import { GET_TRAINEE, CREATE_TRAINEES, SET_TRAINEE, GET_TRAINEE_ATTENDANCE, GET_TRAINEE_PERFORMANCE} from ".."; const initialState = { loading: false, error: null, data: [], + currentTrainee: null, + traineeAttendance: null, + traineePerformance: null }; export default (state = initialState, { type, payload }: any) => { @@ -23,6 +26,24 @@ export default (state = initialState, { type, payload }: any) => { loading: false, data: [...state.data, payload], }; + + case SET_TRAINEE: + return { + ...state, + loading: false, + currentTrainee: payload + }; + + case GET_TRAINEE_ATTENDANCE: + return { + ...state, + data: payload + }; + case GET_TRAINEE_PERFORMANCE: + return { + ...state, + data: payload + }; default: return state; diff --git a/src/routes/routes.tsx b/src/routes/routes.tsx index 654a7f505..3ad24eba7 100644 --- a/src/routes/routes.tsx +++ b/src/routes/routes.tsx @@ -48,6 +48,7 @@ import ApplicationDetails from "../pages/Applications/ViewSingleApplication"; import Dashboard from "../pages/Dashboard"; import ApplicantLayout from "../pages/Applicant/ApplicantLayout"; import AdminLayout from "../components/Layout/Admins/AdminLayout"; +import ApplicantDashboard from "../pages/Applicant/ApplicantDashboard"; import UpdateJobPost from "../pages/JobPost/updateJobPost"; import VerifyEmail from "../pages/verifyEmail"; import Search from "./../pages/search"; @@ -306,22 +307,8 @@ function Navigation() { {/* Applicant Routes (Protected) */} - - - - } - > - - - - } - /> + }> + } />