From a52a5042a1b8a432240eb485ed5828938ade8d55 Mon Sep 17 00:00:00 2001 From: aimedivin Date: Wed, 16 Oct 2024 06:52:15 +0200 Subject: [PATCH] fix: move client-side attendance logic to server --- src/Mutations/Attendance.tsx | 416 +- src/components/AttendanceSymbols.tsx | 8 +- src/components/EditAttendenceButton.tsx | 29 +- src/components/ModalAttendance.tsx | 76 +- src/components/Sidebar.tsx | 25 +- src/containers/admin-dashBoard/Sessions.tsx | 1 - src/pages/Attendance.tsx | 2 +- src/pages/Organization/Orglogin.tsx | 1 - src/pages/TraineeAttendanceTracker.tsx | 801 ++-- src/queries/attendance.queries.tsx | 100 +- src/queries/team.queries.tsx | 52 + src/utils/getDateForDays.ts | 44 - .../components/EditAttendenceButton.test.tsx | 8 +- tests/components/ModalAttendance.test.tsx | 206 +- .../AdminTraineeDashboard.test.tsx.snap | 11 +- .../__snapshots__/DashHeader.test.tsx.snap | 2 +- .../EditAttendenceButton.test.tsx.snap | 2 +- .../__snapshots__/Header.test.tsx.snap | 126 +- .../__snapshots__/SideNavLink.test.tsx.snap | 2 +- .../__snapshots__/MainRoutes.test.tsx.snap | 76 +- .../__snapshots__/Cohorts.test.tsx.snap | 154 +- .../__snapshots__/App.test.tsx.snap | 55 +- .../__snapshots__/Siderbar.test.tsx.snap | 55 +- tests/pages/TraineeAttendanceTracker.test.tsx | 410 +- tests/pages/__snapshots__/About.test.tsx.snap | 29 +- .../AdminTraineeDashboard.test.tsx.snap | 11 +- .../__snapshots__/Admindash.test.tsx.snap | 154 +- .../__snapshots__/GradingSystem.test.tsx.snap | 186 +- tests/pages/__snapshots__/Home.test.tsx.snap | 55 +- .../pages/__snapshots__/Profile.test.tsx.snap | 3346 +++++++++++++++-- .../TraineeAttendanceTracker.test.tsx.snap | 118 +- .../TraineeRatingDashboard.test.tsx.snap | 170 +- .../UpdateTraineeRating.test.tsx.snap | 186 +- .../__snapshots__/userRegister.test.tsx.snap | 2 +- tests/utils/getDateForDays.test.tsx | 24 - 35 files changed, 5077 insertions(+), 1866 deletions(-) delete mode 100644 src/utils/getDateForDays.ts delete mode 100644 tests/utils/getDateForDays.test.tsx diff --git a/src/Mutations/Attendance.tsx b/src/Mutations/Attendance.tsx index e081cedf4..58aeeb0c7 100644 --- a/src/Mutations/Attendance.tsx +++ b/src/Mutations/Attendance.tsx @@ -1,44 +1,216 @@ import { gql } from '@apollo/client'; +export const PAUSE_AND_RESUME_ATTENDANCE = gql` + mutation PauseAndResumeTeamAttendance($team: String!, $orgToken: String) { + pauseAndResumeTeamAttendance(team: $team, orgToken: $orgToken) { + team { + id + name + isJobActive + }, + sanitizedAttendance { + today + yesterday + attendanceWeeks { + phase { + id + name + } + weeks + } + attendance { + week + phase { + id + name + } + dates { + mon { + date + isValid + } + tue { + date + isValid + } + wed { + date + isValid + } + thu { + date + isValid + } + fri { + date + isValid + } + } + days { + mon { + trainee { + id + email + profile { + id + name + } + } + score + } + tue { + trainee { + id + email + profile { + name + } + } + score + } + wed { + trainee { + id + email + profile { + name + } + } + score + } + thu { + trainee { + id + email + profile { + name + } + } + score + } + fri { + trainee { + id + email + profile { + name + } + } + score + } + } + } + } + } + } +`; + export const RECORD_ATTENDANCE = gql` mutation RecordAttendance( + $today: Boolean! + $yesterday: Boolean! $week: Int! $team: String! - $date: String! $trainees: [TraineeInput!]! $orgToken: String! ) { recordAttendance( + today: $today + yesterday: $yesterday week: $week team: $team - date: $date trainees: $trainees orgToken: $orgToken ) { - team { - id - name - cohort { + today + yesterday + attendanceWeeks { + phase { id name - phase { - name - id - } } + weeks } - trainees { - trainee { - profile { - name - } - email + attendance { + week + phase { id + name + } + dates { + mon { + date + isValid + } + tue { + date + isValid + } + wed { + date + isValid + } + thu { + date + isValid + } + fri { + date + isValid + } } - status { - day - date - score + days { + mon { + trainee { + id + email + profile { + id + name + } + } + score + } + tue { + trainee { + id + email + profile { + name + } + } + score + } + wed { + trainee { + id + email + profile { + name + } + } + score + } + thu { + trainee { + id + email + profile { + name + } + } + score + } + fri { + trainee { + id + email + profile { + name + } + } + score + } } } } @@ -48,6 +220,7 @@ export const RECORD_ATTENDANCE = gql` export const UPDATE_ATTENDANCE = gql` mutation UpdateAttendance( $week: Int! + $day: String! $team: String! $phase: String! $trainees: [TraineeInput!]! @@ -55,38 +228,99 @@ export const UPDATE_ATTENDANCE = gql` ) { updateAttendance( week: $week + day: $day team: $team phase: $phase trainees: $trainees orgToken: $orgToken ) { - id - week - phase { - id - name - } - cohort { - id - name + today + yesterday + attendanceWeeks { + phase { + id + name + } + weeks } - teams { - team { + attendance { + week + phase { id name } - trainees { - trainee { - id - email - profile { + dates { + mon { + date + isValid + } + tue { + date + isValid + } + wed { + date + isValid + } + thu { + date + isValid + } + fri { + date + isValid + } + } + days { + mon { + trainee { id - name + email + profile { + id + name + } } + score } - status { - day - date + tue { + trainee { + id + email + profile { + name + } + } + score + } + wed { + trainee { + id + email + profile { + name + } + } + score + } + thu { + trainee { + id + email + profile { + name + } + } + score + } + fri { + trainee { + id + email + profile { + name + } + } score } } @@ -96,35 +330,95 @@ export const UPDATE_ATTENDANCE = gql` `; export const DELETE_ATTENDANCE = gql` - mutation DeleteAttendance($week: String!, $team: String!, $day: String!) { + mutation DeleteAttendance($week: Int!, $team: String!, $day: String!) { deleteAttendance(week: $week, team: $team, day: $day) { - id - week - phase { - id - name - } - cohort { - id - name + today + yesterday + attendanceWeeks { + phase { + id + name + } + weeks } - teams { - team { + attendance { + week + phase { id name } - trainees { - trainee { - id - email - profile { + dates { + mon { + date + isValid + } + tue { + date + isValid + } + wed { + date + isValid + } + thu { + date + isValid + } + fri { + date + isValid + } + } + days { + mon { + trainee { id - name + email + profile { + id + name + } } + score } - status { - day - date + tue { + trainee { + id + email + profile { + name + } + } + score + } + wed { + trainee { + id + email + profile { + name + } + } + score + } + thu { + trainee { + id + email + profile { + name + } + } + score + } + fri { + trainee { + id + email + profile { + name + } + } score } } diff --git a/src/components/AttendanceSymbols.tsx b/src/components/AttendanceSymbols.tsx index 4b5b026db..7cfb28222 100644 --- a/src/components/AttendanceSymbols.tsx +++ b/src/components/AttendanceSymbols.tsx @@ -17,7 +17,7 @@ function AttendanceSymbols({ status }: Props) { const property: Property = { color: 'bg-[#0E8E0B]', // icon: , - icon: , + icon: , }; if (score === '1') { @@ -26,11 +26,13 @@ function AttendanceSymbols({ status }: Props) { } if (score === '0') { property.color = 'bg-[#C30909]'; - property.icon = ; + property.icon = ( + + ); } return (
{property.icon}
diff --git a/src/components/EditAttendenceButton.tsx b/src/components/EditAttendenceButton.tsx index aa3810f0d..c6f839b8c 100644 --- a/src/components/EditAttendenceButton.tsx +++ b/src/components/EditAttendenceButton.tsx @@ -1,11 +1,12 @@ import React, { useEffect, useState } from 'react'; +import { number } from 'zod'; import AttendanceSymbols from './AttendanceSymbols'; import { TraineeAttendanceDataInterface } from '../pages/TraineeAttendanceTracker'; interface EditAttendanceProps { setTraineeAttendanceData: React.Dispatch>; setUpdated: React.Dispatch>; - week: string; + week: number; day: string; phase: string; traineeId: string; @@ -25,21 +26,22 @@ function EditAttendanceButton({ setOpenEdit(false); }, [week, phase, day]); - const handleUpdateAttendance = (score: string) => { + const handleUpdateAttendance = (score: number) => { + setTraineeAttendanceData((prev) => prev.map((attendanceData) => { - if (attendanceData.week === week && attendanceData.phase === phase) { + if (attendanceData.week === week && attendanceData.phase.id === phase) { const updatedDay = attendanceData.days[ day as keyof typeof attendanceData.days ].map((traineeData: TraineeAttendanceDataInterface) => { if ( traineeData.trainee.id === traineeId && - traineeData.status.toString() !== score.toString() + traineeData.score !== score ) { setUpdated(true); return { trainee: traineeData.trainee, - status: score, + score, }; } return traineeData; @@ -61,7 +63,7 @@ function EditAttendanceButton({ {!openEdit && ( setOpenEdit(true)} - className="px-2 py-[2px] border dark:border-white border-black rounded-md text-[.85rem]" + className="px-3 py-[3px] border dark:border-white border-black rounded-md font-medium text-[.85rem]" data-testid="edit-button" > Edit @@ -69,22 +71,13 @@ function EditAttendanceButton({ )} {openEdit && (
-
handleUpdateAttendance('2')} - data-testid="score-2" - > +
handleUpdateAttendance(2)} data-testid="score-2">
-
handleUpdateAttendance('1')} - data-testid="score-1" - > +
handleUpdateAttendance(1)} data-testid="score-1">
-
handleUpdateAttendance('0')} - data-testid="score-0" - > +
handleUpdateAttendance(0)} data-testid="score-0">
diff --git a/src/components/ModalAttendance.tsx b/src/components/ModalAttendance.tsx index 25b02f957..25be121c0 100644 --- a/src/components/ModalAttendance.tsx +++ b/src/components/ModalAttendance.tsx @@ -4,15 +4,18 @@ import { useMutation } from '@apollo/client'; import { format } from 'date-fns'; import { RECORD_ATTENDANCE } from '../Mutations/Attendance'; import AttendanceSymbols from './AttendanceSymbols'; +import { AttendanceDataInterface } from '../pages/TraineeAttendanceTracker'; interface ModalProps { isVisible: boolean; onClose: () => void; - setAttendanceData: React.Dispatch>; + setAttendanceData: React.Dispatch< + React.SetStateAction + >; trainees: any; week: number; date: string; - day: string; + dayType: 'today' | 'yesterday' | 'others'; team: string; teamName: string; } @@ -20,17 +23,11 @@ interface attendanceProps { name: string; score: number; id: string; - day: string; -} - -interface StatusInput { - day: string; - score: string; } export interface recordTraineeProps { trainee: string; - status: StatusInput; + score: number; } function ModalAttendance({ @@ -39,7 +36,7 @@ function ModalAttendance({ trainees, week, date, - day, + dayType, setAttendanceData, team, teamName, @@ -60,52 +57,18 @@ function ModalAttendance({ { data: recordAttendanceData, loading: loadingRecordAttendance, error }, ] = useMutation(RECORD_ATTENDANCE, { variables: { + today: dayType === 'today', + yesterday: dayType === 'yesterday', week, team, - date, trainees: recordTrainees, orgToken: localStorage.getItem('orgToken'), }, fetchPolicy: 'no-cache', onCompleted: (data) => { - if (data) { - toast.success('Attendance recorded successfully.'); - } + toast.success('Attendance recorded successfully.'); - setAttendanceData((prev) => { - let isSet = false; - const result = prev.map((attendance) => { - if ( - attendance.week.toString() === week.toString() && - attendance.cohort.id === data.recordAttendance.team.cohort.id && - attendance.phase.id === data.recordAttendance.team.cohort.phase.id - ) { - isSet = true; - return { - ...attendance, - teams: [data.recordAttendance], - }; - } - return attendance; - }); - - if (!isSet && team === data.recordAttendance.team.id) { - result.push({ - week: week.toString(), - id: Math.random().toString(), - cohort: { - id: data.recordAttendance.team.cohort.id, - name: data.recordAttendance.team.cohort.name, - }, - phase: { - id: data.recordAttendance.team.cohort.phase.id, - name: data.recordAttendance.team.cohort.phase.name, - }, - teams: [data.recordAttendance], - }); - } - return result; - }); + setAttendanceData(data.recordAttendance); setTraineesAttendance([]); setRecordTrainees([]); onClose(); @@ -141,12 +104,7 @@ function ModalAttendance({ if (!isVisible) return null; - const handleGiveAttendance = ( - name: string, - score: number, - id: string, - day: string, - ) => { + const handleGiveAttendance = (name: string, score: number, id: string) => { let updatedAttendance; const updateAttendance = traineesAttendance.find( (attendance) => attendance.id === id, @@ -159,7 +117,7 @@ function ModalAttendance({ return attendance; }); } else { - updatedAttendance = [...traineesAttendance, { name, score, id, day }]; + updatedAttendance = [...traineesAttendance, { name, score, id }]; } setTraineesAttendance(updatedAttendance); @@ -175,10 +133,7 @@ function ModalAttendance({ const updatedRecords = traineesAttendance.map((attendance) => ({ trainee: attendance.id, - status: { - day: attendance.day, - score: attendance.score.toString(), - }, + score: attendance.score, })); setRecordTrainees(updatedRecords); @@ -288,7 +243,6 @@ function ModalAttendance({ trainee.profile.name, 2, trainee.id, - day, ); }} className="cursor-pointer hover:brightness-75" @@ -303,7 +257,6 @@ function ModalAttendance({ trainee.profile.name, 1, trainee.id, - day, ); }} className="cursor-pointer hover:brightness-75" @@ -318,7 +271,6 @@ function ModalAttendance({ trainee.profile.name, 0, trainee.id, - day, ); }} className="cursor-pointer hover:brightness-75" diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index d601c1f97..b4eda3ca8 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -140,27 +140,32 @@ function Sidebar({ style, toggle }: { style: string; toggle: () => void }) { - - - - {/* manger role */} - - - - - {/* TTL role */} + + {/* FOR COORDINATORS AND A TTL */} + + + + + + + {/* manger role */} + + + + + @@ -186,7 +191,7 @@ function Sidebar({ style, toggle }: { style: string; toggle: () => void }) { - + diff --git a/src/containers/admin-dashBoard/Sessions.tsx b/src/containers/admin-dashBoard/Sessions.tsx index 356497f15..2d7af0c08 100644 --- a/src/containers/admin-dashBoard/Sessions.tsx +++ b/src/containers/admin-dashBoard/Sessions.tsx @@ -158,7 +158,6 @@ function AdminSission() { getSessionRefetch(); }) .catch((error) => {}); - // console.log("------",id) } }; diff --git a/src/pages/Attendance.tsx b/src/pages/Attendance.tsx index 0125a4433..11e762727 100644 --- a/src/pages/Attendance.tsx +++ b/src/pages/Attendance.tsx @@ -10,7 +10,7 @@ const TraineeAttendance = React.lazy( function Attendance() { return ( <> - + diff --git a/src/pages/Organization/Orglogin.tsx b/src/pages/Organization/Orglogin.tsx index 37762f472..7a20c6c14 100644 --- a/src/pages/Organization/Orglogin.tsx +++ b/src/pages/Organization/Orglogin.tsx @@ -88,7 +88,6 @@ function Orglogin() { } }; const names = name.replace(/ /gi, '').toLowerCase(); - // console.log(names) const completeOrgUrl = `${names}`; return ( diff --git a/src/pages/TraineeAttendanceTracker.tsx b/src/pages/TraineeAttendanceTracker.tsx index 5e4805420..194ef9451 100644 --- a/src/pages/TraineeAttendanceTracker.tsx +++ b/src/pages/TraineeAttendanceTracker.tsx @@ -1,57 +1,66 @@ /* eslint-disable no-underscore-dangle */ -import React, { useEffect, useRef, useState } from 'react'; -import { useQuery, useLazyQuery, useMutation } from '@apollo/client'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +import { useLazyQuery, useMutation } from '@apollo/client'; import { useTranslation } from 'react-i18next'; import 'react-datepicker/dist/react-datepicker.css'; import { toast } from 'react-toastify'; import { RiDeleteBin6Line } from 'react-icons/ri'; import { LuClipboardEdit } from 'react-icons/lu'; -import { MdCalendarMonth, MdOutlineCalendarMonth } from 'react-icons/md'; -import { isSameWeek } from 'date-fns'; +import { MdOutlineCalendarMonth } from 'react-icons/md'; import { PulseLoader } from 'react-spinners'; -import { DELETE_ATTENDANCE, UPDATE_ATTENDANCE } from '../Mutations/Attendance'; +import { FaRegCirclePause, FaRegCirclePlay } from 'react-icons/fa6'; +import { + DELETE_ATTENDANCE, + PAUSE_AND_RESUME_ATTENDANCE, + UPDATE_ATTENDANCE, +} from '../Mutations/Attendance'; import { GET_TEAM_ATTENDANCE } from '../queries/attendance.queries'; import 'react-circular-progressbar/dist/styles.css'; -import { GET_ALL_TEAMS } from '../queries/team.queries'; -import { GET_TEAMS_CARDS } from '../components/CoordinatorCard'; +import { GET_ALL_TEAMS, GET_TTL_TEAMS } from '../queries/team.queries'; import AttendanceSymbols from '../components/AttendanceSymbols'; -import { getDateForDays, Weekdays } from '../utils/getDateForDays'; -import Modal, { recordTraineeProps } from '../components/ModalAttendance'; +import Modal from '../components/ModalAttendance'; import EditAttendanceButton from '../components/EditAttendenceButton'; +import { UserContext } from '../hook/useAuth'; /* istanbul ignore next */ +interface UserInterface { + id: string; + email: string; + role: string; + status: { + date: string; + reason: string; + status: string; + }; + profile: { + firstName?: string; + lastName?: string; + city?: string; + country?: string; + phoneNumber?: string; + biography?: string; + avatar?: string; + id?: string; + name?: string; + }; +} interface TeamData { id: string; + active: boolean; + isJobActive: boolean; name: string; + phase?: { + id: string; + name: string; + }; cohort: { name: string; phase: { - id: 'string'; - name: 'string'; + id: string; + name: string; }; }; - members: [ - { - id: string; - email: string; - status: { - date: string; - reason: string; - status: string; - }; - profile: { - firstName: string; - lastName: string; - city: string; - country: string; - phoneNumber: string; - biography: string; - avatar: string; - id: string; - name: string; - }; - }, - ]; + members: UserInterface[]; } export interface TraineeAttendanceDataInterface { @@ -62,12 +71,28 @@ export interface TraineeAttendanceDataInterface { name: string; }; }; - status: string; + score: number; +} +interface DayInterface { + date: string; + isValid: boolean; +} +export interface WeekdaysInterface { + mon: DayInterface; + tue: DayInterface; + wed: DayInterface; + thu: DayInterface; + fri: DayInterface; +} + +interface PhaseInterface { + id: string; + name: string; } export interface TraineeAttendanceDayInterface { - week: string; - phase: string; - dates: Weekdays; + week: number; + phase: PhaseInterface; + dates: WeekdaysInterface; days: { mon: TraineeAttendanceDataInterface[]; tue: TraineeAttendanceDataInterface[]; @@ -77,213 +102,72 @@ export interface TraineeAttendanceDayInterface { }; } -interface PhaseInterface { - id: string; - name: string; +interface ValidDatesInterface { + today: string; + yesterday: string; +} + +export interface AttendanceDataInterface extends ValidDatesInterface { + attendanceWeeks: Array }>; + attendance: Array; } function TraineeAttendanceTracker() { const { t } = useTranslation(); + const { user } = useContext(UserContext); const [getTeamAttendance, { loading: teamAttendanceLoading }] = useLazyQuery(GET_TEAM_ATTENDANCE); - const [selectedWeek, setSelectedWeek] = useState(); - const [weeks, setWeeks] = useState([]); + const [selectedWeek, setSelectedWeek] = useState(); + const [weeks, setWeeks] = useState([]); const [selectedTeam, setSelectedTeam] = useState(''); - const [isValidAttendanceDay, SetIsValidAttendanceDay] = + const [isValidAttendanceDay, setIsValidAttendanceDay] = useState(false); const [selectedDay, setSelectedDay] = useState< 'mon' | 'tue' | 'wed' | 'thu' | 'fri' >('mon'); const [selectedDayDate, setSelectedDayDate] = useState(''); - const [currentTime, setCurrentTime] = useState(); const [selectedDayHasData, setSelectedDayHasData] = useState(false); const [selectedPhase, setSelectedPhase] = useState< PhaseInterface | undefined >(); const [phases, setPhases] = useState([]); const [teamsData, setTeamsData] = useState(); - const [getAllTeams, { loading: teamLoading }] = useLazyQuery(GET_ALL_TEAMS); + const [getAllTeams, { loading: teamsLoading }] = useLazyQuery(GET_ALL_TEAMS); + const [getTTLTeams, { loading: teamLoading }] = useLazyQuery(GET_TTL_TEAMS); const [initialTraineeAttendanceData, setInitialTraineeAttendanceData] = useState([]); const [traineeAttendanceData, setTraineeAttendanceData] = useState< TraineeAttendanceDayInterface[] >([]); - const [attendanceData, setAttendanceData] = useState([]); + const [attendanceData, setAttendanceData] = + useState(); const [orgToken, setOrgToken] = useState(''); const [resetDayAndWeek, setResetDayAndWeek] = useState(true); const [selectedTeamId, setSelectedTeamId] = useState(''); const [selectedTeamData, setSelectedTeamData] = useState(); + const [selectedTeamTrainees, setSelectedTeamTrainees] = + useState(); const [isModalOpen, setIsModalOpen] = useState(false); const [isUpdatedMode, setIsUpdatedMode] = useState(false); + const [pauseResumeAttendance, setPauseResumeAttendance] = + useState(false); const [deleteAttendance, { loading: loadingDeleteAttendance }] = useMutation(DELETE_ATTENDANCE); const [updated, setUpdate] = useState(false); - const [updateTrainees, setUpdateTrainees] = useState( - [], + const [dayType, setDayType] = useState<'today' | 'yesterday' | 'others'>( + 'others', ); + const [validDate, setValidDate] = useState({ + today: '', + yesterday: '', + }); const editColumnRef = useRef(null); - const formatAttendanceData = (data: any) => { - const tempPhases: PhaseInterface[] = []; - - const updatedTraineeData: TraineeAttendanceDayInterface[] = []; - - data.forEach((attendance: any, index: any) => { - let hasData = false; - !selectedWeek && index === 0 && setSelectedWeek(attendance.week); - - if (resetDayAndWeek && selectedWeek! < attendance.week) { - setSelectedWeek(attendance.week); - } - - if (!tempPhases.find((p) => p.id === attendance.phase.id)) - tempPhases.push({ - id: attendance.phase.id, - name: attendance.phase.name, - }); - - const result: TraineeAttendanceDayInterface = { - week: attendance.week, - dates: { - mon: '', - tue: '', - wed: '', - thu: '', - fri: '', - }, - phase: attendance.phase.id, - days: { - mon: [], - tue: [], - wed: [], - thu: [], - fri: [], - }, - }; - - let date = ''; - - attendance.teams[0].trainees.forEach((traineeData: any) => { - if (traineeData.status.length) { - hasData = true; - traineeData.status.forEach((traineeStatus: any) => { - if (traineeStatus.day === selectedDay) { - setSelectedDayHasData(true); - } - if (traineeStatus.date && !date) { - date = traineeStatus.date; - } - - result.days[ - traineeStatus.day as 'mon' | 'tue' | 'wed' | 'thu' | 'fri' - ].push({ - trainee: traineeData.trainee, - status: traineeStatus.score as string, - }); - }); - resetDayAndWeek && - setSelectedDay( - traineeData.status[traineeData.status.length - 1].day, - ); - } - if (!traineeData.status.length && !hasData) { - setSelectedDayHasData(false); - date = currentTime ? currentTime.toString() : Date.now().toString(); - } - }); - if (date) result.dates = getDateForDays(date); - updatedTraineeData.push(result); - }); - - setTraineeAttendanceData(updatedTraineeData); - setInitialTraineeAttendanceData(updatedTraineeData); - - teamsData?.forEach((team) => { - if (team.id === selectedTeamId) { - - const names = tempPhases.map((phase) => phase.name); - let isDataSet = false; - if (!data.length) { - isDataSet = true; - setWeeks(['1']); - setSelectedWeek('1'); - setSelectedDayDate( - getDateForDays( - currentTime ? currentTime.toString() : Date.now().toString(), - )[selectedDay], - ); - setTraineeAttendanceData([ - { - week: '1', - phase: team.cohort.phase.id, - dates: getDateForDays( - currentTime ? currentTime.toString() : Date.now().toString(), - ), - days: { - mon: [], - tue: [], - wed: [], - thu: [], - fri: [], - }, - }, - ]); - } - - if (!names.includes(team.cohort.phase.name)) { - tempPhases.push({ - id: team.cohort.phase.id, - name: team.cohort.phase.name, - }); - - if (!isDataSet) { - setSelectedWeek('1'); - setTraineeAttendanceData((prevData) => [ - ...prevData, - { - week: '1', - data: false, - phase: team.cohort.phase.id, - dates: getDateForDays( - currentTime ? currentTime.toString() : Date.now().toString(), - ), - days: { - mon: [], - tue: [], - wed: [], - thu: [], - fri: [], - }, - }, - ]); - } - } - } - }); - setPhases(tempPhases); - setResetDayAndWeek(true); - selectedTeamData && - !selectedPhase && - setSelectedPhase({ - id: selectedTeamData?.cohort.phase.id, - name: selectedTeamData?.cohort.phase.name, - }); - }; - const [updateAttendance, { loading: loadingupdateAttendance }] = useMutation( UPDATE_ATTENDANCE, { - variables: { - week: Number(selectedWeek), - team: selectedTeamId, - phase: selectedPhase?.id, - trainees: updateTrainees, - orgToken: localStorage.getItem('orgToken'), - }, onCompleted: (data) => { - if (data) { - toast.success('Attendance updated successfully.'); - } + toast.success('Attendance updated successfully.'); setUpdate(false); setIsUpdatedMode(false); setAttendanceData(data.updateAttendance); @@ -296,69 +180,111 @@ function TraineeAttendanceTracker() { }, }, ); - useEffect(() => { - if (attendanceData) { - formatAttendanceData(attendanceData); - } - }, [attendanceData, currentTime]); - useEffect(() => { - if (updateTrainees.length > 0) { - updateAttendance(); - } - }, [updateTrainees]); - - useEffect(() => { - fetch('http://worldtimeapi.org/api/timezone/Etc/UTC') - .then((response) => response.json()) - .then((data) => { - const utcDate = new Date(data.datetime); - setCurrentTime(utcDate.getTime()); - }) - .catch((error) => {}); - }, []); + const [pauseAndResumeTeamAttendance, { loading: loadingPRTeamAttendance }] = + useMutation(PAUSE_AND_RESUME_ATTENDANCE, { + onCompleted: (data) => { + setPauseResumeAttendance(false); + setSelectedWeek(undefined); + setAttendanceData( + data.pauseAndResumeTeamAttendance.sanitizedAttendance, + ); + const { team } = data.pauseAndResumeTeamAttendance; + toast.success( + `Attendance for team '${team.name}' was successfully ${ + team.isJobActive ? 'resumed' : 'paused' + }. `, + ); + setSelectedTeamData((prevData) => { + const result = prevData; + if (result) { + result.isJobActive = team.isJobActive; + } + return result; + }); + }, + onError: (error) => { + setPauseResumeAttendance(false); + const errorMessage = + error.graphQLErrors?.[0]?.message || 'An unexpected error occurred'; + toast.error(errorMessage); + }, + }); useEffect(() => { setOrgToken(localStorage.getItem('orgToken')); }, []); - const { data, loading } = useQuery(GET_TEAMS_CARDS, { - variables: { orgToken }, - }); - - useEffect(() => { - if (!loading && data && data.getAllTeams.length > 0) { - setSelectedTeam(data.getAllTeams[0].name); - } - }, [loading, data]); - useEffect(() => { const fetchData = async () => { const orgToken = localStorage.getItem('orgToken'); + if (orgToken) { - getAllTeams({ - fetchPolicy: 'no-cache', - variables: { orgToken }, - onCompleted: (data) => { - setTeamsData(data.getAllTeams); - setSelectedTeam(data.getAllTeams[0].name); - setSelectedTeamData(data.getAllTeams[0]); - setSelectedTeamId(data.getAllTeams[0].id); - }, - onError: (error) => { - const errorMessage = - error.graphQLErrors?.[0]?.message || - 'An unexpected error occurred'; - toast.error(errorMessage); - }, - }); + if (user.role === 'coordinator') { + getAllTeams({ + fetchPolicy: 'no-cache', + variables: { orgToken }, + onCompleted: (data) => { + setTeamsData(data.getAllTeams); + setSelectedTeam(data.getAllTeams[0].name); + setSelectedTeamData(data.getAllTeams[0]); + data.getAllTeams[0].isJobActive && + data.getAllTeams[0].active && + setSelectedPhase( + data.getAllTeams[0].phase || data.getAllTeams[0].cohort.phase, + ); + setSelectedTeamId(data.getAllTeams[0].id); + }, + onError: (error) => { + const errorMessage = + error.graphQLErrors?.[0]?.message || + 'An unexpected error occurred'; + toast.error(errorMessage); + }, + }); + } + if (user.role === 'ttl') { + getTTLTeams({ + fetchPolicy: 'no-cache', + variables: { + orgToken, + }, + onCompleted: (data) => { + setTeamsData(data.getTTLTeams); + setSelectedTeam(data.getTTLTeams[0].name); + setSelectedTeamData(data.getTTLTeams[0]); + setPhases([ + data.getTTLTeams[0].phase || data.getTTLTeams[0].cohort.phase, + ]); + data.getTTLTeams[0].isJobActive && + data.getTTLTeams[0].active && + setSelectedPhase( + data.getTTLTeams[0].phase || data.getTTLTeams[0].cohort.phase, + ); + setSelectedTeamId(data.getTTLTeams[0].id); + }, + onError: (error) => { + const errorMessage = + error.graphQLErrors?.[0]?.message || + 'An unexpected error occurred'; + toast.error(errorMessage); + }, + }); + } } }; - fetchData(); - }, [getAllTeams]); + fetchData(); + }, [getAllTeams, getTTLTeams]); useEffect(() => { + if (selectedTeamData) { + const trainees = selectedTeamData?.members.filter( + (member: UserInterface) => member.role === 'trainee', + ); + setSelectedTeamTrainees(trainees); + } + setTraineeAttendanceData([]); - setAttendanceData([]); + setAttendanceData(undefined); teamsData?.forEach((team) => { if (team.id === selectedTeamId) { setSelectedTeam(team.name); @@ -372,6 +298,16 @@ function TraineeAttendanceTracker() { team: selectedTeamId, }, onCompleted: (data) => { + selectedTeamData?.isJobActive && + selectedTeamData.active && + setSelectedPhase({ + id: + selectedTeamData!.phase?.id || + selectedTeamData!.cohort.phase.id, + name: + selectedTeamData!.phase?.name || + selectedTeamData!.cohort.phase.name, + }); setAttendanceData(data.getTeamAttendance); }, onError: (error) => { @@ -380,78 +316,94 @@ function TraineeAttendanceTracker() { }); }, [selectedTeamId]); - // New week for team attendance + // Handle retrieved attendance data useEffect(() => { - const tempTeam = teamsData?.find((team) => team.id === selectedTeamId); - let lastDayDate = ''; - if (tempTeam) { - const tempWeeks: string[] = traineeAttendanceData - .map((attendanceData) => { + if (attendanceData) { + setValidDate({ + today: attendanceData.today, + yesterday: attendanceData.yesterday, + }); + setTraineeAttendanceData(attendanceData.attendance); + setInitialTraineeAttendanceData(attendanceData.attendance); + const teamAttendancePhases: PhaseInterface[] = []; + const attendanceWeek = attendanceData.attendanceWeeks.filter( + (attendanceWeek: any, index) => { + teamAttendancePhases.push(attendanceWeek.phase); if ( - attendanceData.phase === selectedPhase?.id + (!selectedTeamData?.isJobActive || !selectedTeamData?.active) && + index === 0 ) { - if (attendanceData.dates.fri) - lastDayDate = attendanceData.dates.fri; - return attendanceData.week; + return true; } - return ''; - }) - .filter((week) => week); - - const isInSameWeek = isSameWeek( - new Date(lastDayDate), - new Date(currentTime || Date.now()), - { - weekStartsOn: 1, + return ( + attendanceWeek.phase.id === + (selectedTeamData?.phase?.id || selectedTeamData?.cohort.phase.id) + ); }, ); + teamAttendancePhases.length && + (!selectedTeamData?.isJobActive || !selectedTeamData?.active) && + setSelectedPhase({ + id: teamAttendancePhases[0].id, + name: teamAttendancePhases[0].name, + }); + setPhases(teamAttendancePhases); + const tempWeeks = attendanceWeek.length + ? [...attendanceWeek[0].weeks] + : [1]; + tempWeeks.sort((a, b) => a - b); + setWeeks(tempWeeks); + !selectedWeek && setSelectedWeek(tempWeeks[tempWeeks.length - 1]); + } + }, [attendanceData]); - if (tempWeeks.length && !isInSameWeek) { - tempWeeks.push(String(tempWeeks.length + 1)); - const baseDate = isInSameWeek - ? currentTime || Date.now() + 7 * 24 * 60 * 60 * 1000 - : currentTime || Date.now(); - - const dates = getDateForDays(baseDate.toString()); - - // setAttendanceData(prevData => [...prevData, {}]) - setTraineeAttendanceData((prevData) => [ - ...prevData, - { - week: String(tempWeeks[tempWeeks.length - 1]), - data: false, - phase: tempTeam!.cohort.phase.id, - dates, - days: { - mon: [], - tue: [], - wed: [], - thu: [], - fri: [], - }, - }, - ]); - - !selectedWeek && setSelectedWeek(tempWeeks[0]); - } + // Handle week on phase change + useEffect(() => { + if (attendanceData && selectedPhase) { + const attendanceWeek = attendanceData.attendanceWeeks.filter( + (attendanceWeek: any) => attendanceWeek.phase.id === selectedPhase?.id, + ); + attendanceWeek.length ? setWeeks(attendanceWeek[0].weeks) : setWeeks([1]); + const tempWeeks = attendanceWeek.length + ? [...attendanceWeek[0].weeks] + : [1]; + tempWeeks.sort((a, b) => a - b); setWeeks(tempWeeks); + setSelectedWeek(tempWeeks[tempWeeks.length - 1]); } - }, [selectedPhase, initialTraineeAttendanceData, currentTime]); + }, [selectedPhase]); // Change Date for selected Day useEffect(() => { const result = traineeAttendanceData.find((attendanceData) => { setSelectedDayHasData(!!attendanceData.days[selectedDay].length); if ( - attendanceData.phase === selectedPhase?.id && + attendanceData.phase.id === selectedPhase?.id && attendanceData.week === selectedWeek ) { return true; } return null; }); + if (result) { + const selectedDate = new Date(result.dates[selectedDay].date) + .toISOString() + .split('T')[0]; + const today = new Date(Number(validDate.today)) + .toISOString() + .split('T')[0]; + const yesterday = new Date(Number(validDate.yesterday)) + .toISOString() + .split('T')[0]; - result && setSelectedDayDate(result.dates[selectedDay]); + (selectedDate === today && setDayType('today')) || + (selectedDate === yesterday && setDayType('yesterday')) || + (selectedDate !== today && + selectedDate !== yesterday && + setDayType('others')); + + setSelectedDayDate(result.dates[selectedDay].date); + } }, [selectedDay, selectedPhase, selectedWeek, traineeAttendanceData]); const openModal = () => { @@ -463,9 +415,14 @@ function TraineeAttendanceTracker() { }; const submitAttendance = async () => { + if (!selectedTeamData?.active) { + toast.error('Attendance for inactive team can not be recorded.'); + return; + } if ( isValidAttendanceDay && - selectedTeamData?.cohort.phase.id === selectedPhase?.id + (selectedTeamData?.phase?.id || selectedTeamData?.cohort.phase.id) === + selectedPhase?.id ) { openModal(); return; @@ -477,7 +434,7 @@ function TraineeAttendanceTracker() { const attendanceToUpdate = traineeAttendanceData.find( (attendance) => attendance.week === selectedWeek && - attendance.phase === selectedPhase?.id, + attendance.phase.id === selectedPhase?.id, ); if (!attendanceToUpdate) { toast.error('This day has no attendance yet'); @@ -486,17 +443,24 @@ function TraineeAttendanceTracker() { const traineesAttendance = attendanceToUpdate.days[selectedDay]; const updatedRecords = traineesAttendance.map((attendance) => ({ trainee: attendance.trainee.id, - status: { - day: selectedDay, - score: attendance.status.toString(), - }, + score: attendance.score, })); - setUpdateTrainees(updatedRecords); + + updatedRecords.length && + updateAttendance({ + variables: { + week: selectedWeek, + day: selectedDay, + team: selectedTeamId, + phase: selectedPhase?.id, + trainees: updatedRecords, + orgToken: localStorage.getItem('orgToken'), + }, + }); }; const handleDeleteAttendance = () => { if (loadingDeleteAttendance) return; - const date = new Date().toISOString().split('T')[0]; if (!selectedDayHasData) { toast.warning( 'You cannot delete attendance for the day without any entries.', @@ -504,7 +468,10 @@ function TraineeAttendanceTracker() { ); return; } - if (selectedTeamData?.cohort.phase.id !== selectedPhase?.id) { + if ( + (selectedTeamData?.phase?.id || selectedTeamData?.cohort.phase.id) !== + selectedPhase?.id + ) { toast.warning( 'Attendance records from previous phase cannot be deleted; they can only be updated.', { style: { color: '#000', lineHeight: '.95rem' } }, @@ -532,41 +499,20 @@ function TraineeAttendanceTracker() { setResetDayAndWeek(false); }, onError: (error) => { - toast.error(error.message); + toast.error("Couldn't delete attendance, please try again"); }, }); }; - const handleAttendanceDay = () => { - const today = new Date(); - const input = new Date(selectedDayDate); - - today.setHours(0, 0, 0, 0); - input.setHours(0, 0, 0, 0); - - const previousDay = new Date(today); - previousDay.setDate(today.getDate() - 1); - - if (input.getTime() === today.getTime()) { - return true; - } - - if (input.getTime() === previousDay.getTime()) { - return true; - } - - // Check if today is Monday and selectedDayDate is last Friday - if (today.getDay() === 1 && input.getDay() === 5) { - const lastFriday = new Date(today); - lastFriday.setDate(today.getDate() - 3); - - return input.getTime() === lastFriday.getTime(); - } - - return false; - }; + // Check if date for selected day is valid useEffect(() => { - SetIsValidAttendanceDay(handleAttendanceDay()); + setIsValidAttendanceDay((prevData) => + Object.values(validDate).some( + (date) => + new Date(Number(date)).toISOString().split('T')[0] === + selectedDayDate, + ), + ); }, [selectedDayDate]); useEffect(() => { @@ -575,18 +521,61 @@ function TraineeAttendanceTracker() { } }, [isUpdatedMode]); return ( -
+
+ {pauseResumeAttendance && ( +
+
+
+

+ {selectedTeamData?.isJobActive + ? 'Pause Attendance' + : 'Resume Attendance'} +

+
+

+ {selectedTeamData?.isJobActive + ? "By confirming, automatic attendance week additions for upcoming weeks will be paused. You can still record attendance for the current week. Don't worry you can reactivate this feature at any time!." + : "By confirming, automatic attendance week additions for upcoming weeks will be activated again. If you ever wish to pause this feature again, it's easy to do!"} +

+
+ + +
+
+
+ )}
@@ -594,7 +583,9 @@ function TraineeAttendanceTracker() {
-

Team

+

+ Team +

@@ -636,7 +632,8 @@ function TraineeAttendanceTracker() { isValidAttendanceDay && !teamAttendanceLoading && !selectedDayHasData && - selectedTeamData?.cohort.phase.id === selectedPhase?.id + (selectedTeamData?.phase?.id || + selectedTeamData?.cohort.phase.id) === selectedPhase?.id ? 'bg-primary text-white' : 'bg-neutral-400/60 dark:bg-neutral-500 text-black dark:text-white cursor-not-allowed' } text-[.84rem] xmd:text-[.9rem] font-semibold w-[8.9rem] xmd:w-[10rem] h-[1.9rem] xmd:h-[2.3rem] rounded-[4px] tracking-tight`} @@ -645,15 +642,16 @@ function TraineeAttendanceTracker() { !isValidAttendanceDay || teamAttendanceLoading || selectedDayHasData || - selectedTeamData?.cohort.phase.id !== selectedPhase?.id + (selectedTeamData?.phase?.id || + selectedTeamData?.cohort.phase.id) !== selectedPhase?.id } > - {!teamLoading ? t('Submit Attendance') : 'Loading...'} + {!teamsLoading ? t('Submit Attendance') : 'Loading...'}
-
-
+
+
{phases.map((phase, index) => (
{ if (isUpdatedMode && selectedPhase !== phase && updated) { toast.warning('First Discard or Update your changes', { @@ -678,7 +676,7 @@ function TraineeAttendanceTracker() {
))}
-
+
Week: - - - +
+
- Show - 100 - - + +
+
diff --git a/tests/other-tests/__snapshots__/App.test.tsx.snap b/tests/other-tests/__snapshots__/App.test.tsx.snap index 4a4fcb485..fe12c0aba 100644 --- a/tests/other-tests/__snapshots__/App.test.tsx.snap +++ b/tests/other-tests/__snapshots__/App.test.tsx.snap @@ -227,6 +227,32 @@ exports[`App test Should render app 1`] = ` About +
  • + + Product + +
  • +
    +
    +

    + Docs +

    + + ▼ + +
    +
  • @@ -456,7 +482,6 @@ exports[`App test Should render app 1`] = ` > Come shape the future together -
  • - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    diff --git a/tests/other-tests/__snapshots__/Siderbar.test.tsx.snap b/tests/other-tests/__snapshots__/Siderbar.test.tsx.snap index c3b70f179..536d31b54 100644 --- a/tests/other-tests/__snapshots__/Siderbar.test.tsx.snap +++ b/tests/other-tests/__snapshots__/Siderbar.test.tsx.snap @@ -224,6 +224,32 @@ exports[` Renders Home 1`] = ` About +
  • + + Product + +
  • +
    +
    +

    + Docs +

    + + ▼ + +
    +
  • @@ -453,7 +479,6 @@ exports[` Renders Home 1`] = ` > Come shape the future together -
  • Renders Home 1`] = `

    - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    Renders Home 1`] = `

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    Renders Home 1`] = `

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    Renders Home 1`] = `

    - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    Renders Home 1`] = `

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    Renders Home 1`] = `

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    diff --git a/tests/pages/TraineeAttendanceTracker.test.tsx b/tests/pages/TraineeAttendanceTracker.test.tsx index c911cba0b..0b4861e33 100644 --- a/tests/pages/TraineeAttendanceTracker.test.tsx +++ b/tests/pages/TraineeAttendanceTracker.test.tsx @@ -3,7 +3,6 @@ import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import renderer, { act } from 'react-test-renderer'; import { toast } from 'react-toastify'; -import fetchMock from 'jest-fetch-mock'; import { cleanup, fireEvent, @@ -17,8 +16,91 @@ import { MockedProvider, } from '@apollo/client/testing'; import TraineeAttendanceTracker from '../../src/pages/TraineeAttendanceTracker'; -import { GET_ALL_TEAMS } from '../../src/queries/team.queries'; +import { GET_ALL_TEAMS, GET_TTL_TEAMS } from '../../src/queries/team.queries'; import { GET_TEAM_ATTENDANCE } from '../../src/queries/attendance.queries'; +import { PAUSE_AND_RESUME_ATTENDANCE } from '../../src/Mutations/Attendance'; + +const sampleResult = { + today: '1729533725697', + yesterday: '1729274525697', + attendanceWeeks: [ + { + phase: { + id: 'test-phase-i', + name: 'Phase I', + }, + weeks: [1], + }, + { + phase: { + id: 'test-phase-ii', + name: 'Phase II', + }, + weeks: [1, 2], + }, + ], + attendance: [ + { + week: 1, + phase: { + id: 'test-phase-i', + name: 'Phase I', + }, + dates: { + mon: { + date: '2024-10-21', + isValid: true, + }, + tue: { + date: '2024-10-22', + isValid: false, + }, + wed: { + date: '2024-10-23', + isValid: false, + }, + thu: { + date: '2024-10-24', + isValid: false, + }, + fri: { + date: '2024-10-25', + isValid: false, + }, + }, + days: { + mon: [ + { + trainee: { + id: 'test-trainee-name', + email: 'test-trainee-name@gmail.com', + profile: { + id: 'trainee-name-profile', + name: 'test-trainee-name', + }, + }, + score: 2, + }, + { + trainee: { + id: 'test-trainee-name2', + email: 'test-trainee-name2@gmail.com', + profile: { + id: 'trainee-name2-profile', + name: 'test-trainee-name2', + }, + }, + score: 1, + }, + ], + tue: [], + wed: [], + thu: [], + fri: [], + }, + }, + ], +}; const mocks = [ { @@ -32,18 +114,76 @@ const mocks = [ data: { getAllTeams: [ { - id: '66eea29cba07ede8a49e8bc6', + id: 'Team-I-id-123', name: 'Team I', + isJobActive: true, + active: true, + phase: { + id: 'test-phase-i', + name: 'Phase I', + }, cohort: { name: 'cohort 1', phase: { + id: 'test-phase-i', name: 'Phase I', - id: '66eea29cba07ede8a49e8bad', }, coordinator: { - id: '66eea29cba07ede8a49e8dcz', + id: 'coordinator-id', }, }, + members: [ + { + id: 'test-trainee-name', + email: 'test-trainee-name@gmail.com', + role: 'trainee', + status: { + date: null, + reason: null, + status: 'active', + }, + profile: { + id: 'trainee-name-profile', + name: 'test-trainee-name', + }, + }, + ], + }, + { + id: 'Team-II-id-123', + name: 'Team II', + isJobActive: false, + active: true, + phase: { + id: 'test-phase-i', + name: 'Phase I', + }, + cohort: { + name: 'cohort 1', + phase: { + id: 'test-phase-i', + name: 'Phase I', + }, + coordinator: { + id: 'coordinator-id', + }, + }, + members: [ + { + id: 'test-trainee-name2', + email: 'test-trainee-name2@gmail.com', + role: 'ttl', + status: { + date: null, + reason: null, + status: 'active', + }, + profile: { + id: 'trainee-name2-profile', + name: 'test-trainee-name2', + }, + }, + ], }, { id: '66eea29cba07ede8a49e8bc7', @@ -58,6 +198,7 @@ const mocks = [ id: '66eea29cba07ede8a49e8dcz', }, }, + members: [], }, ], }, @@ -65,92 +206,48 @@ const mocks = [ maxUsageCount: 10, }, { - delay: 500, request: { - query: GET_TEAM_ATTENDANCE, + query: GET_TTL_TEAMS, variables: { orgToken: 'mocked-org-token', - team: '66eea29cba07ede8a49e8bc6', }, }, result: { data: { - getTeamAttendance: [ + getTTLTeams: [ { - id: '66faedaab256018b1efcd57a', - week: '1', + id: 'Team-I-id-123', + name: 'Team I', + isJobActive: true, + active: true, phase: { + id: 'test-phase-i', name: 'Phase I', - id: '66eea29cba07ede8a49e8bad', }, cohort: { - id: '66eea29cba07ede8a49e8bbb', name: 'cohort 1', - }, - teams: [ - { - team: { - id: '66eea29cba07ede8a49e8bc6', - name: 'Team I', - }, - trainees: [ - { - trainee: { - id: '66eea29cba07ede8a49e8b81', - email: 'example@gmail.com', - profile: { - id: '66eea29cba07ede8a49e8b90', - name: 'Example Virgile', - }, - }, - status: [ - { - day: 'mon', - date: '1727654400000', - score: 2, - }, - ], - }, - ], + phase: { + id: 'test-phase-i', + name: 'Phase I', + }, + coordinator: { + id: 'coordinator-id', }, - ], - }, - { - id: '66fc3cd78788248998569bb7', - week: '1', - phase: { - name: 'Phase II', - id: '66eea29cba07ede8a49ewxyz', - }, - cohort: { - id: '66eea29cba07ede8a49e8bbb', - name: 'cohort 1', }, - teams: [ + members: [ { - team: { - id: '66eea29cba07ede8a49e8bc6', - name: 'Team I', + id: 'test-trainee-name', + email: 'test-trainee-name@gmail.com', + role: 'trainee', + status: { + date: null, + reason: null, + status: 'active', + }, + profile: { + id: 'trainee-name-profile', + name: 'test-trainee-name', }, - trainees: [ - { - trainee: { - id: '66eea29cba07ede8a49e8b81', - email: 'example@gmail.com', - profile: { - id: '66eea29cba07ede8a49e8b90', - name: 'Example Virgile', - }, - }, - status: [ - { - day: 'mon', - date: '1728259200000', - score: 1, - }, - ], - }, - ], }, ], }, @@ -159,6 +256,45 @@ const mocks = [ }, maxUsageCount: 10, }, + { + delay: 500, + request: { + query: GET_TEAM_ATTENDANCE, + variables: { + orgToken: 'mocked-org-token', + team: 'Team-I-id-123', + }, + }, + result: { + data: { + getTeamAttendance: sampleResult, + }, + }, + maxUsageCount: 10, + }, + { + delay: 500, + request: { + query: PAUSE_AND_RESUME_ATTENDANCE, + variables: { + orgToken: 'mocked-org-token', + team: 'Team-I-id-123', + }, + }, + result: { + data: { + pauseAndResumeTeamAttendance: { + team: { + id: 'Team-I-id-123', + name: 'Team-I-id-123', + isJobActive: true, + }, + sanitizedAttendance: sampleResult, + }, + }, + }, + maxUsageCount: 10, + }, ]; jest.mock('react-toastify', () => ({ @@ -171,12 +307,6 @@ jest.mock('react-toastify', () => ({ describe('CRUD Of Trainee Attendance', () => { beforeEach(() => { localStorage.setItem('orgToken', 'mocked-org-token'); - fetchMock.enableMocks(); - fetchMock.mockResponseOnce( - JSON.stringify({ - datetime: '2023-10-03T12:34:56Z', - }), - ); }); afterEach(async () => { @@ -186,38 +316,39 @@ describe('CRUD Of Trainee Attendance', () => { }); it('Renders the TraineeAttendance Page', () => { + jest.spyOn(React, 'useContext').mockImplementation(() => ({ + user: { + role: 'coordinator', + }, + })); const elem = renderer .create( - - - - - , + + + , ) .toJSON(); expect(elem).toMatchSnapshot(); }); it('Renders the TraineeAttendance Page', async () => { + jest.spyOn(React, 'useContext').mockImplementation(() => ({ + user: { + role: 'ttl', + }, + })); render( , ); - await waitFor(() => { - expect(fetchMock).toHaveBeenCalledTimes(1); - expect(fetchMock).toHaveBeenCalledWith( - 'http://worldtimeapi.org/api/timezone/Etc/UTC', - ); - }); - expect(await screen.findByText('Loading Data...')).toBeInTheDocument(); const teamElement = await screen.findByTestId('team-test'); expect(teamElement).toBeInTheDocument(); fireEvent.change(teamElement, { - target: { value: '66eea29cba07ede8a49e8bc6' }, + target: { value: 'Team-I-id-123' }, }); const weeksElement = await screen.findByTestId('week-test'); @@ -244,7 +375,11 @@ describe('CRUD Of Trainee Attendance', () => { }); fireEvent.click(daysElement[0]); - expect(await screen.findByText('Example Virgile')).toBeInTheDocument(); + // Back to Phase I + fireEvent.click(phase1Element); + + // Find row with trainee name test-trainee-name + expect(await screen.findByText('test-trainee-name')).toBeInTheDocument(); fireEvent.click(updateLink2); @@ -264,15 +399,16 @@ describe('CRUD Of Trainee Attendance', () => { { style: { color: '#000', lineHeight: '.95rem' } }, ); - const editButton = await screen.findByTestId('edit-button'); - expect(editButton).toBeInTheDocument(); + const editButton = await screen.findAllByTestId('edit-button'); + expect(editButton).toHaveLength(2); - fireEvent.click(editButton); + fireEvent.click(editButton[0]); const zeroScore = await screen.findByTestId('score-0'); + expect(zeroScore).toBeInTheDocument(); fireEvent.click(zeroScore); - fireEvent.click(phase2Element); + await fireEvent.click(phase2Element); expect(toast.warning).toHaveBeenCalledWith( 'First Discard or Update your changes', @@ -301,6 +437,11 @@ describe('CRUD Of Trainee Attendance', () => { }); it("Doesn't Delete attendance Test for day without entries", async () => { await cleanup(); + jest.spyOn(React, 'useContext').mockImplementation(() => ({ + user: { + role: 'coordinator', + }, + })); render( @@ -327,7 +468,7 @@ describe('CRUD Of Trainee Attendance', () => { const updateLink2 = screen.getByTestId('update-link-2'); expect(updateLink2).toBeInTheDocument(); - expect(await screen.findByText('Example Virgile')).toBeInTheDocument(); + expect(await screen.findByText('test-trainee-name')).toBeInTheDocument(); fireEvent.change(weeksElement, { target: { value: '2' } }); fireEvent.change(weeksElement, { target: { value: '1' } }); @@ -345,4 +486,75 @@ describe('CRUD Of Trainee Attendance', () => { fireEvent.click(deleteBtn); }); + it('Pause attendance for team with active attendance', async () => { + await cleanup(); + jest.spyOn(React, 'useContext').mockImplementation(() => ({ + user: { + role: 'coordinator', + }, + })); + + render( + + + , + ); + + expect(await screen.findByText('Loading Data...')).toBeInTheDocument(); + + const teamElement = await screen.findByTestId('team-test'); + expect(teamElement).toBeInTheDocument(); + + fireEvent.change(teamElement, { + target: { value: 'Team-I-id-123' }, + }); + + const pauseAttendanceElement = await screen.findByText('Pause Attendance'); + expect(pauseAttendanceElement).toBeInTheDocument(); + + fireEvent.click(pauseAttendanceElement); + + const cancelBtn = screen.getByText('Cancel'); + expect(cancelBtn).toBeInTheDocument(); + fireEvent.click(cancelBtn); + }); + it('Resume attendance for team with inactive attendance', async () => { + await cleanup(); + jest.spyOn(React, 'useContext').mockImplementation(() => ({ + user: { + role: 'coordinator', + }, + })); + + mocks[0].result.data.getAllTeams![0].isJobActive = false; + + render( + + + , + ); + + expect(await screen.findByText('Loading Data...')).toBeInTheDocument(); + + const teamElement = await screen.findByTestId('team-test'); + expect(teamElement).toBeInTheDocument(); + + fireEvent.change(teamElement, { + target: { value: 'Team-I-id-123' }, + }); + + const phase1Element = await screen.findByText('Phase I'); + expect(phase1Element).toBeInTheDocument(); + + const resumeAttendanceElement = await screen.findByText( + 'Resume Attendance', + ); + expect(resumeAttendanceElement).toBeInTheDocument(); + + fireEvent.click(resumeAttendanceElement); + + const confirmBtn = screen.getByText('Confirm'); + expect(confirmBtn).toBeInTheDocument(); + fireEvent.click(confirmBtn); + }); }); diff --git a/tests/pages/__snapshots__/About.test.tsx.snap b/tests/pages/__snapshots__/About.test.tsx.snap index 539be8335..7df40910f 100644 --- a/tests/pages/__snapshots__/About.test.tsx.snap +++ b/tests/pages/__snapshots__/About.test.tsx.snap @@ -170,7 +170,6 @@ exports[`About page renders the about page 1`] = ` > Come shape the future together -
    - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    diff --git a/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap b/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap index cdb28ed1f..e3a616220 100644 --- a/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap +++ b/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap @@ -463,7 +463,7 @@ Array [ name="date" readOnly={true} type="text" - value="2024-10-06" + value="2024-10-24" />
    -
    diff --git a/tests/pages/__snapshots__/GradingSystem.test.tsx.snap b/tests/pages/__snapshots__/GradingSystem.test.tsx.snap index ce701c773..6dc4578ab 100644 --- a/tests/pages/__snapshots__/GradingSystem.test.tsx.snap +++ b/tests/pages/__snapshots__/GradingSystem.test.tsx.snap @@ -369,7 +369,7 @@ Array [ className="max-w-full" >
    -   - - - - - - -
    - -

    - - No records available - -

    -
    - + No records available +

    - @@ -504,14 +484,14 @@ Array [ colSpan={3} >
    diff --git a/tests/pages/__snapshots__/Home.test.tsx.snap b/tests/pages/__snapshots__/Home.test.tsx.snap index e8e6437c2..cc9b67cac 100644 --- a/tests/pages/__snapshots__/Home.test.tsx.snap +++ b/tests/pages/__snapshots__/Home.test.tsx.snap @@ -224,6 +224,32 @@ exports[`Home page renders the landing page 1`] = ` About +
  • + + Product + +
  • +
    +
    +

    + Docs +

    + + ▼ + +
    +
  • @@ -453,7 +479,6 @@ exports[`Home page renders the landing page 1`] = ` > Come shape the future together -
  • - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    - I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

    - I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

    - - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

    diff --git a/tests/pages/__snapshots__/Profile.test.tsx.snap b/tests/pages/__snapshots__/Profile.test.tsx.snap index 50a62de3d..da0515bac 100644 --- a/tests/pages/__snapshots__/Profile.test.tsx.snap +++ b/tests/pages/__snapshots__/Profile.test.tsx.snap @@ -165,16 +165,17 @@ Object { >
    -