diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 02e69112..44402c8c 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -11,8 +11,8 @@ "UPDATE": "Update", "MODIFY": "Modify", "CLEAR_ALL": "Clear All", - "SEARCH_STUDENT": "Search Student", - "SORT_BY": "sort by", + "SEARCH_STUDENT": "Search Learner", + "SORT_BY": "Sort by", "APPLY": "Apply", "A_TO_Z": "A TO Z", "Z_TO_A": "Z TO A", @@ -34,10 +34,17 @@ "DROPOUT_YEAR": "Dropout Year", "EMPLOYMENT_STATUS": "Employment Status", "ENROLLMENT_DATE": "Enrollment Date", + "AS_OF_LAST_SIX_MONTH": "As of last six month", "AS_OF_TODAY": "As of today", "AS_OF_LAST_WEEK": "As of last week", - "AS_OF_LAST_SIX_MONTH": "As of last six month", - "NONE": "None" + "LAST_SEVEN_DAYS": "Last 7 Days", + "LAST_MONTH": "Last Month", + "LAST_SIX_MONTHS": "Last Six Months", + "CUSTOM_RANGE": "Custom Range", + "NONE": "None", + "LEARNER_NAME": "Learner Name", + "DATE_RANGE": "Date Range", + "SELECT_AN_OPTION": "Select an option" }, "LOGIN_PAGE": { "USERNAME": "Username", @@ -72,7 +79,7 @@ "UPCOMING_EXTRA_SESSION": "Upcoming Extra Sessions" }, "ATTENDANCE": { - "TOTAL_STUDENTS": "Total No of Students: {{count}}", + "TOTAL_STUDENTS": "Total Number of Learners: {{count}}", "PRESENT": "Present", "ABSENT": "Absent", "HALF_DAY": "Half Day", @@ -86,7 +93,10 @@ "CLEAR_ATTENDANCE_ALERT": "Are you sure you want to clear this attendance?", "YES": "Yes", "NO_GO_BACK": "No Go Back", - "DAY_WISE_ATTENDANCE": "Day-Wise Attendance" + "DAY_WISE_ATTENDANCE": "Day-Wise Attendance", + "ATTENDANCE_OVERVIEW": "Attendance Overview", + "CENTER_ATTENDANCE": "Center Attendance", + "LOW_ATTENDANCE_STUDENTS": "Low Attendance Students" }, "PROFILE": { "EDIT_PROFILE": "Edit Profile", diff --git a/public/locales/hi/common.json b/public/locales/hi/common.json index e6e2cd43..a5789770 100644 --- a/public/locales/hi/common.json +++ b/public/locales/hi/common.json @@ -34,9 +34,17 @@ "EMPLOYMENT_STATUS": "रोज़गार स्थिति", "ENROLLMENT_DATE": "नामांकन की तिथि", "AS_OF_TODAY": "आज से", + "LAST_SEVEN_DAYS": "पिछले 7 दिन", + "LAST_MONTH": "पिछला महीना", + "LAST_SIX_MONTHS": "पिछले छह महीने", + "CUSTOM_RANGE": "कस्टम रेंज", "AS_OF_LAST_WEEK": "पिछले सप्ताह तक", "AS_OF_LAST_SIX_MONTH": "पिछले छह महीने से", - "NONE": "कोई नहीं" + "NONE": "कोई नहीं", + "LEARNER_NAME": "शिक्षार्थी का नाम", + "DATE_RANGE": "तिथि सीमा", + "SELECT_AN_OPTION": "कोई विकल्प चुनें" + }, "LOGIN_PAGE": { "USERNAME": "उपयोगकर्ता नाम", @@ -85,7 +93,10 @@ "YES": "हां", "NO_GO_BACK": "नहीं वापस जाएं", "MY_TIMETABLE": "मेरी समय सारिणी", - "DAY_WISE_ATTENDANCE": "दिनविशेष उपस्थिति" + "DAY_WISE_ATTENDANCE": "दिनविशेष उपस्थिति", + "ATTENDANCE_OVERVIEW": "उपस्थिति अवलोकन", + "CENTER_ATTENDANCE": "केंद्र उपस्थिति", + "LOW_ATTENDANCE_STUDENTS": "कम उपस्थिति वाले छात्र" }, "PROFILE": { "EDIT_PROFILE": "प्रोफ़ाइल संपादित करें", diff --git a/public/locales/mr/common.json b/public/locales/mr/common.json index 06b9a56f..a759f437 100644 --- a/public/locales/mr/common.json +++ b/public/locales/mr/common.json @@ -32,9 +32,19 @@ "EMPLOYMENT_STATUS": "रोज़गार की स्थिति", "ENROLLMENT_DATE": "नावनोंदणी तारीख", "AS_OF_TODAY": "आजपासून", + "LAST_SEVEN_DAYS": "शेवटचे ७ दिवस", + "LAST_MONTH": "गेल्या महिन्यात", + "LAST_SIX_MONTHS": "शेवटचे सहा महिने", + "CUSTOM_RANGE": "कस्टम रेंज", + "AS_OF_LAST_WEEK": "गेल्या आठवड्याप्रमाणे", "AS_OF_LAST_SIX_MONTH": "गेल्या सहा महिन्यांपासून", - "NONE": "काहीही नाही" + "NONE": "काहीही नाही", + "LEARNER_NAME": "शिकणाऱ्याचे नाव", + "DATE_RANGE": "तारीख श्रेणी", + "SELECT_AN_OPTION": "एक पर्याय निवडा" + + }, "LOGIN_PAGE": { "USERNAME": "वापरकर्ता नाव", @@ -79,12 +89,15 @@ "CLEAR_ATTENDANCE_ALERT": "आपल्याकडून हजेरी साफ करायला खात्री आहे का?", "YES": "हो", "NO_GO_BACK": "नाही लौटा", - "DAY_WISE_ATTENDANCE": "दिवस-विशेष उपस्थिती" + "DAY_WISE_ATTENDANCE": "दिवस-विशेष उपस्थिती", + "ATTENDANCE_OVERVIEW": "उपस्थिती विहंगावलोकन", + "CENTER_ATTENDANCE": "केंद्र उपस्थिती", + "LOW_ATTENDANCE_STUDENTS": "कमी उपस्थिती विद्यार्थी" }, "PROFILE": { "EDIT_PROFILE": "प्रोफाइल संपादित करा", "CONTACT_INFORMATION": "संपर्क माहिती", - "OTHER_INFORMATION": "Other Information", + "OTHER_INFORMATION": "इतर माहिती", "PHONE": "फोन", "EMAIL_ID": "ईमेल आयडी", "BIO": "बायो", diff --git a/src/components/DateRangePopup.tsx b/src/components/DateRangePopup.tsx new file mode 100644 index 00000000..861cfd79 --- /dev/null +++ b/src/components/DateRangePopup.tsx @@ -0,0 +1,143 @@ +import React, { useState } from 'react'; +import { + Box, + Button, + Divider, + FormControl, + Grid, + MenuItem, + MenuList, + Modal, + Select, + Typography, +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import Check from '@mui/icons-material/Check'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import { useTranslation } from 'next-i18next'; + +const modalStyle = { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: 400, + bgcolor: 'background.paper', + border: '2px solid #000', + boxShadow: 24, + p: 4, +}; + +const dividerStyle = { + my: 2, +}; + +interface CustomSelectModalProps { + menuItems: string[]; + selectedValue: string; + setSelectedValue: (value: string) => void; +} + +const DateRangePopup: React.FC = ({ + menuItems, + selectedValue, + setSelectedValue, +}) => { + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedIndex, setSelectedIndex] = useState(null); + + const handleModalOpen = () => setIsModalOpen(true); + const handleModalClose = () => setIsModalOpen(false); + const { t } = useTranslation(); + + const handleMenuItemClick = (index: number, item: string) => { + setSelectedIndex(index); + setSelectedValue(item); + handleModalClose(); + }; + + return ( + + + + + + + + + + + + + + + {t('COMMON.DATE_RANGE')} + + + + + + + + + {menuItems.map((item, index) => ( + handleMenuItemClick(index, item)} + sx={{ + display: 'flex', + alignItems: 'center', + paddingLeft: '32px', + }} + > + {selectedIndex === index && ( + + + + )} + {item} + + ))} + + + + + + + ); +}; + +export default DateRangePopup; diff --git a/src/components/LearnerAttendanceStatsListView.tsx b/src/components/LearnerAttendanceStatsListView.tsx index a06f8ea8..4f67da85 100644 --- a/src/components/LearnerAttendanceStatsListView.tsx +++ b/src/components/LearnerAttendanceStatsListView.tsx @@ -3,28 +3,28 @@ import React from 'react'; import { Box, Grid, Stack, Typography } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import Link from 'next/link' +import Link from 'next/link'; interface StudentsStatsListProps { name: string; - value1: number; - value2: number; + presentPercent: number; + classesMissed: number; userId?: string; cohortId?: string; } const StudentsStatsList: React.FC = ({ name, - value1, - value2, + presentPercent, + classesMissed, userId, cohortId, }) => { const theme = useTheme(); const { t } = useTranslation(); -// const handleStudentDetails = () => { -// router.push(`/student-details/${cohortId}/${userId}`); -// }; + // const handleStudentDetails = () => { + // router.push(`/student-details/${cohortId}/${userId}`); + // }; return ( @@ -38,35 +38,35 @@ const StudentsStatsList: React.FC = ({ - - {name} + + {name} - {value1}% + {presentPercent}% + + + + + {classesMissed} - - - {value2} - - diff --git a/src/components/LearnerListHeader.tsx b/src/components/LearnerListHeader.tsx new file mode 100644 index 00000000..1ad0c818 --- /dev/null +++ b/src/components/LearnerListHeader.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { useTranslation } from 'next-i18next'; +import { Box,Grid,Stack,Typography } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; +import { LearListHeaderProps } from '@/utils/Interfaces'; + +const LearnerListHeader: React.FC = ({ + numberOfColumns, + firstColumnName, + secondColumnName, + +}) => { + const { t } = useTranslation(); + const theme = useTheme(); + + + + return ( + + {(numberOfColumns == 3)? + + + + {t('COMMON.LEARNER_NAME')} + + + {firstColumnName} + + + {secondColumnName} + + + : 'none'} + + ); +}; + +export default LearnerListHeader; diff --git a/src/components/WeekDays.tsx b/src/components/WeekDays.tsx index f181e665..7039a04f 100644 --- a/src/components/WeekDays.tsx +++ b/src/components/WeekDays.tsx @@ -21,7 +21,7 @@ const WeekDays: React.FC = ({ useAbbreviation }) => { const days = useAbbreviation ? ['S', 'M', 'T', 'W', 'T', 'F', 'S'] - : ['Sun', 'Mon', 'Tues', 'Wed', 'Thu', 'Fri', 'Sat']; + : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const currentDate = new Date(); const currentDayIndex = currentDate.getDay(); diff --git a/src/pages/attendance-history.tsx b/src/pages/attendance-history.tsx index 97e94ef1..64c91e33 100644 --- a/src/pages/attendance-history.tsx +++ b/src/pages/attendance-history.tsx @@ -48,6 +48,7 @@ import { usePathname } from 'next/navigation'; import { useRouter } from 'next/router'; import { useTheme } from '@mui/material/styles'; import { useTranslation } from 'next-i18next'; +import LearnerListHeader from '@/components/LearnerListHeader'; interface user { userId: string; @@ -59,6 +60,7 @@ interface user { const UserAttendanceHistory = () => { const theme = useTheme(); const { t } = useTranslation(); + const { push } = useRouter(); const [selectedDate, setSelectedDate] = useState(new Date()); const [classId, setClassId] = React.useState(''); const [cohortsData, setCohortsData] = React.useState>([]); @@ -108,6 +110,18 @@ const UserAttendanceHistory = () => { setOpen(false); }; + useEffect(() => { + if (typeof window !== 'undefined' && window.localStorage) { + const token = localStorage.getItem('token'); + setLoading(false); + if (token) { + push('/attendance-history'); + } else { + push('/login', undefined, { locale: 'en' }); + } + } + }, []); + // API call to get center list useEffect(() => { const fetchCohortList = async () => { @@ -706,12 +720,14 @@ const UserAttendanceHistory = () => { {status && ( )} + {cohortMemberList?.length > 0 ? ( {displayStudentList?.map((user: any) => ( diff --git a/src/pages/attendance-overview.tsx b/src/pages/attendance-overview.tsx index ba0a3722..07c6034c 100644 --- a/src/pages/attendance-overview.tsx +++ b/src/pages/attendance-overview.tsx @@ -25,18 +25,16 @@ import { cohortList } from '@/services/CohortServices'; import { useRouter } from 'next/router'; import { usePathname } from 'next/navigation'; import React, { useEffect } from 'react'; -import { - cohort, -} from '@/utils/Interfaces'; +import { cohort } from '@/utils/Interfaces'; import { useTheme } from '@mui/material/styles'; import SortingModal from '@/components/SortingModal'; import { debounce } from '@/utils/Helper'; -import { - classesMissedAttendancePercentList, -} from '@/services/AttendanceService'; +import { classesMissedAttendancePercentList } from '@/services/AttendanceService'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { getMyCohortMemberList } from '@/services/MyClassDetailsService'; import StudentsStatsList from '@/components/LearnerAttendanceStatsListView'; +import LearnerListHeader from '@/components/LearnerListHeader'; +import DateRangePopup from '@/components/DateRangePopup'; interface AttendanceOverviewProps { // buttonText: string; @@ -54,10 +52,20 @@ const AttendanceOverview: React.FC = () => { const [displayStudentList, setDisplayStudentList] = React.useState< Array >([]); + const [selectedValue, setSelectedValue] = React.useState(''); const theme = useTheme(); const pathname = usePathname(); + const menuItems = [ + t('COMMON.LAST_SEVEN_DAYS'), + t('COMMON.AS_OF_TODAY'), + t('COMMON.AS_OF_LAST_WEEK'), + t('COMMON.LAST_MONTH'), + t('COMMON.LAST_SIX_MONTHS'), + t('COMMON.CUSTOM_RANGE'), + ]; + // API call to get center list useEffect(() => { const fetchCohortList = async () => { @@ -142,6 +150,7 @@ const AttendanceOverview: React.FC = () => { }); }); setlearnerData(mergedArray); + console.log('//////////////////', mergedArray) setDisplayStudentList(mergedArray); } } @@ -166,32 +175,32 @@ const AttendanceOverview: React.FC = () => { const handleSearchClear = () => { setSearchWord(''); - // setDisplayStudentList(cohortMemberList); + setDisplayStudentList(learnerData); }; // debounce use for searching time period is 2 sec const debouncedSearch = debounce((value: string) => { - // let filteredList = cohortMemberList?.filter((user: any) => - // user.name.toLowerCase().includes(value.toLowerCase()) - // ); - // setDisplayStudentList(filteredList) + let filteredList = learnerData?.filter((user: any) => + user.name.toLowerCase().includes(value.toLowerCase()) + ); + setDisplayStudentList(filteredList) }, 200); // handle search student data const handleSearch = (event: React.ChangeEvent) => { setSearchWord(event.target.value); - // if (event.target.value.length >= 3) { - // debouncedSearch(event.target.value); - // } else { - // setDisplayStudentList(cohortMemberList) - // } + if (event.target.value.length >= 3) { + debouncedSearch(event.target.value); + } else { + setDisplayStudentList(learnerData) + } }; const handleSearchSubmit = () => { - // let filteredList = cohortMemberList?.filter((user: any) => - // user.name.toLowerCase().includes(searchWord.toLowerCase()) - // ); - // setDisplayStudentList(filteredList) + let filteredList = learnerData?.filter((user: any) => + user.name.toLowerCase().includes(searchWord.toLowerCase()) + ); + setDisplayStudentList(filteredList) }; // open modal of sort @@ -205,200 +214,196 @@ const AttendanceOverview: React.FC = () => { }; //handel sorting - const handleSorting = () => {}; + const handleSorting = (sortByName: string, sortByAttendanceNumber: string, sortByClassesMissed: string) => { + handleCloseModal(); + let sortedData = [...learnerData]; + + // Sorting by name + switch (sortByName) { + case 'asc': + sortedData.sort((a, b) => a.name.localeCompare(b.name)); + break; + case 'desc': + sortedData.sort((a, b) => b.name.localeCompare(a.name)); + break; + } + + // Sorting by attendance + switch (sortByAttendanceNumber) { + case 'high': + sortedData.sort((a, b) => parseFloat(b.present_percent) - parseFloat(a.present_percent)); + break; + case 'low': + return sortedData.sort((b, a) => parseFloat(a.present_percent) - parseFloat(b.present_percent)); + break; + } + setDisplayStudentList(sortedData); + }; return ( - <> - -
- - - - {t('Attendance Overview')} - - + +
+ + + + {t('ATTENDANCE.ATTENDANCE_OVERVIEW')} + + - - - - - - + + + + + + - - - - - - - + - - - - + + + - + + - - - - - + + + + + + - + + + + {searchWord?.length > 0 && ( - + - - {searchWord?.length > 0 && ( - - - - )} - - - - - + )} + - - - + + + + + + + + + {learnerData?.length > 0 ? ( + + {displayStudentList?.map((user: any) => ( + + ))} + + ) : ( + + {t('COMMON.NO_DATA_FOUND')} + + )} + ); }; diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index 44915e7f..80bd0a9e 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -111,8 +111,8 @@ const Dashboard: React.FC = () => { useEffect(() => { if (typeof window !== 'undefined' && window.localStorage) { - const refreshToken = localStorage.getItem('refreshToken'); - if (refreshToken) { + const token = localStorage.getItem('token'); + if (token) { setIsAuthenticated(true); } else { router.push('/login'); @@ -189,6 +189,7 @@ const Dashboard: React.FC = () => { filters: { fromDate: selectedDate || currentDate, toDate: selectedDate || currentDate, + contextId: classId }, }; const res = await attendanceStatusList(attendanceStatusData); @@ -209,12 +210,12 @@ const Dashboard: React.FC = () => { const attendance = response.find( (status) => status.userId === userId ); - if (attendance) { - userAttendanceArray.push({ - userId, - attendance: attendance.attendance, - }); - } + userAttendanceArray.push({ + userId, + attendance: attendance?.attendance + ? attendance.attendance + : '', + }); }); return userAttendanceArray; }; @@ -866,7 +867,7 @@ const Dashboard: React.FC = () => { diff --git a/src/pages/index.tsx b/src/pages/index.tsx index b7908f88..bae160fa 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -8,16 +8,16 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; // const Dashboard = dynamic(() => import('./Dashboard'), { ssr: false }); const Home: React.FC = () => { - const { push } = useRouter(); + const { push } = useRouter(); const { t } = useTranslation(); const [loading, setLoading] = React.useState(true); useEffect(() => { if (typeof window !== 'undefined' && window.localStorage) { - const refreshToken = localStorage.getItem('refreshToken'); + const token = localStorage.getItem('token'); setLoading(false); - if (refreshToken) { + if (token) { push('/dashboard'); } else { push('/login', undefined, { locale: 'en' }); @@ -25,11 +25,7 @@ const Home: React.FC = () => { } }, []); - return ( - <> - {loading &&

{t('COMMON.LOADING')}...

} - - ); + return <>{loading &&

{t('COMMON.LOADING')}...

}; }; export async function getStaticProps({ locale }: any) { diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 3118747f..11a76b21 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -51,7 +51,7 @@ const LoginPage = () => { // const location = useLocation(); const DEFAULT_POSITION: Pick = { - vertical: 'top', + vertical: 'bottom', horizontal: 'center', }; const [state, setState] = React.useState({ @@ -64,8 +64,8 @@ const LoginPage = () => { if (typeof window !== 'undefined' && window.localStorage) { const lang = localStorage.getItem('preferredLanguage') || 'en'; setLanguage(lang); - const refreshToken = localStorage.getItem('refreshToken'); - if (refreshToken) { + const token = localStorage.getItem('token'); + if (token) { router.push('/dashboard'); } } @@ -222,7 +222,7 @@ const LoginPage = () => { style={{ borderRadius: '0.5rem', color: theme.palette.warning['200'], - width: 'auto', + width: '7rem', marginBottom: '0rem', }} > @@ -244,7 +244,6 @@ const LoginPage = () => { }} > { > { /> - + @@ -313,7 +311,7 @@ const LoginPage = () => { diff --git a/src/pages/teacher-profile.tsx b/src/pages/teacher-profile.tsx index 6ea8e0e5..909f77b9 100644 --- a/src/pages/teacher-profile.tsx +++ b/src/pages/teacher-profile.tsx @@ -75,8 +75,8 @@ const TeacherProfile = () => { useEffect(() => { if (typeof window !== 'undefined' && window.localStorage) { - const refreshToken = localStorage.getItem('refreshToken'); - if (refreshToken) { + const token = localStorage.getItem('token'); + if (token) { setIsAuthenticated(true); } else { router.push('/login'); @@ -84,7 +84,6 @@ const TeacherProfile = () => { } }, []); - const handleUpdateClick = async () => { setLoading(true); try { diff --git a/src/services/AttendanceService.ts b/src/services/AttendanceService.ts index d29cf1cc..75864335 100644 --- a/src/services/AttendanceService.ts +++ b/src/services/AttendanceService.ts @@ -42,12 +42,12 @@ const postAttendanceList = async ({ export const attendanceStatusList = async ({ limit, page, - filters: { fromDate, toDate }, + filters: { fromDate, toDate, contextId }, }: AttendanceStatusListProps): Promise => { return postAttendanceList({ limit, page, - filters: { fromDate, toDate }, + filters: { fromDate, toDate, contextId }, }); }; diff --git a/src/utils/Interfaces.ts b/src/utils/Interfaces.ts index b2d7e2d7..d98021cb 100644 --- a/src/utils/Interfaces.ts +++ b/src/utils/Interfaces.ts @@ -100,6 +100,7 @@ export interface AttendanceStatusListProps { filters: { fromDate: string | Date; toDate: string | Date; + contextId?: string; }; } @@ -124,3 +125,10 @@ export interface cohort { name: string; value: string; } + +export interface LearListHeaderProps { + numberOfColumns: number, + firstColumnName: string, + secondColumnName: string, +} +