From 7766e95482fa5a627007128da409180a36ebd032 Mon Sep 17 00:00:00 2001 From: suvarnakale Date: Sat, 1 Jun 2024 15:58:11 +0530 Subject: [PATCH] Issue #PS-628 and PS-629 feat: Mark attendance UI and API integration --- src/components/AttendanceStatus.tsx | 192 ++++++++++++++++------- src/components/MarkAttendance.tsx | 164 +++++++++++-------- src/components/MonthCalender.tsx | 1 - src/pages/attendance-history.tsx | 2 +- src/pages/learner-attendance-history.tsx | 26 ++- src/services/AttendanceService.ts | 21 +++ src/styles/globals.css | 4 - src/utils/Interfaces.ts | 10 +- 8 files changed, 283 insertions(+), 137 deletions(-) diff --git a/src/components/AttendanceStatus.tsx b/src/components/AttendanceStatus.tsx index 26fcebf2..8797851b 100644 --- a/src/components/AttendanceStatus.tsx +++ b/src/components/AttendanceStatus.tsx @@ -2,14 +2,19 @@ import { Box, Button, Grid, Typography } from '@mui/material'; import { CircularProgressbar, buildStyles } from 'react-circular-progressbar'; import { formatToShowDateMonth, shortDateFormat } from '@/utils/Helper'; -import { CreateOutlined } from '@mui/icons-material'; +import { + CancelOutlined, + CheckCircleOutlineOutlined, + CreateOutlined, +} from '@mui/icons-material'; import useDeterminePathColor from '../hooks/useDeterminePathColor'; import { useTheme } from '@mui/material/styles'; import { useTranslation } from 'next-i18next'; interface AttendanceStatusProps { date: Date; - formattedAttendanceData: FormattedAttendanceData; + formattedAttendanceData?: FormattedAttendanceData; + learnerAttendanceData?: learnerAttendanceData; onDateSelection: Date; onUpdate?: () => void; } @@ -25,9 +30,14 @@ type FormattedAttendanceData = { [date: string]: AttendanceData; }; +type learnerAttendanceData = { + [date: string]: AttendanceData; +}; + function AttendanceStatus({ date, formattedAttendanceData, + learnerAttendanceData, onDateSelection, onUpdate, }: AttendanceStatusProps) { @@ -37,6 +47,7 @@ function AttendanceStatus({ const selectedDate = shortDateFormat(onDateSelection); const dateString = shortDateFormat(onDateSelection); const attendanceData = formattedAttendanceData?.[dateString]; + const learnerAttendance = learnerAttendanceData?.[dateString]; const todayDate = shortDateFormat(new Date()); const currentDate = new Date(); const sevenDaysAgo = new Date(); @@ -45,6 +56,7 @@ function AttendanceStatus({ const currentAttendance = formattedAttendanceData?.[dateString] || 'notMarked'; let attendanceStatus; + let learnerAttendanceStatus = learnerAttendance?.attendanceStatus; if (!attendanceData) { attendanceStatus = 'notMarked'; @@ -53,6 +65,33 @@ function AttendanceStatus({ } } + if (learnerAttendanceData && !learnerAttendance) { + learnerAttendanceStatus = 'notMarked'; + if (selectedDate > todayDate) { + learnerAttendanceStatus = 'futureDate'; + } + } + + let icon, message; + switch (learnerAttendanceStatus) { + case 'present': + icon = ; + message = 'Present'; + break; + case 'absent': + icon = ; + message = 'Absent'; + break; + case 'notMarked': + message = t('DASHBOARD.NOT_MARKED'); + break; + case 'futureDate': + message = t('DASHBOARD.FUTURE_DATE_CANT_MARK'); + break; + default: + break; + } + const presentPercentage = currentAttendance?.present_percentage; const pathColor = determinePathColor(presentPercentage); @@ -76,72 +115,102 @@ function AttendanceStatus({ {formatToShowDateMonth(date)} - - {attendanceStatus !== 'notMarked' && - attendanceStatus !== 'futureDate' && ( - <> - - - - - - {t('DASHBOARD.PERCENT_ATTENDANCE', { - percent_students: currentAttendance?.present_percentage, - })} - -   - + {attendanceStatus !== 'notMarked' && + attendanceStatus !== 'futureDate' && ( + <> + - {t('DASHBOARD.PRESENT_STUDENTS', { - present_students: currentAttendance?.present_students, - total_students: currentAttendance?.totalcount, - })} - - - + + + + + {t('DASHBOARD.PERCENT_ATTENDANCE', { + percent_students: + currentAttendance?.present_percentage, + })} + +   + + {t('DASHBOARD.PRESENT_STUDENTS', { + present_students: currentAttendance?.present_students, + total_students: currentAttendance?.totalcount, + })} + + + + )} + + {attendanceStatus === 'notMarked' && ( + + {t('DASHBOARD.NOT_MARKED')} + )} - {attendanceStatus === 'notMarked' && ( - - {t('DASHBOARD.NOT_MARKED')} - - )} + {attendanceStatus === 'futureDate' && ( + + {t('DASHBOARD.FUTURE_DATE_CANT_MARK')} + + )} + + )} - {attendanceStatus === 'futureDate' && ( + {learnerAttendanceStatus && ( + + {icon && ( +
+ {icon} +
+ )} - {t('DASHBOARD.FUTURE_DATE_CANT_MARK')} + {message} - )} - +
+ )} + {onUpdate && ( - + )} diff --git a/src/components/MarkAttendance.tsx b/src/components/MarkAttendance.tsx index 33501f0b..4e4a3d21 100644 --- a/src/components/MarkAttendance.tsx +++ b/src/components/MarkAttendance.tsx @@ -20,8 +20,9 @@ import CloseIcon from '@mui/icons-material/Close'; import HighlightOffIcon from '@mui/icons-material/HighlightOff'; // import RemoveCircleIcon from '@mui/icons-material/RemoveCircle'; //Half-Day // import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline'; -import { MarkAttendanceProps } from '../utils/Interfaces'; +import { MarkAttendanceParams, MarkAttendanceProps } from '../utils/Interfaces'; import Snackbar, { SnackbarOrigin } from '@mui/material/Snackbar'; +import { markAttendance } from '@/services/AttendanceService'; interface State extends SnackbarOrigin { openModal: boolean; @@ -39,22 +40,20 @@ const BootstrapDialog = styled(Dialog)(({ theme }) => ({ })); const MarkAttendance: React.FC = ({ isOpen, - isSelfAttendance = true, + isSelfAttendance, date, name, currentStatus, + onAttendanceUpdate, handleClose, - handleSubmit, - message, }) => { const { t } = useTranslation(); - const [status, setStatus] = React.useState(currentStatus); + const [updatedStatus, setUpdatedStatus] = React.useState(currentStatus); useEffect(() => { - setStatus(currentStatus); + setUpdatedStatus(currentStatus); }, [currentStatus]); const theme = useTheme(); - const SNACKBAR_AUTO_HIDE_DURATION = 5000; const [openMarkUpdateAttendance, setOpenMarkUpdateAttendance] = React.useState(false); const handleMarkUpdateAttendanceModal = () => @@ -70,51 +69,78 @@ const MarkAttendance: React.FC = ({ horizontal: 'center', }); const { vertical, horizontal, openModal } = modal; - const submitUpdateAttendance = () => { - handleClose(); - handleMarkUpdateAttendanceModal(); - }; - const submitClearAttendance = () => { - setStatus(ATTENDANCE_ENUM.NOT_MARKED); + + const submitUpdateAttendance = async () => { + console.log(updatedStatus); + try { + const learnerId = localStorage.getItem('learnerId'); + const classId = localStorage.getItem('classId'); + if (classId && learnerId) { + const markAttendanceRequest: MarkAttendanceParams = { + userId: learnerId, + attendanceDate: date, + contextId: classId, + attendance: updatedStatus, + }; + const response = await markAttendance(markAttendanceRequest); + setUpdatedStatus(response?.data?.attendance); + onAttendanceUpdate(); + } + } catch (error) { + console.log(error); + } + handleClick({ vertical: 'bottom', horizontal: 'center' })(); handleClose(); - handleMarkClearAttendanceModal(); + // handleMarkUpdateAttendanceModal(); }; - const submitAttendance = (newState: SnackbarOrigin) => () => { - handleSubmit(date, status); - // setOpenMarkUpdateAttendance(!openMarkUpdateAttendance); + + const handleClick = (newState: SnackbarOrigin) => () => { setModal({ ...newState, openModal: true }); - setTimeout(() => { - handleClose2(); - }, SNACKBAR_AUTO_HIDE_DURATION); }; - const submitConfirmAttendance = (newState: SnackbarOrigin) => () => { - // setOpenMarkClearAttendance(!openMarkClearAttendance); - handleMarkUpdateAttendanceModal(); - handleSubmit(date, status); + // const submitClearAttendance = () => { + // setStatus(ATTENDANCE_ENUM.NOT_MARKED); + // handleClose(); + // handleMarkClearAttendanceModal(); + // }; - setModal({ ...newState, openModal: true }); - setTimeout(() => { - handleClose2(); - }, SNACKBAR_AUTO_HIDE_DURATION); - }; + // const submitAttendance = (newState: SnackbarOrigin) => () => { + // handleSubmit(date, status); + // // setOpenMarkUpdateAttendance(!openMarkUpdateAttendance); + // setModal({ ...newState, openModal: true }); + // setTimeout(() => { + // handleSnackbarClose(); + // }, SNACKBAR_AUTO_HIDE_DURATION); + // }; + + // const submitConfirmAttendance = (newState: SnackbarOrigin) => () => { + // // setOpenMarkClearAttendance(!openMarkClearAttendance); + // handleMarkUpdateAttendanceModal(); + // handleSubmit(date, status); - const handleClose2 = () => { + // setModal({ ...newState, openModal: true }); + // setTimeout(() => { + // handleSnackbarClose(); + // }, SNACKBAR_AUTO_HIDE_DURATION); + // }; + + const handleSnackbarClose = () => { setModal({ ...modal, openModal: false }); }; - const handleClear = () => { - if (status !== ATTENDANCE_ENUM.NOT_MARKED) { - setStatus(ATTENDANCE_ENUM.NOT_MARKED); - } - }; + + // const handleClear = () => { + // if (status !== ATTENDANCE_ENUM.NOT_MARKED) { + // setStatus(ATTENDANCE_ENUM.NOT_MARKED); + // } + // }; + const getButtonComponent = ( value: string, icon1: IconType, icon2: IconType, text: string ) => { - //setStatus(currentStatus) - // console.log(status === value) + // setStatus(currentStatus); return ( = ({ flexDirection="column" alignItems="center" p={2} - onClick={() => setStatus(value)} + onClick={() => setUpdatedStatus(value)} > - {status === value ? icon1 : icon2} + {updatedStatus === value ? icon1 : icon2} {text} ); }; + return ( = ({ variant="outlined" autoFocus onClick={() => { - if (currentStatus === ATTENDANCE_ENUM.NOT_MARKED) { - { - handleClear(); - } - } else { - submitClearAttendance(); - } + // if (currentStatus === ATTENDANCE_ENUM.NOT_MARKED) { + // { + handleClose(); + + // handleClear(); + // } + // } else { + // submitClearAttendance(); + // } }} sx={{ width: '100%', }} > - {t('ATTENDANCE.CLEAR')} + {t('COMMON.CANCEL')}