{isVisible && (
@@ -61,6 +78,7 @@ const UpDownButton = () => {
{t('DASHBOARD.BACK_TO_TOP')}
@@ -69,6 +87,7 @@ const UpDownButton = () => {
{t('COMMON.LEARNERS')}
diff --git a/src/components/WeekDays.tsx b/src/components/WeekDays.tsx
index 7d46779f..1add9e0f 100644
--- a/src/components/WeekDays.tsx
+++ b/src/components/WeekDays.tsx
@@ -17,7 +17,7 @@ const CardStyled = styled(Card)(({ theme }) => ({
border: '1px solid #ccc',
boxShadow: 'none',
backgroundImage: 'none',
- overflow: 'visible'
+ overflow: 'visible',
}));
const WeekDays: React.FC = ({ useAbbreviation }) => {
@@ -41,13 +41,18 @@ const WeekDays: React.FC = ({ useAbbreviation }) => {
const itemLeft = selectedItem.offsetLeft;
const itemWidth = selectedItem.offsetWidth;
- const scrollPosition = itemLeft - (containerWidth / 2) + (itemWidth / 2);
+ const scrollPosition = itemLeft - containerWidth / 2 + itemWidth / 2;
scrollContainer.scrollTo({ left: scrollPosition, behavior: 'smooth' });
}
}, []);
return (
-
+
{days.map((day, index) => (
{
const theme = useTheme();
const determinePathColor = (presentPercentage) => {
// if (presentPercentage == 0) return theme.palette.warning['400'];
- if (presentPercentage < lowLearnerAttendanceLimit) return theme.palette.error.main;
- if (presentPercentage <= avgLearnerAttendanceLimit) return theme.palette.action.activeChannel;
+ if (presentPercentage < lowLearnerAttendanceLimit)
+ return theme.palette.error.main;
+ if (presentPercentage <= avgLearnerAttendanceLimit)
+ return theme.palette.action.activeChannel;
return theme.palette.success.main;
};
diff --git a/src/hooks/useDeterminePathColor.js b/src/hooks/useDeterminePathColor.js
index c372a232..0810987a 100644
--- a/src/hooks/useDeterminePathColor.js
+++ b/src/hooks/useDeterminePathColor.js
@@ -1,13 +1,18 @@
import { useTheme } from '@mui/material/styles';
-import { avgLearnerAttendanceLimit, lowLearnerAttendanceLimit } from '../../app.config';
+import {
+ avgLearnerAttendanceLimit,
+ lowLearnerAttendanceLimit,
+} from '../../app.config';
const useDeterminePathColor = () => {
const theme = useTheme();
const determinePathColor = (presentPercentage) => {
if (presentPercentage == 0) return theme.palette.warning['400'];
- if (presentPercentage < lowLearnerAttendanceLimit) return theme.palette.error.main;
- if (presentPercentage <= avgLearnerAttendanceLimit) return theme.palette.action.activeChannel;
+ if (presentPercentage < lowLearnerAttendanceLimit)
+ return theme.palette.error.main;
+ if (presentPercentage <= avgLearnerAttendanceLimit)
+ return theme.palette.action.activeChannel;
return theme.palette.success.main;
};
diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index e65e425c..852384d5 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -4,7 +4,6 @@ import Typography from '@mui/material/Typography';
import ErrorIcon from '../../public/images/404.png'; // Make sure to replace this with the actual path to your image
import Image from 'next/image';
-
const PageNotFound = () => {
return (
{} });
const poppins = Poppins({
@@ -49,6 +54,53 @@ export function DarkTheme() {
}
function App({ Component, pageProps }: AppProps) {
+ const router = useRouter();
+ useEffect(() => {
+ telemetryFactory.init();
+ }, []);
+
+ useEffect(() => {
+ // Initialize GA only once
+ if (!window.GA_INITIALIZED) {
+ initGA(`${process.env.NEXT_PUBLIC_MEASUREMENT_ID}`);
+ window.GA_INITIALIZED = true;
+ }
+
+ const handleRouteChange = (url: string) => {
+ const windowUrl = url;
+
+ const cleanedUrl = windowUrl.replace(/^\//, '');
+
+ const telemetryImpression = {
+ context: {
+ env: cleanedUrl,
+ cdata: [],
+ },
+ edata: {
+ id: cleanedUrl,
+ type: 'VIEW',
+ subtype: '',
+ pageid: cleanedUrl,
+ uid: localStorage.getItem('userId') || 'Anonymous',
+ },
+ };
+ telemetryFactory.impression(telemetryImpression);
+
+ logPageView(url);
+ };
+
+ // Log initial page load
+ handleRouteChange(window.location.pathname);
+
+ // Subscribe to route changes and log page views
+ router.events.on('routeChangeComplete', handleRouteChange);
+
+ // Clean up the subscription on unmount
+ return () => {
+ router.events.off('routeChangeComplete', handleRouteChange);
+ };
+ }, [router.events]);
+
function ModeToggle() {
const { mode, setMode } = useColorScheme();
return (
@@ -70,8 +122,13 @@ function App({ Component, pageProps }: AppProps) {
`}
{/* */}
-
+
+
>
diff --git a/src/pages/attendance-history.tsx b/src/pages/attendance-history.tsx
index aa7da4b9..8ebb6d36 100644
--- a/src/pages/attendance-history.tsx
+++ b/src/pages/attendance-history.tsx
@@ -19,6 +19,7 @@ import {
Typography,
} from '@mui/material';
import React, { useEffect, useRef, useState } from 'react';
+import ReactGA from 'react-ga4';
import {
debounce,
getTodayDate,
@@ -48,7 +49,8 @@ import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router';
import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'next-i18next';
-import ToastMessage from '@/components/ToastMessage';
+import { logEvent } from '@/utils/googleAnalytics';
+import { showToastMessage } from '@/components/Toastify';
interface user {
userId: string;
@@ -84,7 +86,6 @@ const UserAttendanceHistory = () => {
const [open, setOpen] = useState(false);
const [handleSaveHasRun, setHandleSaveHasRun] = React.useState(false);
const searchRef = useRef(null);
- const [isError, setIsError] = React.useState(false);
const pathname = usePathname();
let userId: string;
@@ -166,7 +167,7 @@ const UserAttendanceHistory = () => {
}
} catch (error) {
console.error('Error fetching cohort list:', error);
- setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
setLoading(false);
}
};
@@ -335,7 +336,7 @@ const UserAttendanceHistory = () => {
}
} catch (error) {
console.error('Error fetching cohort list:', error);
- setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
setLoading(false);
} finally {
setLoading(false);
@@ -404,6 +405,9 @@ const UserAttendanceHistory = () => {
const handleCohortSelection = (event: SelectChangeEvent) => {
setClassId(event.target.value as string);
+ ReactGA.event('cohort-selection-attendance-history-page', {
+ selectedCohortID: event.target.value,
+ });
setHandleSaveHasRun(!handleSaveHasRun);
// ---------- set cohortId and stateName-----------
@@ -440,6 +444,9 @@ const UserAttendanceHistory = () => {
// handle search student data
const handleSearch = (event: React.ChangeEvent) => {
setSearchWord(event.target.value);
+ ReactGA.event('search-by-keyword-attendance-history-age', {
+ keyword: event.target.value,
+ });
if (event.target.value.length >= 3) {
debouncedSearch(event.target.value);
} else {
@@ -570,7 +577,16 @@ const UserAttendanceHistory = () => {
width={'100%'}
paddingTop={'10px'}
>
- window.history.back()}>
+ {
+ window.history.back();
+ logEvent({
+ action: 'back-button-clicked-attendance-history-page',
+ category: 'Attendance History Page',
+ label: 'Back Button Clicked',
+ });
+ }}
+ >
{
{displayStudentList.length >= 1 ? : null}
- {isError && }
);
};
diff --git a/src/pages/attendance-overview.tsx b/src/pages/attendance-overview.tsx
index 7d5ba9ec..83798480 100644
--- a/src/pages/attendance-overview.tsx
+++ b/src/pages/attendance-overview.tsx
@@ -15,6 +15,7 @@ import {
Typography,
} from '@mui/material';
import React, { useEffect, useRef, useState } from 'react';
+import ReactGA from 'react-ga4';
import {
classesMissedAttendancePercentList,
getAllCenterAttendance,
@@ -40,7 +41,6 @@ import OverviewCard from '@/components/OverviewCard';
import SearchIcon from '@mui/icons-material/Search';
import SortingModal from '@/components/SortingModal';
import StudentsStatsList from '@/components/LearnerAttendanceStatsListView';
-import ToastMessage from '@/components/ToastMessage';
import UpDownButton from '@/components/UpDownButton';
import { cohortList } from '@/services/CohortServices';
import { getMyCohortMemberList } from '@/services/MyClassDetailsService';
@@ -50,6 +50,8 @@ import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router';
import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'next-i18next';
+import { logEvent } from '@/utils/googleAnalytics';
+import { showToastMessage } from '@/components/Toastify';
interface AttendanceOverviewProps {
// buttonText: string;
@@ -71,9 +73,9 @@ const AttendanceOverview: React.FC = () => {
const [searchWord, setSearchWord] = React.useState('');
const [modalOpen, setModalOpen] = React.useState(false);
const [learnerData, setLearnerData] = React.useState>([]);
- const [isFromDate, setIsFromDate] = useState(formatSelectedDate(
- new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000)
- ));
+ const [isFromDate, setIsFromDate] = useState(
+ formatSelectedDate(new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000))
+ );
const [isToDate, setIsToDate] = useState(getTodayDate());
const [displayStudentList, setDisplayStudentList] = React.useState<
Array
@@ -89,7 +91,6 @@ const AttendanceOverview: React.FC = () => {
const [numberOfDaysAttendanceMarked, setNumberOfDaysAttendanceMarked] =
useState(0);
const [dateRange, setDateRange] = React.useState('');
- const [isError, setIsError] = React.useState(false);
const theme = useTheme();
const pathname = usePathname();
@@ -159,7 +160,7 @@ const AttendanceOverview: React.FC = () => {
contextId: classId,
},
facets: ['attendanceDate'],
- sort: ['present_percentage', 'asc']
+ sort: ['present_percentage', 'asc'],
};
const res = await getCohortAttendance(cohortAttendanceData);
const response = res?.data?.result?.attendanceDate;
@@ -172,9 +173,13 @@ const AttendanceOverview: React.FC = () => {
if (classId) {
getAttendanceMarkedDays();
}
- }, [classId, selectedValue === t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
- date_range: dateRange,
- })]);
+ }, [
+ classId,
+ selectedValue ===
+ t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
+ date_range: dateRange,
+ }),
+ ]);
// API call to get center list
useEffect(() => {
@@ -236,10 +241,9 @@ const AttendanceOverview: React.FC = () => {
}
}
setLoading(false);
- setIsError(false);
} catch (error) {
console.error('Error fetching cohort list:', error);
- setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
setLoading(false);
}
};
@@ -344,7 +348,7 @@ const AttendanceOverview: React.FC = () => {
contextId: classId,
},
facets: ['contextId'],
- sort: ['present_percentage', 'asc']
+ sort: ['present_percentage', 'asc'],
};
const res = await getCohortAttendance(cohortAttendanceData);
const response = res?.data?.result;
@@ -437,7 +441,7 @@ const AttendanceOverview: React.FC = () => {
}
} catch (error) {
console.error('Error fetching cohort list:', error);
- setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
setLoading(false);
} finally {
setLoading(false);
@@ -454,6 +458,9 @@ const AttendanceOverview: React.FC = () => {
const handleCohortSelection = (event: SelectChangeEvent) => {
setClassId(event.target.value as string);
+ ReactGA.event('cohort-selection-attendance-overview-page', {
+ selectedCohortID: event.target.value,
+ });
// ---------- set cohortId and stateName-----------
const cohort_id = event.target.value;
@@ -491,6 +498,9 @@ const AttendanceOverview: React.FC = () => {
setSearchWord(event.target.value);
if (event.target.value.length >= 1) {
debouncedSearch(event.target.value);
+ ReactGA.event('search-by-keyword-attendance-overview-page', {
+ keyword: event.target.value,
+ });
} else {
setDisplayStudentList(learnerData);
}
@@ -585,6 +595,11 @@ const AttendanceOverview: React.FC = () => {
};
const handleBackEvent = () => {
window.history.back();
+ logEvent({
+ action: 'back-button-clicked-attendance-overview',
+ category: 'Attendance Overview Page',
+ label: 'Back Button Clicked',
+ });
};
const truncate = (str: string, length: number) => {
if (str.length <= length) return str;
@@ -681,10 +696,10 @@ const AttendanceOverview: React.FC = () => {
onDateRangeSelected={handleDateRangeSelected}
dateRange={dateRange}
/>
- {(selectedValue ===
- t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
- date_range: dateRange,
- }) || selectedValue === "") ? (
+ {selectedValue ===
+ t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
+ date_range: dateRange,
+ }) || selectedValue === '' ? (
= () => {
/>
))}
valuePartOne={
- Array.isArray(lowAttendanceLearnerList) && lowAttendanceLearnerList.length > 2
+ Array.isArray(lowAttendanceLearnerList) &&
+ lowAttendanceLearnerList.length > 2
? `${lowAttendanceLearnerList[0]}, ${lowAttendanceLearnerList[1]}`
: lowAttendanceLearnerList.length === 2
? `${lowAttendanceLearnerList[0]}, ${lowAttendanceLearnerList[1]}`
@@ -731,7 +747,7 @@ const AttendanceOverview: React.FC = () => {
: t('ATTENDANCE.N/A')
}
valuePartTwo={
- Array.isArray(lowAttendanceLearnerList) &&
+ Array.isArray(lowAttendanceLearnerList) &&
lowAttendanceLearnerList.length > 2
? `${t('COMMON.AND')} ${lowAttendanceLearnerList.length - 2} ${t('COMMON.MORE')}`
: null
@@ -909,7 +925,6 @@ const AttendanceOverview: React.FC = () => {
{t('COMMON.NO_DATA_FOUND')}
)}
- {isError && }
);
};
diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx
index 7d6d5fc3..81d5fd45 100644
--- a/src/pages/dashboard.tsx
+++ b/src/pages/dashboard.tsx
@@ -1,11 +1,5 @@
'use client';
-import {
- AttendancePercentageProps,
- cohort,
- cohortAttendancePercentParam,
- cohortMemberList,
-} from '../utils/Interfaces';
import {
Box,
Button,
@@ -17,50 +11,52 @@ import {
Stack,
Typography,
} from '@mui/material';
+import React, { useEffect } from 'react';
import { CircularProgressbar, buildStyles } from 'react-circular-progressbar';
-import React, { useEffect, useState } from 'react';
+import ReactGA from 'react-ga4';
+import {
+ AttendancePercentageProps,
+ cohort,
+ cohortAttendancePercentParam,
+ cohortMemberList,
+} from '../utils/Interfaces';
// import Snackbar, { SnackbarOrigin } from '@mui/material/Snackbar';
+import { format, isAfter, isValid, parse, startOfDay } from 'date-fns';
import {
classesMissedAttendancePercentList,
getAllCenterAttendance,
getCohortAttendance,
} from '../services/AttendanceService';
-import { format, isAfter, isValid, parse, startOfDay } from 'date-fns';
import {
formatSelectedDate,
- getMonthName,
getTodayDate,
shortDateFormat,
toPascalCase,
} from '../utils/Helper';
-import ArrowForwardSharpIcon from '@mui/icons-material/ArrowForwardSharp';
-import CalendarMonthIcon from '@mui/icons-material/CalendarMonth';
-import Divider from '@mui/material/Divider';
-import Header from '../components/Header';
-import Image from 'next/image';
-import Link from 'next/link';
-import Loader from '../components/Loader';
import MarkBulkAttendance from '@/components/MarkBulkAttendance';
import OverviewCard from '@/components/OverviewCard';
-import ToastMessage from '@/components/ToastMessage';
+import { showToastMessage } from '@/components/Toastify';
import WeekCalender from '@/components/WeekCalender';
+import { getMyCohortMemberList } from '@/services/MyClassDetailsService';
import { calculatePercentage } from '@/utils/attendanceStats';
+import { logEvent } from '@/utils/googleAnalytics';
+import ArrowForwardSharpIcon from '@mui/icons-material/ArrowForwardSharp';
+import Divider from '@mui/material/Divider';
+import { useTheme } from '@mui/material/styles';
+import { useTranslation } from 'next-i18next';
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
+import Image from 'next/image';
+import Link from 'next/link';
+import { useRouter } from 'next/navigation';
+import { modifyAttendanceLimit } from '../../app.config';
import calendar from '../assets/images/calendar.svg';
+import Header from '../components/Header';
+import Loader from '../components/Loader';
+import useDeterminePathColor from '../hooks/useDeterminePathColor';
import { cohortList } from '../services/CohortServices';
-import { getMyCohortMemberList } from '@/services/MyClassDetailsService';
import { lowLearnerAttendanceLimit } from './../../app.config';
-import { modifyAttendanceLimit } from '../../app.config';
-import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
-import useDeterminePathColor from '../hooks/useDeterminePathColor';
-import { useRouter } from 'next/navigation';
-import { useTheme } from '@mui/material/styles';
-import { useTranslation } from 'next-i18next';
-
-// interface State extends SnackbarOrigin {
-// openModal: boolean;
-// }
-
+import GuideTour from '@/components/GuideTour';
interface DashboardProps {
// buttonText: string;
}
@@ -90,16 +86,8 @@ const Dashboard: React.FC = () => {
const [dateRange, setDateRange] = React.useState('');
const [allCenterAttendanceData, setAllCenterAttendanceData] =
React.useState(cohortsData);
- const [isError, setIsError] = React.useState(false);
- // const [state, setState] = React.useState({
- // openModal: false,
- // vertical: 'top',
- // horizontal: 'center',
- // });
- // const { vertical, horizontal, openModal } = state;
-
+ const [isClient, setIsClient] = React.useState(false);
const router = useRouter();
- const contextId = classId;
const theme = useTheme();
const determinePathColor = useDeterminePathColor();
const currentDate = new Date();
@@ -108,6 +96,7 @@ const Dashboard: React.FC = () => {
const formattedSevenDaysAgo = shortDateFormat(sevenDaysAgo);
useEffect(() => {
+ setIsClient(true);
const calculateDateRange = () => {
const endRangeDate = new Date();
endRangeDate.setHours(23, 59, 59, 999);
@@ -118,8 +107,8 @@ const Dashboard: React.FC = () => {
const startDayMonth = startRangeDate.toLocaleString('default', {
month: 'long',
});
- let endDay = endRangeDate.getDate();
- let endDayMonth = endRangeDate.toLocaleString('default', {
+ const endDay = endRangeDate.getDate();
+ const endDayMonth = endRangeDate.toLocaleString('default', {
month: 'long',
});
if (startDayMonth == endDayMonth) {
@@ -191,7 +180,7 @@ const Dashboard: React.FC = () => {
}
} catch (error) {
console.error('Error fetching cohort list:', error);
- setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
setLoading(false);
}
};
@@ -286,7 +275,7 @@ const Dashboard: React.FC = () => {
contextId: classId,
},
facets: ['contextId'],
- sort: ['present_percentage', 'asc']
+ sort: ['present_percentage', 'asc'],
};
const res = await getCohortAttendance(cohortAttendanceData);
const response = res?.data?.result;
@@ -326,14 +315,13 @@ const Dashboard: React.FC = () => {
filters,
facets,
});
- setIsError(false);
return { cohortId, data: response?.data?.result };
} catch (error) {
console.error(
`Error fetching data for cohortId ${cohortId}:`,
error
);
- setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
return { cohortId, error };
}
});
@@ -370,11 +358,10 @@ const Dashboard: React.FC = () => {
.filter((item) => item.presentPercentage !== null); // Filter out items with no valid percentage
// console.log('Filtered and merged data:', nameIDAttendanceArray);
- setIsError(false);
setAllCenterAttendanceData(nameIDAttendanceArray);
} catch (error) {
console.error('Error fetching attendance data:', error);
- setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
}
};
@@ -398,10 +385,20 @@ const Dashboard: React.FC = () => {
setShowDetails(true);
};
- const handleModalToggle = () => setOpen(!open);
+ const handleModalToggle = () => {
+ setOpen(!open);
+ logEvent({
+ action: 'mark/modify-attendance-button-clicked-dashboard',
+ category: 'Dashboard Page',
+ label: 'Mark/ Modify Attendance',
+ });
+ };
const handleCohortSelection = (event: SelectChangeEvent) => {
setClassId(event.target.value as string);
+ ReactGA.event('cohort-selection-dashboard', {
+ selectedCohortID: event.target.value,
+ });
localStorage.setItem('classId', event.target.value);
setHandleSaveHasRun(!handleSaveHasRun);
};
@@ -470,6 +467,7 @@ const Dashboard: React.FC = () => {
const viewAttendanceHistory = () => {
if (classId !== 'all') {
router.push('/attendance-history');
+ ReactGA.event('month-name-clicked', { selectedCohortID: classId });
}
};
@@ -494,447 +492,488 @@ const Dashboard: React.FC = () => {
}
}
const presentPercentage = parseFloat(currentAttendance?.present_percentage);
-
const pathColor = determinePathColor(presentPercentage);
- const truncate = (str: string, length: number) => {
- if (str.length <= length) return str;
- return str.slice(0, length) + '...';
+ const handleMoreDetailsClicked = () => {
+ logEvent({
+ action: 'more-details-button-clicked',
+ category: 'Dashboard Page',
+ label: 'More Details Link Clicked',
+ });
};
-
+ let hasSeenTutorial = false;
+ if (typeof window !== 'undefined' && window.localStorage) {
+ const storedValue = localStorage.getItem('hasSeenTutorial');
+ if (storedValue !== null) {
+ hasSeenTutorial = storedValue === 'true'; // Convert string 'true' or 'false' to boolean
+ }
+ }
return (
<>
- {!isAuthenticated && (
-
- )}
+ {isClient && (
+ <>
+
+ <>
+ {!isAuthenticated && (
+
+ )}
- {isAuthenticated && (
-
-
-
-
-
- {t('DASHBOARD.DASHBOARD')}
-
-
-
- {loading && (
-
- )}
-
-
-
-
-
- {t('DASHBOARD.DAY_WISE_ATTENDANCE')}
-
+ {isAuthenticated && (
+
+
+
- {getMonthName(selectedDate)}
+ {t('DASHBOARD.DASHBOARD')}
- {/* */}
-
-
-
- {cohortsData?.length > 1 ? (
-
-
+ )}
+
+
+
+
+
+ {t('DASHBOARD.DAY_WISE_ATTENDANCE')}
+
+
- {cohortsData?.length !== 0 ? (
- manipulatedCohortData?.map((cohort) => (
-
+
+
+
+ {cohortsData?.length > 1 ? (
+
+
+
) : (
-
- {t('COMMON.NO_DATA_FOUND')}
+
+ {cohortsData[0]?.name}
)}
-
-
- ) : (
-
- {cohortsData[0]?.name}
-
- )}
-
-
- {/* TODO: Write logic to disable this block on all select */}
-
-
-
-
-
-
-
- {currentAttendance !== 'notMarked' &&
- currentAttendance !== 'futureDate' && (
- <>
-
-
-
- {t('DASHBOARD.PERCENT_ATTENDANCE', {
- percent_students:
- currentAttendance?.present_percentage,
- })}
-
+
+
+ {/* TODO: Write logic to disable this block on all select */}
+
+
+
+
+
+
+
+ {currentAttendance !== 'notMarked' &&
+ currentAttendance !== 'futureDate' && (
+ <>
+
+
+
+ {t('DASHBOARD.PERCENT_ATTENDANCE', {
+ percent_students:
+ currentAttendance?.present_percentage,
+ })}
+
+
+ {t('DASHBOARD.PRESENT_STUDENTS', {
+ present_students:
+ currentAttendance?.present_students,
+ total_students:
+ currentAttendance?.totalcount,
+ })}
+
+
+ >
+ )}
+ {currentAttendance === 'notMarked' &&
+ currentAttendance !== 'futureDate' && (
+
+ {t('DASHBOARD.NOT_MARKED')}
+
+ )}
+ {currentAttendance === 'futureDate' && (
- {t('DASHBOARD.PRESENT_STUDENTS', {
- present_students:
- currentAttendance?.present_students,
- total_students:
- currentAttendance?.totalcount,
- })}
+ {t('DASHBOARD.FUTURE_DATE_CANT_MARK')}
-
- >
- )}
- {currentAttendance === 'notMarked' &&
- currentAttendance !== 'futureDate' && (
-
+
- )}
- {currentAttendance === 'futureDate' && (
-
- {t('DASHBOARD.FUTURE_DATE_CANT_MARK')}
-
+ {currentAttendance === 'notMarked' ||
+ currentAttendance === 'futureDate'
+ ? t('COMMON.MARK')
+ : t('COMMON.MODIFY')}
+
+
+
+ {open && (
+ {
+ if (isModified) {
+ showToastMessage(
+ t(
+ 'ATTENDANCE.ATTENDANCE_MODIFIED_SUCCESSFULLY'
+ ),
+ 'success'
+ );
+ } else {
+ showToastMessage(
+ t(
+ 'ATTENDANCE.ATTENDANCE_MARKED_SUCCESSFULLY'
+ ),
+ 'success'
+ );
+ }
+ setHandleSaveHasRun(!handleSaveHasRun);
+ }}
+ />
)}
-
-
-
- {open && (
-
- setHandleSaveHasRun(!handleSaveHasRun)
- }
- />
- )}
-
- {/* */}
-
-
-
-
+
+
+
+
- {/* Overview Card Section */}
-
-
-
+ {/* Overview Card Section */}
-
- {t('DASHBOARD.OVERVIEW')}
-
-
-
- {t('DASHBOARD.MORE_DETAILS')}
-
-
-
+
+
+
+ {t('DASHBOARD.OVERVIEW')}
+
+
+
+ {t('DASHBOARD.MORE_DETAILS')}
+
+
+
+
+
+ {t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
+ date_range: dateRange,
+ })}
+
+
+
+ {loading && (
+
+ )}
-
- {t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
- date_range: dateRange,
- })}
-
-
-
- {loading && (
-
- )}
-
-
- {classId &&
- classId !== 'all' &&
- cohortsData &&
- lowAttendanceLearnerList ? (
-
-
-
-
-
-
- ))}
- valuePartOne={
- Array.isArray(lowAttendanceLearnerList) &&
- lowAttendanceLearnerList.length > 2
- ? `${lowAttendanceLearnerList[0]}, ${lowAttendanceLearnerList[1]}`
- : lowAttendanceLearnerList.length === 2
- ? `${lowAttendanceLearnerList[0]}, ${lowAttendanceLearnerList[1]}`
- : lowAttendanceLearnerList.length === 1
- ? `${lowAttendanceLearnerList[0]}`
- : Array.isArray(lowAttendanceLearnerList) &&
- lowAttendanceLearnerList.length === 0
- ? t(
- 'ATTENDANCE.NO_LEARNER_WITH_LOW_ATTENDANCE'
- )
- : t('ATTENDANCE.N/A')
- }
- valuePartTwo={
- Array.isArray(lowAttendanceLearnerList) &&
- lowAttendanceLearnerList.length > 2
- ? `${t('COMMON.AND')} ${lowAttendanceLearnerList.length - 2} ${t('COMMON.MORE')}`
- : null
- }
- />
-
-
- ) : (
-
- {allCenterAttendanceData.map(
- (item: {
- cohortId: React.Key | null | undefined;
- name: string;
- presentPercentage: number;
- }) => (
-
-
+
+ {classId &&
+ classId !== 'all' &&
+ cohortsData &&
+ lowAttendanceLearnerList ? (
+
+
+
+
+
+
+ ))}
+ valuePartOne={
+ Array.isArray(lowAttendanceLearnerList) &&
+ lowAttendanceLearnerList.length > 2
+ ? `${lowAttendanceLearnerList[0]}, ${lowAttendanceLearnerList[1]}`
+ : lowAttendanceLearnerList.length === 2
+ ? `${lowAttendanceLearnerList[0]}, ${lowAttendanceLearnerList[1]}`
+ : lowAttendanceLearnerList.length === 1
+ ? `${lowAttendanceLearnerList[0]}`
+ : Array.isArray(
+ lowAttendanceLearnerList
+ ) &&
+ lowAttendanceLearnerList.length === 0
+ ? t(
+ 'ATTENDANCE.NO_LEARNER_WITH_LOW_ATTENDANCE'
+ )
+ : t('ATTENDANCE.N/A')
+ }
+ valuePartTwo={
+ Array.isArray(lowAttendanceLearnerList) &&
+ lowAttendanceLearnerList.length > 2
+ ? `${t('COMMON.AND')} ${lowAttendanceLearnerList.length - 2} ${t('COMMON.MORE')}`
+ : null
+ }
+ />
+
- )
- )}
-
- )}
-
-
-
- {isError && (
-
- )}
- {/*
+ ) : (
+
+ {allCenterAttendanceData.map(
+ (item: {
+ cohortId: React.Key | null | undefined;
+ name: string;
+ presentPercentage: number;
+ }) => (
+
+
+
+ )
+ )}
+
+ )}
+
+
+
+
+ {/*
= () => {
/>
*/}
-
+
+ )}
+ >
+ >
)}
>
);
diff --git a/src/pages/learner-attendance-history.tsx b/src/pages/learner-attendance-history.tsx
index 2abc0469..3872d018 100644
--- a/src/pages/learner-attendance-history.tsx
+++ b/src/pages/learner-attendance-history.tsx
@@ -14,7 +14,8 @@ import { shortDateFormat } from '@/utils/Helper';
import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
-import ToastMessage from '@/components/ToastMessage';
+import { logEvent } from '@/utils/googleAnalytics';
+import { showToastMessage } from '@/components/Toastify';
type LearnerAttendanceData = {
[date: string]: {
@@ -41,7 +42,6 @@ const LearnerAttendanceHistory = () => {
const [learnerAttendance, setLearnerAttendance] = useState<
LearnerAttendanceData | undefined
>(undefined);
- const [isError, setIsError] = React.useState
(false);
useEffect(() => {
if (typeof window !== 'undefined' && window.localStorage) {
@@ -69,6 +69,11 @@ const LearnerAttendanceHistory = () => {
const handleOpen = () => {
setOpen(true);
+ logEvent({
+ action: 'individual-learner-attendance-modal-open',
+ category: 'Learner Attendance History Page',
+ label: 'Mark Individual Learner Modal Open',
+ });
};
const handleModalOpen = () => {
@@ -77,6 +82,11 @@ const LearnerAttendanceHistory = () => {
const handleModalClose = () => {
setOpen(false);
+ logEvent({
+ action: 'individual-learner-attendance-modal-close',
+ category: 'Learner Attendance History Page',
+ label: 'Mark Individual Learner Modal Close',
+ });
};
useEffect(() => {
@@ -129,11 +139,10 @@ const LearnerAttendanceHistory = () => {
{}
);
setLearnerAttendance(attendanceData);
- setIsError(false)
setLoading(false);
}
} catch (err) {
- setIsError(true)
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
setLoading(false);
} finally {
setLoading(false);
@@ -229,9 +238,6 @@ const LearnerAttendanceHistory = () => {
handleClose={handleModalClose}
onAttendanceUpdate={() => setAttendanceUpdated(!attendanceUpdated)}
/>
- { isError &&
-
- }
);
};
diff --git a/src/pages/learner/[userId].tsx b/src/pages/learner/[userId].tsx
index 147948e3..6c63bc02 100644
--- a/src/pages/learner/[userId].tsx
+++ b/src/pages/learner/[userId].tsx
@@ -16,6 +16,7 @@ import {
FormControl,
FormControlLabel,
FormGroup,
+ FormHelperText,
Grid,
IconButton,
InputLabel,
@@ -37,6 +38,7 @@ import {
} from '@/utils/Interfaces';
import Menu, { MenuProps } from '@mui/material/Menu';
import React, { useEffect, useState } from 'react';
+import ReactGA from 'react-ga4';
import { alpha, styled, useTheme } from '@mui/material/styles';
import {
classesMissedAttendancePercentList,
@@ -61,6 +63,8 @@ import { format } from 'date-fns';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
+import { logEvent } from '@/utils/googleAnalytics';
+import { showToastMessage } from '@/components/Toastify';
// import { UserData, updateCustomField } from '../utils/Interfaces';
@@ -111,14 +115,14 @@ const LearnerProfile: React.FC = () => {
present_percentage: any;
// Add other properties as needed
}
- const [isFromDate, setIsFromDate] = useState(formatSelectedDate(
- new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000)
- ));
+ const [isFromDate, setIsFromDate] = useState(
+ formatSelectedDate(new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000))
+ );
const [isToDate, setIsToDate] = useState(getTodayDate());
const [submittedOn, setSubmitedOn] = useState();
const [overallAttendance, setOverallAttendance] =
useState();
- const [currentDayMonth, setCurrentDayMonth] = React.useState('');
+ const [currentDayMonth, setCurrentDayMonth] = React.useState('');
const [selectedValue, setSelectedValue] = React.useState('');
const [numberOfDaysAttendanceMarked, setNumberOfDaysAttendanceMarked] =
useState(0);
@@ -129,7 +133,9 @@ const LearnerProfile: React.FC = () => {
const [totalScore, setTotalScore] = useState('');
const [errors, setErrors] = useState<{ [key: string]: boolean }>({});
const [hasErrors, setHasErrors] = useState(false);
-
+ const [hasInputChanged, setHasInputChanged] = React.useState(false);
+ const [isValidationTriggered, setIsValidationTriggered] =
+ React.useState(false);
const [unitName, setUnitName] = useState('');
const [blockName, setBlockName] = useState('');
const [uniqueDoId, setUniqueDoId] = useState('');
@@ -280,6 +286,7 @@ const LearnerProfile: React.FC = () => {
setIsError(false);
} catch (error) {
setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
setLoading(false);
console.error('Error fetching user details:', error);
}
@@ -326,12 +333,16 @@ const LearnerProfile: React.FC = () => {
const handleChangeTest = (event: SelectChangeEvent) => {
const test = event.target.value;
setTest(test);
+ ReactGA.event('pre-post-test-selected', { testTypeSelected: test });
getDoIdForAssesmentReport(test, subject);
};
const handleChangeSubject = (event: SelectChangeEvent) => {
const subject = event.target.value;
setSubject(event.target.value);
+ ReactGA.event('select-subject-learner-details-page', {
+ subjectSelected: subject,
+ });
getDoIdForAssesmentReport(test, subject);
};
@@ -370,6 +381,7 @@ const LearnerProfile: React.FC = () => {
setIsError(false);
} catch (error) {
setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
console.error(
'Error fetching getDoIdForAssesmentDetails results:',
error
@@ -494,8 +506,26 @@ const LearnerProfile: React.FC = () => {
const [contactNumber, setContactNumber] = useState(null);
const [openEdit, setOpenEdit] = React.useState(false);
const [loading, setLoading] = useState(false);
- const handleOpen = () => setOpenEdit(true);
- const handleClose = () => setOpenEdit(false);
+ const handleOpen = () => {
+ setOpenEdit(true);
+ logEvent({
+ action: 'edit-learner-profile-modal-open',
+ category: 'Learner Detail Page',
+ label: 'Edit Learner Profile Modal Open',
+ });
+ };
+ const handleClose = () => {
+ logEvent({
+ action: 'edit-learner-profile-modal-close',
+ category: 'Learner Detail Page',
+ label: 'Edit Learner Profile Modal Close',
+ });
+ setOpenEdit(false);
+ initialFormData();
+ setHasInputChanged(false);
+ setHasErrors(false);
+ setErrors({});
+ };
const style = {
position: 'absolute',
top: '50%',
@@ -524,7 +554,7 @@ const LearnerProfile: React.FC = () => {
})),
});
- useEffect(() => {
+ const initialFormData = () => {
setFormData({
userData: {
name: userName || '',
@@ -535,15 +565,26 @@ const LearnerProfile: React.FC = () => {
value: field.value,
})),
});
+ };
+
+ useEffect(() => {
+ initialFormData();
}, [userData, customFieldsData]);
const handleFieldChange = (fieldId: string, value: string) => {
+ const sanitizedValue = value.replace(/^\s+/, '').replace(/\s+/g, ' ');
+
setFormData((prevState) => ({
...prevState,
customFields: prevState.customFields.map((field) =>
- field.fieldId === fieldId ? { ...field, value: [value] } : field
+ field.fieldId === fieldId
+ ? { ...field, value: [sanitizedValue] }
+ : field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleCheckboxChange = (
@@ -566,6 +607,9 @@ const LearnerProfile: React.FC = () => {
: field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleDropdownChange = (fieldId: string, value: string) => {
@@ -575,6 +619,9 @@ const LearnerProfile: React.FC = () => {
field.fieldId === fieldId ? { ...field, value: [value] } : field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleRadioChange = (fieldId: string, value: string) => {
@@ -584,12 +631,20 @@ const LearnerProfile: React.FC = () => {
field.fieldId === fieldId ? { ...field, value: [value] } : field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleSubmit = async (
e: React.MouseEvent
) => {
e.preventDefault();
+ logEvent({
+ action: 'save-button-clicked-edit-learner-profile',
+ category: 'Learner Detail Page',
+ label: 'Learner Profile Save Button Clicked',
+ });
setLoading(true);
const user_id = userId;
const data = {
@@ -608,8 +663,10 @@ const LearnerProfile: React.FC = () => {
try {
if (userId) {
const response = await editEditUser(user_id, userDetails);
+ ReactGA.event('edit-learner-profile-successful', { userId: userId });
if (response.responseCode !== 200 || response.params.err) {
+ ReactGA.event('edit-learner-profile-failed', { userId: userId });
throw new Error(
response.params.errmsg ||
'An error occurred while updating the user.'
@@ -625,6 +682,7 @@ const LearnerProfile: React.FC = () => {
}
} catch (error) {
setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
console.error('Error:', error);
}
};
@@ -695,7 +753,7 @@ const LearnerProfile: React.FC = () => {
contextId: classId,
},
facets: ['attendanceDate'],
- sort: ['present_percentage', 'asc']
+ sort: ['present_percentage', 'asc'],
};
const res = await getCohortAttendance(cohortAttendanceData);
const response = res?.data?.result?.attendanceDate;
@@ -708,9 +766,13 @@ const LearnerProfile: React.FC = () => {
if (classId) {
getAttendanceMarkedDays();
}
- }, [classId, selectedValue === t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
- date_range: dateRange,
- })]);
+ }, [
+ classId,
+ selectedValue ===
+ t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
+ date_range: dateRange,
+ }),
+ ]);
//-------------validation for edit fields ---------------------------
@@ -718,16 +780,17 @@ const LearnerProfile: React.FC = () => {
const newErrors: { [key: string]: boolean } = {};
const fieldsToValidate = [...customFieldsData];
-
- learnerDetailsByOrder.forEach((field) => {
+ filteredSortedForEdit?.forEach((field) => {
const value =
formData?.customFields?.find((f) => f.fieldId === field.fieldId)
?.value[0] || '';
if (field.type === 'text') {
- newErrors[field.fieldId] = !value.trim();
+ newErrors[field.fieldId] = !value.trim() || /^\s/.test(value);
} else if (field.type === 'numeric') {
newErrors[field.fieldId] = !/^\d{1,4}$/.test(value);
+ } else if (field.type === 'drop_down') {
+ newErrors[field.fieldId] = !value.trim() || value === '';
}
});
@@ -739,7 +802,10 @@ const LearnerProfile: React.FC = () => {
};
const handleInputChange = (e: React.ChangeEvent) => {
const { value } = e.target;
- const sanitizedValue = value.replace(/[^a-zA-Z_ ]/g, '');
+ const sanitizedValue = value
+ .replace(/[^a-zA-Z_ ]/g, '')
+ .replace(/^\s+/, '')
+ .replace(/\s+/g, ' ');
setFormData((prevData) => ({
...prevData,
@@ -750,11 +816,15 @@ const LearnerProfile: React.FC = () => {
}));
// setHasErrors(!sanitizedValue.trim());
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
validateFields();
};
useEffect(() => {
- validateFields();
+ if (hasInputChanged) {
+ validateFields();
+ }
}, [formData, customFieldsData]);
// flag for contactNumberAdded in dynamic list fo fields in lerner basic details
@@ -768,7 +838,16 @@ const LearnerProfile: React.FC = () => {
- window.history.back()}>
+ {
+ window.history.back();
+ logEvent({
+ action: 'back-button-clicked-learner-detail-page',
+ category: 'Learner Detail Page',
+ label: 'Back Button Clicked',
+ });
+ }}
+ >
{
>
Attendance Marked : 3 out of last 7 days
*/}
- {(selectedValue ===
- t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
- date_range: dateRange,
- }) || selectedValue === "")? (
+ {selectedValue ===
+ t('DASHBOARD.LAST_SEVEN_DAYS_RANGE', {
+ date_range: dateRange,
+ }) || selectedValue === '' ? (
{
onChange={handleChangeSubject}
>
-
+
@@ -1317,7 +1396,7 @@ const LearnerProfile: React.FC = () => {
const fieldValue =
formData?.customFields?.find((f) => f.fieldId === field.fieldId)
?.value[0] || '';
- const isError = errors[field.fieldId];
+ const isError: any = errors[field.fieldId];
return (
@@ -1359,6 +1438,20 @@ const LearnerProfile: React.FC = () => {
}
variant="outlined"
value={fieldValue}
+ onKeyDown={(e) => {
+ // Allow only numeric keys, Backspace, and Delete
+ if (
+ !(
+ (
+ /[0-9]/.test(e.key) ||
+ e.key === 'Backspace' ||
+ e.key === 'Delete'
+ ) // Allow decimal point if needed
+ )
+ ) {
+ e.preventDefault();
+ }
+ }}
onChange={(e) => {
const inputValue = e.target.value;
if (/^\d{0,4}$/.test(inputValue)) {
@@ -1420,6 +1513,7 @@ const LearnerProfile: React.FC = () => {
: field.label}
+ {isError && (
+
+ {t('PROFILE.SELECT_OPTION')}
+
+ )}
) : field.type === 'radio' || field.type === 'Radio' ? (
@@ -1501,14 +1602,13 @@ const LearnerProfile: React.FC = () => {
}}
onClick={handleSubmit}
variant="contained"
- disabled={hasErrors}
+ disabled={!hasInputChanged || !isValidationTriggered || hasErrors}
>
{t('COMMON.SAVE')}
- {isError && }
>
);
};
diff --git a/src/pages/login.tsx b/src/pages/login.tsx
index f0684860..58ce9153 100644
--- a/src/pages/login.tsx
+++ b/src/pages/login.tsx
@@ -8,16 +8,13 @@ import {
} from '@mui/material';
import React, { useEffect, useRef, useState } from 'react';
import Select, { SelectChangeEvent } from '@mui/material/Select';
-import Snackbar, { SnackbarOrigin } from '@mui/material/Snackbar';
import { Visibility, VisibilityOff } from '@mui/icons-material';
+import ReactGA from 'react-ga4';
import Checkbox from '@mui/material/Checkbox';
-import CloseIcon from '@mui/icons-material/Close';
import Image from 'next/image';
-import Link from '@mui/material/Link';
import Loader from '../components/Loader';
import MenuItem from '@mui/material/MenuItem';
-import ToastMessage from '@/components/ToastMessage';
import appLogo from '../../public/images/appLogo.png';
import config from '../../config.json';
import { getUserId } from '../services/ProfileService';
@@ -26,10 +23,9 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useRouter } from 'next/router';
import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'next-i18next';
-
-interface State extends SnackbarOrigin {
- openModal: boolean;
-}
+import { telemetryFactory } from '@/utils/telemetry';
+import { logEvent } from '@/utils/googleAnalytics';
+import { showToastMessage } from '@/components/Toastify';
const LoginPage = () => {
const { t } = useTranslation();
@@ -37,7 +33,6 @@ const LoginPage = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [rememberMe, setRememberMe] = useState(false);
- const [showToastMessage, setShowToastMessage] = useState(false);
const [usernameError, setUsernameError] = useState(false);
const [passwordError, setPasswordError] = useState(false);
const [loading, setLoading] = useState(false);
@@ -52,16 +47,6 @@ const LoginPage = () => {
const passwordRef = useRef(null);
const loginButtonRef = useRef(null);
- const DEFAULT_POSITION: Pick = {
- vertical: 'bottom',
- horizontal: 'center',
- };
- const [state, setState] = React.useState({
- openModal: false,
- ...DEFAULT_POSITION,
- });
- const { vertical, horizontal, openModal } = state;
-
useEffect(() => {
if (typeof window !== 'undefined' && window.localStorage) {
if (localStorage.getItem('preferredLanguage')) {
@@ -91,7 +76,14 @@ const LoginPage = () => {
setPassword(value);
};
- const handleClickShowPassword = () => setShowPassword((show) => !show);
+ const handleClickShowPassword = () => {
+ setShowPassword((show) => !show);
+ logEvent({
+ action: 'show-password-icon-clicked',
+ category: 'Login Page',
+ label: 'Show Password',
+ });
+ };
const handleMouseDownPassword = (
event: React.MouseEvent
@@ -101,6 +93,11 @@ const LoginPage = () => {
const handleFormSubmit = async (event: React.FormEvent) => {
event.preventDefault();
+ logEvent({
+ action: 'login-button-clicked',
+ category: 'Login Page',
+ label: 'Login Button Clicked',
+ });
if (!usernameError && !passwordError) {
setLoading(true);
try {
@@ -109,13 +106,6 @@ const LoginPage = () => {
password: password,
});
if (response) {
- setTimeout(() => {
- setState({
- openModal: false,
- ...DEFAULT_POSITION,
- });
- });
-
if (typeof window !== 'undefined' && window.localStorage) {
const token = response?.result?.access_token;
const refreshToken = response?.result?.refresh_token;
@@ -129,16 +119,34 @@ const LoginPage = () => {
}
}
setLoading(false);
- setShowToastMessage(false);
+ const telemetryInteract = {
+ context: {
+ env: 'sign-in',
+ cdata: [],
+ },
+ edata: {
+ id: 'login-success',
+ type: 'CLICK',
+ subtype: '',
+ pageid: 'sign-in',
+ uid: localStorage.getItem('userId') || 'Anonymous',
+ },
+ };
+ telemetryFactory.interact(telemetryInteract);
router.push('/dashboard');
} catch (error: any) {
setLoading(false);
if (error.response && error.response.status === 404) {
- handleClick({ ...DEFAULT_POSITION })();
- setShowToastMessage(true);
+ showToastMessage(
+ t('LOGIN_PAGE.USERNAME_PASSWORD_NOT_CORRECT'),
+ 'error'
+ );
} else {
console.error('Error:', error);
- setShowToastMessage(true);
+ showToastMessage(
+ t('LOGIN_PAGE.USERNAME_PASSWORD_NOT_CORRECT'),
+ 'error'
+ );
}
}
}
@@ -152,18 +160,13 @@ const LoginPage = () => {
if (typeof window !== 'undefined' && window.localStorage) {
localStorage.setItem('preferredLanguage', newLocale);
setLanguage(event.target.value);
+ ReactGA.event('select-language-login-page', {
+ selectedLanguage: event.target.value,
+ });
router.push('/login', undefined, { locale: newLocale });
}
};
- const handleClick = (newState: SnackbarOrigin) => () => {
- setState({ ...newState, openModal: true });
- };
-
- const handleClose = () => {
- setState({ ...state, openModal: false });
- };
-
useEffect(() => {
const handlePasswordFocus = () => {
if (loginButtonRef.current) {
@@ -184,6 +187,14 @@ const LoginPage = () => {
}
}, []);
+ const handleForgotPasswordClick = () => {
+ logEvent({
+ action: 'forgot-password-link-clicked',
+ category: 'Login Page',
+ label: 'Forgot Password Link Clicked',
+ });
+ };
+
return (
- {/*
-
- {t('LOGIN_PAGE.FORGOT_PASSWORD')}
-
- */}
+ {
+ //
+ //
+ // {t('LOGIN_PAGE.FORGOT_PASSWORD')}
+ //
+ //
+ }
setRememberMe(e.target.checked)}
@@ -331,7 +345,14 @@ const LoginPage = () => {
color: theme.palette.warning['300'],
}}
className="fw-400"
- onClick={() => setRememberMe(!rememberMe)}
+ onClick={() => {
+ setRememberMe(!rememberMe);
+ logEvent({
+ action: 'remember-me-button-clicked',
+ category: 'Login Page',
+ label: `Remember Me ${rememberMe ? 'Checked' : 'Unchecked'}`,
+ });
+ }}
>
{t('LOGIN_PAGE.REMEMBER_ME')}
@@ -356,11 +377,6 @@ const LoginPage = () => {
- {showToastMessage && (
-
- )}
);
diff --git a/src/pages/logout.tsx b/src/pages/logout.tsx
index 0a67cb6f..3930a111 100644
--- a/src/pages/logout.tsx
+++ b/src/pages/logout.tsx
@@ -1,11 +1,37 @@
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { logout } from '../services/LoginService';
+import { telemetryFactory } from '@/utils/telemetry';
function Logout() {
const router = useRouter();
useEffect(() => {
const userLogout = async () => {
+ const telemetryInteract = {
+ context: {
+ env: 'sign-out',
+ cdata: [],
+ },
+ edata: {
+ id: 'logout-success',
+ type: 'CLICK',
+ subtype: '',
+ pageid: 'sign-out',
+ uid: 'id',
+
+ studentid: localStorage.getItem('userId'),
+
+ userName: 'userName',
+
+ grade: 'grade',
+
+ medium: 'medium',
+
+ board: 'board',
+ },
+ };
+ telemetryFactory.interact(telemetryInteract);
+
try {
const refreshToken = localStorage.getItem('refreshToken');
if (refreshToken) {
diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx
index 3e77197e..ba5408c3 100644
--- a/src/pages/profile.tsx
+++ b/src/pages/profile.tsx
@@ -5,6 +5,7 @@ import {
FormControl,
FormControlLabel,
FormGroup,
+ FormHelperText,
Grid,
IconButton,
InputLabel,
@@ -19,6 +20,7 @@ import {
} from '@mui/material';
import { CustomField, UserDatas, updateCustomField } from '@/utils/Interfaces';
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
+import ReactGA from 'react-ga4';
import { editEditUser, getUserDetails } from '@/services/ProfileService';
import { useTheme, withStyles } from '@mui/material/styles';
@@ -31,13 +33,14 @@ import Image from 'next/image';
import Loader from '@/components/Loader';
import Modal from '@mui/material/Modal';
import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined';
-import ToastMessage from '@/components/ToastMessage';
import { getLabelForValue } from '@/utils/Helper';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import userPicture from '@/assets/images/imageOne.jpg';
import user_placeholder from '../assets/images/user_placeholder.png';
+import { logEvent } from '@/utils/googleAnalytics';
+import { showToastMessage } from '@/components/Toastify';
interface FieldOption {
name: string;
@@ -63,8 +66,28 @@ const TeacherProfile = () => {
const router = useRouter();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
- const handleOpen = () => setOpen(true);
- const handleClose = () => setOpen(false);
+ const handleOpen = () => {
+ setOpen(true);
+ logEvent({
+ action: 'edit-teacher-profile-modal-open',
+ category: 'Profile Page',
+ label: 'Edit Teacher Profile Modal Open',
+ });
+ };
+ const [errors, setErrors] = useState<{ [key: string]: boolean }>({});
+
+ const handleClose = () => {
+ setOpen(false);
+ initialFormData();
+ setHasInputChanged(false);
+ setHasErrors(false);
+ setErrors({});
+ logEvent({
+ action: 'edit-teacher-profile-modal-close',
+ category: 'Profile Page',
+ label: 'Edit Teacher Profile Modal Close',
+ });
+ };
const [userData, setUserData] = useState(null);
const [userName, setUserName] = useState(null);
const [updatedCustomFields, setUpdatedCustomFields] = useState([]);
@@ -81,6 +104,10 @@ const TeacherProfile = () => {
const [blockName, setBlockName] = useState('');
const [radioValues, setRadioValues] = useState([]);
const [isError, setIsError] = React.useState(false);
+ const [isData, setIsData] = React.useState(false);
+ const [hasInputChanged, setHasInputChanged] = React.useState(false);
+ const [isValidationTriggered, setIsValidationTriggered] =
+ React.useState(false);
const handleNameFieldChange = (event: ChangeEvent) => {
const { value } = event.target;
@@ -126,15 +153,16 @@ const TeacherProfile = () => {
try {
if (userId) {
const response = await getUserDetails(userId, true);
+ console.log('response', response);
const data = response?.result;
if (data) {
const userData = data?.userData;
-
setUserData(userData);
setUserName(userData?.name);
const customDataFields = userData?.customFields;
+ setIsData(true);
if (customDataFields?.length > 0) {
setCustomFieldsData(customDataFields);
@@ -145,6 +173,8 @@ const TeacherProfile = () => {
setLoading(false);
}
} else {
+ setLoading(false);
+ setIsData(false);
console.log('No data Found');
}
}
@@ -152,6 +182,7 @@ const TeacherProfile = () => {
} catch (error) {
setLoading(false);
setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
console.error('Error fetching user details:', error);
}
}
@@ -211,11 +242,6 @@ const TeacherProfile = () => {
?.filter((field) => field.order !== 0 && field.name !== 'main_subject')
?.sort((a, b) => a.order - b.order);
- //fields for edit popup by order
- const filteredSortedForEdit = [...customFieldsData]
- ?.filter((field) => field.order !== 0 && field.isEditable)
- ?.sort((a, b) => a.order - b.order);
-
// fields for showing in basic details
const getLabelForValue = (field: any, value: string) => {
if (
@@ -251,24 +277,31 @@ const TeacherProfile = () => {
})),
});
- useEffect(() => {
+ const initialFormData = () => {
setFormData({
userData: {
name: userName || '',
},
- customFields: customFieldsData.map((field) => ({
+ customFields: customFieldsData?.map((field) => ({
fieldId: field.fieldId,
type: field.type,
value: field.value ? field.value : '',
})),
});
+ };
+
+ useEffect(() => {
+ initialFormData();
}, [userData, customFieldsData]);
const [hasErrors, setHasErrors] = useState(false);
const handleInputChange = (e: React.ChangeEvent) => {
const { value } = e.target;
- const sanitizedValue = value.replace(/[^a-zA-Z_ ]/g, '');
+ const sanitizedValue = value
+ .replace(/[^a-zA-Z_ ]/g, '')
+ .replace(/^\s+/, '')
+ .replace(/\s+/g, ' ');
setFormData((prevData) => ({
...prevData,
@@ -277,16 +310,21 @@ const TeacherProfile = () => {
name: sanitizedValue,
},
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
validateFields();
// setHasErrors(!sanitizedValue.trim());
};
- const [errors, setErrors] = useState<{ [key: string]: boolean }>({});
+ //fields for edit popup by order
+ const filteredSortedForEdit = [...customFieldsData]
+ ?.filter((field) => field.isEditable)
+ ?.sort((a, b) => a.order - b.order);
const validateFields = () => {
const newErrors: { [key: string]: boolean } = {};
- customFieldsData.forEach((field) => {
+ filteredSortedForEdit?.forEach((field) => {
const value =
formData?.customFields?.find((f) => f.fieldId === field.fieldId)
?.value[0] || '';
@@ -295,6 +333,8 @@ const TeacherProfile = () => {
newErrors[field.fieldId] = !value.trim();
} else if (field.type === 'numeric') {
newErrors[field.fieldId] = !/^\d{1,4}$/.test(value);
+ } else if (field.type === 'dropdown' || field.type === 'drop_down') {
+ newErrors[field.fieldId] = !value.trim();
}
});
@@ -305,16 +345,25 @@ const TeacherProfile = () => {
};
useEffect(() => {
- validateFields();
+ if (hasInputChanged) {
+ validateFields();
+ }
}, [formData, customFieldsData]);
const handleFieldChange = (fieldId: string, value: string) => {
+ const sanitizedValue = value.replace(/^\s+/, '').replace(/\s+/g, ' ');
+
setFormData((prevState) => ({
...prevState,
customFields: prevState.customFields.map((field) =>
- field.fieldId === fieldId ? { ...field, value: [value] } : field
+ field.fieldId === fieldId
+ ? { ...field, value: [sanitizedValue] }
+ : field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleCheckboxChange = (
@@ -337,6 +386,9 @@ const TeacherProfile = () => {
: field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleDropdownChange = (fieldId: string, value: string) => {
@@ -346,6 +398,9 @@ const TeacherProfile = () => {
field.fieldId === fieldId ? { ...field, value: [value] } : field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleRadioChange = (fieldId: string, value: string) => {
@@ -355,12 +410,20 @@ const TeacherProfile = () => {
field.fieldId === fieldId ? { ...field, value: [value] } : field
),
}));
+ setHasInputChanged(true);
+ setIsValidationTriggered(true);
+ validateFields();
};
const handleSubmit = async (
e: React.MouseEvent
) => {
e.preventDefault();
+ logEvent({
+ action: 'save-button-clicked-edit-teacher-profile',
+ category: 'Profile Page',
+ label: 'Teacher Profile Save Button Clicked',
+ });
setLoading(true);
const userId = localStorage.getItem('userId');
const data = {
@@ -379,8 +442,9 @@ const TeacherProfile = () => {
try {
if (userId) {
const response = await editEditUser(userId, userDetails);
-
+ ReactGA.event('edit-teacher-profile-successful', { userId: userId });
if (response.responseCode !== 200 || response.params.err) {
+ ReactGA.event('edit-teacher-profile-error', { userId: userId });
throw new Error(
response.params.errmsg ||
'An error occurred while updating the user.'
@@ -396,6 +460,7 @@ const TeacherProfile = () => {
}
} catch (error) {
setIsError(true);
+ showToastMessage(t('COMMON.SOMETHING_WENT_WRONG'), 'error');
console.error('Error:', error);
}
@@ -414,350 +479,363 @@ const TeacherProfile = () => {
{loading && (
)}
-
+ {isData && isData ? (
-
- {t('PROFILE.MY_PROFILE')}
-
-
-
-
-
-
-
-
-
- {userData?.name}
-
-
-
-
- {address ? (
-
- ) : (
- ''
- )}
-
-
- {address}
-
-
-
-
-
-
-
+
-
-
- {filteredSortedForView?.map((item, index) => {
- if (item.order === 5) {
- return (
-
-
- {item?.label && item.name
- ? t(`FIELDS.${item.name.toUpperCase()}`, item.label)
- : item.label}
- {/* {item?.label} */}
-
-
- {orderedSubjects &&
- orderedSubjects?.map((subject, index) => (
-
- ))}
-
-
- );
- } else if (item.order === 7) {
- return (
-
-
- {item?.label && item.name
- ? t(`FIELDS.${item.name.toUpperCase()}`, item.label)
- : item.label}
- {/* {item.label} */}
-
-
- {item.value}
-
-
- );
- } else {
- return (
-
-
- {item?.label && item.name
- ? t(`FIELDS.${item.name.toUpperCase()}`, item.label)
- : item.label}
- {/* {item.label} */}
-
-
- {getLabelForValue(item, item.value[0])}
-
-
- );
- }
- })}
-
+
+
+
+
+
+
+
+ {userData?.name}
+
+
+
+
+ {address ? (
+
+ ) : (
+ ''
+ )}
+
+
+ {address}
+
+
+
+
-
- {/* modal for edit profile */}
-
- {loading && (
-
- )}
-
{t('PROFILE.EDIT_PROFILE')}
+
+
+
+
-
-
-
-
-
+
+ {filteredSortedForView?.map((item, index) => {
+ if (item.order === 5) {
+ return (
+
+
+ {item?.label && item.name
+ ? t(
+ `FIELDS.${item.name.toUpperCase()}`,
+ item.label
+ )
+ : item.label}
+ {/* {item?.label} */}
+
+
+ {orderedSubjects &&
+ orderedSubjects?.map((subject, index) => (
+
+ ))}
+
+
+ );
+ } else if (item.order === 7) {
+ return (
+
+
+ {item?.label && item.name
+ ? t(
+ `FIELDS.${item.name.toUpperCase()}`,
+ item.label
+ )
+ : item.label}
+ {/* {item.label} */}
+
+
+ {item.value}
+
+
+ );
+ } else {
+ return (
+
+
+ {item?.label && item.name
+ ? t(
+ `FIELDS.${item.name.toUpperCase()}`,
+ item.label
+ )
+ : item.label}
+ {/* {item.label} */}
+
+
+ {getLabelForValue(item, item.value[0])}
+
+
+ );
+ }
+ })}
+
+
+
+ {/* modal for edit profile */}
+
+
+ {loading && (
+
+ )}
- {/*
+ {t('PROFILE.EDIT_PROFILE')}
+
+
+
+
+
+
+
+
+
+ {/* {
style={{ alignItems: 'center' }}
/> */}
-
-
- {/* ------- comment for temp
+
+
+ {/* ------- comment for temp
*/}
+
-
-
+
- {customFieldsData
- ?.filter((field) => field.isEditable)
- ?.sort((a, b) => a.order - b.order)
- ?.map((field) => {
- const fieldValue =
- formData?.customFields?.find(
- (f) => f.fieldId === field.fieldId
- )?.value[0] || '';
- const isError = errors[field.fieldId];
+ {customFieldsData
+ ?.filter((field) => field.isEditable)
+ ?.sort((a, b) => a.order - b.order)
+ ?.map((field) => {
+ const fieldValue =
+ formData?.customFields?.find(
+ (f) => f.fieldId === field.fieldId
+ )?.value[0] || '';
+ const isError = errors[field.fieldId];
- return (
-
- {field.type === 'text' ? (
- {
- handleFieldChange(field.fieldId, e.target.value);
- validateFields();
- }}
- error={isError}
- helperText={isError && t('PROFILE.ENTER_CHARACTER')}
- />
- ) : field.type === 'numeric' ? (
- {
- const inputValue = e.target.value;
- if (/^\d{0,4}$/.test(inputValue)) {
- handleFieldChange(field.fieldId, inputValue);
- } else {
- handleFieldChange(field.fieldId, ''); // Clear the field value if it doesn't meet the validation criteria
+ return (
+
+ {field.type === 'text' ? (
+
- ) : field.type === 'checkbox' ? (
-
-
- {field?.label && field.name
- ? t(
- `FIELDS.${field.name.toUpperCase()}`,
- field.label
+ variant="outlined"
+ value={fieldValue}
+ onChange={(e) => {
+ handleFieldChange(
+ field.fieldId,
+ e.target.value
+ );
+ validateFields();
+ }}
+ error={isError}
+ helperText={
+ isError && t('PROFILE.ENTER_CHARACTER')
+ }
+ />
+ ) : field.type === 'numeric' ? (
+ {
+ // Allow only numeric keys, Backspace, and Delete
+ if (
+ !(
+ (
+ /[0-9]/.test(e.key) ||
+ e.key === 'Backspace' ||
+ e.key === 'Delete'
+ ) // Allow decimal point if needed
)
- : field.label}
-
- {field.options?.map((option: any) => (
-
- f.fieldId === field.fieldId
- )?.value || []
- )?.includes(option.value)}
- onChange={(e) =>
- handleCheckboxChange(
- field.fieldId,
- option.value,
- e.target.checked
- )
- }
- />
- }
- label={option.label}
- />
-
- ))}
-
- ) : field.type === 'drop_down' ||
- field.type === 'dropdown' ? (
-
-
-
+ ) {
+ e.preventDefault();
+ }
+ }}
+ onChange={(e) => {
+ const inputValue = e.target.value;
+ if (/^\d{0,4}$/.test(inputValue)) {
+ handleFieldChange(field.fieldId, inputValue);
+ validateFields();
+ }
+ }}
+ error={isError}
+ helperText={isError && t('PROFILE.ENTER_NUMBER')}
+ />
+ ) : field.type === 'checkbox' ? (
+
+
{field?.label && field.name
? t(
`FIELDS.${field.name.toUpperCase()}`,
field.label
)
: field.label}
-
-
+ ) : field.type === 'drop_down' ||
+ field.type === 'dropdown' ? (
+
+
+
+ {field?.label && field.name
? t(
`FIELDS.${field.name.toUpperCase()}`,
field.label
)
- : field.label
- }
+ : field.label}
+
+
+ {errors[field.fieldId] && (
+
+ {t('PROFILE.SELECT_OPTION')}
+
+ )}
+
+
+ ) : field.type === 'radio' ||
+ field.type === 'Radio' ? (
+
+
+ {field?.label && field.name
+ ? t(
+ `FIELDS.${field.name.toUpperCase()}`,
+ field.label
+ )
+ : field.label}
+
+
- handleDropdownChange(
+ handleRadioChange(
field.fieldId,
e.target.value
)
}
>
- {field?.options?.map((option: any) => (
-
- ))}
-
-
-
- ) : field.type === 'radio' || field.type === 'Radio' ? (
-
-
- {field?.label && field.name
- ? t(
- `FIELDS.${field.name.toUpperCase()}`,
- field.label
- )
- : field.label}
-
-
- handleRadioChange(field.fieldId, e.target.value)
- }
- >
-
- {field?.options?.map((option: any) => (
- }
- label={option.label}
- />
- ))}
-
-
-
- ) : null}
-
- );
- })}
-
-
-
-
-
+
+
+ ) : null}
+
+ );
+ })}
+
+
+
+
- {t('COMMON.SAVE')}
-
+
+
-
-
-
+
+
+ ) : (
+
+
+ {t('COMMON.SOMETHING_WENT_WRONG')}
+
+
+ )}{' '}
- {isError && }
>
);
};
diff --git a/src/services/AttendanceService.ts b/src/services/AttendanceService.ts
index 2a70f13f..34713df1 100644
--- a/src/services/AttendanceService.ts
+++ b/src/services/AttendanceService.ts
@@ -97,7 +97,7 @@ export const getLearnerAttendanceStatus = async ({
return postAttendanceList({
limit,
page,
- filters: { contextId, scope, toDate, fromDate, userId }
+ filters: { contextId, scope, toDate, fromDate, userId },
});
};
@@ -106,35 +106,35 @@ export const getCohortAttendance = async ({
page,
filters: { scope, fromDate, toDate, contextId },
facets,
- sort
+ sort,
}: cohortAttendancePercentParam): Promise => {
return postAttendanceList({
limit,
page,
filters: { scope, fromDate, toDate, contextId },
facets,
- sort
+ sort,
});
};
export const getAllCenterAttendance = async ({
limit,
page,
- filters: {scope, fromDate, toDate, contextId},
+ filters: { scope, fromDate, toDate, contextId },
facets,
}: allCenterAttendancePercentParam): Promise => {
return postAttendanceList({
limit,
page,
- filters: {scope, fromDate, toDate, contextId},
+ filters: { scope, fromDate, toDate, contextId },
facets,
- })
+ });
};
export const classesMissedAttendancePercentList = async ({
filters,
facets,
- sort
+ sort,
}: any): Promise => {
const apiUrl: string = `${process.env.NEXT_PUBLIC_BASE_URL}/attendance/list`;
try {
diff --git a/src/services/MyClassDetailsService.ts b/src/services/MyClassDetailsService.ts
index 85634ddf..407b31bd 100644
--- a/src/services/MyClassDetailsService.ts
+++ b/src/services/MyClassDetailsService.ts
@@ -7,7 +7,7 @@ export const getMyCohortMemberList = async ({
filters,
}: cohortMemberList): Promise => {
const apiUrl: string = `${process.env.NEXT_PUBLIC_BASE_URL}/cohortmember/list`;
- filters = {...filters, role: 'Student'};
+ filters = { ...filters, role: 'Student' };
try {
const response = await post(apiUrl, {
limit,
diff --git a/src/services/ProfileService.ts b/src/services/ProfileService.ts
index 333e8262..3fca89f4 100644
--- a/src/services/ProfileService.ts
+++ b/src/services/ProfileService.ts
@@ -35,6 +35,6 @@ export const getUserDetails = async (
return response?.data;
} catch (error) {
console.error('error in fetching user details', error);
- throw error;
+ return error;
}
};
diff --git a/src/styles/Home.module.css b/src/styles/Home.module.css
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/styles/customStyles.tsx b/src/styles/customTheme.tsx
similarity index 100%
rename from src/styles/customStyles.tsx
rename to src/styles/customTheme.tsx
diff --git a/src/styles/darkStyle.tsx b/src/styles/darkStyle.tsx
deleted file mode 100644
index 12b58124..00000000
--- a/src/styles/darkStyle.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-import {
- PaletteColor,
- PaletteColorOptions,
- createTheme,
-} from '@mui/material/styles';
-
-const darkTheme = createTheme({
- palette: {
- primary: {
- main: '#FDBE16',
- light: '#FFDEA1',
- },
- secondary: {
- main: '#0D599E',
- light: '#E7F3F8',
- },
- success: {
- main: '#1A8825',
- light: '#C0FFC7',
- },
- info: {
- main: '#064471',
- light: '#D6EEFF',
- },
- warning: {
- '100': '#17130B',
- '200': '#261900',
- '300': '#1F1B13',
- '400': '#7C766F',
- '500': '#969088',
- '600': '#B1AAA2',
- '700': '#DED8E1',
- '800': '#F8EFE7',
- '900': '#DADADA',
- A100: '#D0C5B4',
- A200: 'red',
- A400: '#FFFFFF',
- A700: '#EDEDED',
- },
- error: {
- main: '#BA1A1A',
- light: '#FFDAD6',
- },
- },
- components: {
- MuiButton: {
- styleOverrides: {
- root: {
- borderRadius: '100px',
- border: '1px solid #1E1B16',
- color: '#1E1B16',
- },
- containedPrimary: {
- backgroundColor: '#FDBE16',
- border: 'none',
- // '&:hover': {
- // backgroundColor: '#FDBE161F'
- // }
- },
- outlinedPrimary: {
- backgroundColor: 'none',
- border: '1px solid #1E1B16',
- // '&:hover': {
- // backgroundColor: '#0D599E'
- // }
- },
- textPrimary: {
- backgroundColor: 'none',
- border: 'none',
- color: '#0D599E',
- // '&:hover': {
- // backgroundColor: '6750A41F',
- // border: 'none'
- // }
- },
- containedSecondary: {
- backgroundColor: '#fbbc13',
- // '&:hover': {
- // backgroundColor: '#b20041'
- // }
- },
- },
- },
- MuiTextField: {
- styleOverrides: {
- root: {
- width: '100%',
- },
- },
- },
- MuiDialog: {
- styleOverrides: {
- paper: {
- borderRadius: '16px', // Override the border radius
- width: '90vw',
- maxWidth: '340px',
- },
- },
- },
- },
- typography: {
- // fontFamily: 'Poppins, sans-serif',
- h1: {
- fontSize: '22px',
- fontWeight: 400,
- lineHeight: '28px',
- marginBottom: '1rem',
- },
- h2: {
- fontSize: '16px',
- fontWeight: 500,
- lineHeight: '24px',
- marginBottom: '0.75rem',
- },
- h3: {
- fontSize: '14px',
- fontWeight: 500,
- lineHeight: '20px',
- marginBottom: '0.5rem',
- },
- h4: {
- //h4 is a large label style
- fontSize: '14px',
- fontWeight: 400,
- lineHeight: '20px',
- letterSpacing: '0.1px',
- },
- h5: {
- //h5 is a medium label style
- fontSize: '12px',
- fontWeight: 500,
- lineHeight: '16px',
- letterSpacing: '0.5px',
- },
- h6: {
- //h6 is a small label style
- fontSize: '11px',
- fontWeight: 500,
- lineHeight: '16px',
- letterSpacing: '0.5px',
- },
- body1: {
- fontSize: '16px',
- fontWeight: 400,
- lineHeight: '24px',
- letterSpacing: '0.5px',
- marginBottom: '1rem',
- },
- body2: {
- fontSize: '14px',
- fontWeight: 400,
- lineHeight: '20px',
- letterSpacing: '0.25px',
- marginBottom: '1rem',
- },
- button: {
- textTransform: 'none',
- fontSize: '14px',
- fontWeight: 600,
- },
- },
-});
-
-export default darkTheme;
diff --git a/src/styles/globals.css b/src/styles/globals.css
index 64c7d8c7..8ac24636 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -1,35 +1,41 @@
.alert .MuiSnackbarContent-root {
- background-color: var(--mui-palette-error-light) !important;
+ margin-top: 35%;
color: var(--mui-palette-error-main);
font-size: 0.75rem;
- margin-top: 35%;
+ background-color: var(--mui-palette-error-light) !important;
}
+
.remember-me-checkbox span svg {
fill: black !important;
}
+
.remember-me-checkbox span {
padding: 6px 7px 6px 0 !important;
}
+
body {
+ box-sizing: border-box;
margin: 0 !important;
padding: 0 !important;
- box-sizing: border-box;
background-color: white;
}
.drawer-select div div {
- height: 32px !important;
- padding: 0 10px;
display: flex;
align-items: center;
+ height: 32px !important;
+ padding: 0 10px;
+ font-weight: 500;
+ font-size: 14px;
+
/* border: 1px solid #1f1b13 !important; */
border-radius: 8px;
- font-size: 14px;
- font-weight: 500;
}
+
.word-break {
word-break: break-all;
}
+
.fs-12 {
font-size: 12px !important;
}
@@ -37,12 +43,15 @@ body {
.fs-14 {
font-size: 14px !important;
}
+
.fs-22 {
font-size: 22px !important;
}
+
.text-4D {
color: #4d4639;
}
+
.text-1F {
color: #1f1b13;
}
@@ -54,40 +63,43 @@ body {
.pt-10 {
padding-top: 8px;
}
+
.fw-500 {
font-weight: 500;
}
+
.fw-600 {
font-weight: 600;
+
.App {
- font-family: sans-serif;
- text-align: center;
+ width: 100%;
min-height: 700px;
- background-color: paleturquoise;
margin: 0;
padding: 20px;
- width: 100%;
+ font-family: sans-serif;
+ text-align: center;
+ background-color: paleturquoise;
}
}
+
/* FONT IMPORTS */
-@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,700);
-@import url(https://fonts.googleapis.com/icon?family=Material+Icons);
+@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,400,700');
+@import url('https://fonts.googleapis.com/icon?family=Material+Icons');
.icon {
+ display: inline-block;
font-family: 'Material Icons', serif;
font-style: normal;
- display: inline-block;
- vertical-align: middle;
line-height: 1;
- text-transform: none;
+ direction: ltr;
letter-spacing: normal;
- word-wrap: normal;
white-space: nowrap;
- direction: ltr;
-
+ text-transform: none;
+ vertical-align: middle;
+ word-wrap: normal;
-webkit-font-smoothing: antialiased;
- text-rendering: optimizeLegibility;
+ text-rendering: optimizelegibility;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'liga';
}
@@ -103,6 +115,15 @@ body {
--pink-color: #ff1a79;
--custom-gradient: linear-gradient(180deg, #fffdf7 0%, #f8efda 100%);
--custom-gradient-pink: linear-gradient(45deg, #ff1a79 0%, #eb82b3 40%);
+ --toastify-color-success: #fff !important;
+ --toastify-icon-color-info: #fff !important;
+ --toastify-icon-color-success: #fff !important;
+ --toastify-icon-color-warning: #fff !important;
+ --toastify-icon-color-error: #fff !important;
+ --toastify-text-color-info: #fff !important;
+ --toastify-text-color-success: #fff !important;
+ --toastify-text-color-warning: #fff !important;
+ --toastify-text-color-error: #fff !important;
}
/* GENERAL */
@@ -113,27 +134,27 @@ body {
}
body {
- font-size: 1em;
+ position: relative;
+ color: var(--text-color);
font-weight: 300;
+ font-size: 1em;
line-height: 1.5;
- color: var(--text-color);
background: #fff;
- position: relative;
}
header {
display: block;
width: 100%;
padding: 1.75em 0;
- border-bottom: 1px solid var(--border-color);
background: var(--mui-palette-success-contrastText);
+ border-bottom: 1px solid var(--border-color);
}
header #logo {
- font-size: 175%;
- text-align: center;
color: var(--main-color);
+ font-size: 175%;
line-height: 1;
+ text-align: center;
}
header #logo .icon {
@@ -142,21 +163,22 @@ header #logo .icon {
main {
display: block;
+ max-width: 50em;
margin: 0 auto;
margin-top: 5em;
- max-width: 50em;
}
/* GRID */
.row {
- margin: 0;
- padding: 0;
display: flex;
+ gap: 0.5rem;
+ justify-content: space-between;
+
/* flex-wrap: wrap; */
width: 100%;
- justify-content: space-between;
- gap: 0.5rem;
+ margin: 0;
+ padding: 0;
}
.row-middle {
@@ -164,8 +186,8 @@ main {
}
.col {
- flex-grow: 1;
flex-basis: 0;
+ flex-grow: 1;
max-width: 100%;
}
@@ -187,26 +209,28 @@ main {
/* Calendar */
.calendar {
- display: block;
position: relative;
+ display: block;
width: 100%;
overflow-x: auto;
}
+
.calender_body_width {
width: 1840px;
height: 110px;
}
+
.col-center {
- font-size: 11px;
- font-weight: 500;
color: #7c766f;
+ font-weight: 500;
+ font-size: 11px;
}
.calendar .header {
- text-transform: uppercase;
+ padding: 1.5em 0;
font-weight: 700;
font-size: 115%;
- padding: 1.5em 0;
+ text-transform: uppercase;
}
.calendar .header .icon {
@@ -215,8 +239,8 @@ main {
}
.calendar .header .icon:hover {
- transition: 0.25s ease-out;
color: var(--main-color);
+ transition: 0.25s ease-out;
}
.calendar .header .icon:first-of-type {
@@ -228,22 +252,22 @@ main {
}
.calendar .days {
- text-transform: uppercase;
+ padding: 0.75em 0;
font-weight: 600;
font-size: 70%;
- padding: 0.75em 0;
+ text-transform: uppercase;
}
.calendar .body .cell {
position: relative;
+ height: 4rem;
padding: 10px;
- border: 1px solid var(--mui-palette-warning-A100);
overflow: hidden;
- cursor: pointer;
- transition: 0.25s ease-out;
font-size: 1.5em;
+ border: 1px solid var(--mui-palette-warning-A100);
border-radius: 4px;
- height: 4rem;
+ cursor: pointer;
+ transition: 0.25s ease-out;
}
.calendar .body .cell:hover {
@@ -258,6 +282,7 @@ main {
.selected {
background-color: var(--mui-palette-primary-main);
}
+
.selected:hover {
background-color: var(--mui-palette-primary-main) !important;
}
@@ -279,13 +304,13 @@ main {
}
.calendar .body .cell .number {
- text-align: center;
- width: 100%;
display: block;
+ width: 100%;
+ color: var(--mui-palette-warning-300);
+ font-weight: 400;
font-size: 14px;
line-height: 1;
- font-weight: 400;
- color: var(--mui-palette-warning-300);
+ text-align: center;
}
.calendar .body .disabled {
@@ -294,16 +319,16 @@ main {
}
.calendar .body .cell .bg {
- font-weight: 700;
- line-height: 1;
- color: var(--main-color);
- opacity: 0;
- font-size: 8em;
position: absolute;
top: -0.2em;
right: -0.05em;
- transition: 0.25s ease-out;
+ color: var(--main-color);
+ font-weight: 700;
+ font-size: 8em;
+ line-height: 1;
letter-spacing: -0.07em;
+ opacity: 0;
+ transition: 0.25s ease-out;
}
.calendar .body .cell:hover .bg,
@@ -311,6 +336,7 @@ main {
opacity: 0.05;
transition: 0.5s ease-in;
}
+
.calendar .body .cell.today .bg {
color: var(--pink-colo);
opacity: 0.05;
@@ -321,42 +347,49 @@ main {
}
.circularProgress {
- width: 100%;
align-items: center;
justify-content: center;
+ width: 100%;
}
+
.calenderTitle p {
font-size: 14px !important;
}
+
.calenderTitle svg {
font-size: 14px !important;
}
+
.flex-center {
display: flex;
- justify-content: center;
align-items: center;
+ justify-content: center;
}
+
.text-decoration {
text-decoration: none;
}
+
.card_overview div {
height: unset;
font-size: 11px;
line-height: 16px;
}
+
.card_overview div div h2 {
- font-size: 16px;
- line-height: 24px;
margin-top: 4px;
+ font-size: 16px;
line-height: 24px;
}
.linerGradient {
background-image: var(--custom-gradient);
}
+
.icon_holder svg {
color: #1e1b16 !important;
}
+
.w-100 {
width: 100% !important;
}
@@ -364,7 +397,7 @@ main {
/* This is for react-calender month view and classes are from library */
.react-calendar {
width: 100% !important;
- font-family: 'Poppins' !important;
+ font-family: Poppins !important;
}
.react-calendar_month-viewdays_day {
@@ -386,50 +419,54 @@ main {
.react-calendar__month-view__days__day--neighboringMonth {
visibility: hidden;
}
+
.react-calendar button abbr {
color: #1f1b13;
- font-family: 'Poppins' !important;
+ font-family: Poppins !important;
}
+
.react-calendar__month-view__weekdays__weekday abbr {
color: #1f1b13;
- font-family: 'Poppins' !important;
+ font-family: Poppins !important;
}
+
.present-marker {
- color: var(--mui-palette-success-main);
width: fit-content !important;
+ color: var(--mui-palette-success-main);
}
.absent-marker {
- color: var(--mui-palette-error-main) !important;
width: fit-content !important;
+ color: var(--mui-palette-error-main) !important;
}
.on-leave-marker {
- color: var(--mui-palette-error-main) !important;
width: fit-content !important;
margin: auto !important;
+ color: var(--mui-palette-error-main) !important;
}
.tile-day {
- border: 1px solid #d0c5b4 !important;
- height: 70px !important;
width: 100% !important;
- border-radius: 5px;
+ height: 70px !important;
margin-bottom: 5px !important;
+ border: 1px solid #d0c5b4 !important;
+ border-radius: 5px;
+
/* margin-left: 2px !important; */
}
.custom-date-range-pop-up {
- border: 1px solid #d0c5b4 !important;
- height: 40px !important;
width: 100% !important;
- border-radius: 5px;
+ height: 40px !important;
margin-bottom: 5px !important;
+ border: 1px solid #d0c5b4 !important;
+ border-radius: 5px;
}
.react-calendar__tile--active {
- background-color: #fbbc13 !important;
color: black !important;
+ background-color: #fbbc13 !important;
}
.react-calendar__tile {
@@ -441,15 +478,15 @@ main {
}
.attendance-not-marked abbr {
- border: 1px dashed var(--mui-palette-error-main);
- color: var(--mui-palette-error-main) !important;
- border-radius: 50%;
- padding: 5px;
- height: 25px;
display: flex;
- width: 25px;
- justify-content: center;
align-items: center;
+ justify-content: center;
+ width: 25px;
+ height: 25px;
+ padding: 5px;
+ color: var(--mui-palette-error-main) !important;
+ border: 1px dashed var(--mui-palette-error-main);
+ border-radius: 50%;
}
.sample .MuiSnackbarContent-root {
@@ -457,9 +494,9 @@ main {
}
.alert .MuiSnackbarContent-root {
- background-color: var(--mui-palette-error-light) !important;
color: var(--mui-palette-error-main);
font-size: 0.75rem;
+ background-color: var(--mui-palette-error-light) !important;
}
.custom-calendar-container {
@@ -484,8 +521,10 @@ main {
.react-calendar button {
display: flex;
flex-direction: column;
+
/* justify-content: center; */
align-items: center;
+
/* border: 1px solid #d0c5b4 !important; */
}
@@ -494,42 +533,45 @@ main {
background: linear-gradient(180deg, #fffdf7 0%, #f8efda 100%) !important;
border: unset !important;
}
+
.react-calendar,
.react-calendar *,
-.react-calendar *:before,
-.react-calendar *:after {
- text-decoration: none;
+.react-calendar *::before,
+.react-calendar *::after {
color: var(--mui-palette-warning-300);
- font-size: 16px;
font-weight: 400;
+ font-size: 16px;
+ text-decoration: none;
}
+
.react-calendar__navigation button {
border: unset !important;
}
.circularProgressBar {
- margin-top: 8px;
width: 25px;
+ margin-top: 8px;
}
.up_down_btn {
position: fixed;
- bottom: 40px;
right: 22px;
+ bottom: 40px;
z-index: 999;
- box-shadow: 0px 1px 3px 0px #0000004d;
display: flex !important;
flex-direction: column;
align-items: center;
justify-content: center;
- /* padding: 20px 15px; */
- border-radius: 100px;
- box-shadow: 0px 4px 8px 3px #00000026;
- background: #4a4640;
color: #fff;
text-align: center;
+ background: #4a4640;
+
+ /* padding: 20px 15px; */
+ border-radius: 100px;
+ box-shadow: 0 4px 8px 3px #00000026;
cursor: pointer;
}
+
.w-98 {
width: 98%;
}
@@ -539,20 +581,24 @@ main {
}
.up_down_btn-disabled {
- pointer-events: none;
cursor: default;
opacity: 0;
+ pointer-events: none;
}
+
.modal_mark .css-rsi8of {
overflow: hidden;
}
+
.modal_label {
- margin-left: 0px !important;
+ margin-left: 0 !important;
}
+
.modal_label span {
- font-size: 14px !important;
color: #1e1b16;
+ font-size: 14px !important;
}
+
.flex-column-center {
display: flex;
flex-direction: column;
@@ -564,6 +610,7 @@ main {
.w-78 {
width: 78%;
}
+
/* @media (min-width: 1350px) {
.up_down_btn {
right: 400px;
@@ -577,10 +624,11 @@ main {
.prev_btn {
position: absolute;
top: 53px;
- z-index: 999;
left: -14px;
+ z-index: 999;
transform: rotate(90deg);
}
+
.next_btn {
position: absolute;
top: 53px;
@@ -588,28 +636,31 @@ main {
z-index: 999;
transform: rotate(270deg);
}
+
.menuSvg path {
width: 36px;
height: 24px;
}
+
.accIcon path {
width: 20;
height: 20px;
}
.present-marker svg path {
- color: var(--mui-palette-success-main) !important;
width: fit-content !important;
+ color: var(--mui-palette-success-main) !important;
}
.absent-marker svg path {
- color: var(--mui-palette-error-main) !important;
width: fit-content !important;
+ color: var(--mui-palette-error-main) !important;
}
+
.react-calendar__month-view__weekdays__weekday {
- font-size: 16px;
- font-weight: 400;
color: #1f1b13;
+ font-weight: 400;
+ font-size: 16px;
}
.two-line-text {
@@ -619,31 +670,39 @@ main {
overflow: hidden;
text-overflow: ellipsis;
}
+
.backgroundFaded div:first-child {
background-color: unset !important;
}
+
.bg-white {
background-color: #fff;
}
+
.customRange .Mui-selected {
background: unset !important;
}
.customRange li:last-child {
- margin-bottom: 0px !important;
+ margin-bottom: 0 !important;
}
+
.w-100 {
width: 100%;
}
+
.p-20 {
padding: 10px 20px 20px;
}
+
.text-4d {
color: #4d4639;
}
+
.userName label {
color: #1f1b13 !important;
}
+
.password label {
color: #1f1b13 !important;
}
@@ -651,22 +710,25 @@ main {
.fs-24 {
font-size: 24px !important;
}
+
.htw-24 {
- height: 24px;
width: 24px;
+ height: 24px;
}
+
.selected-range {
background-color: var(--mui-palette-Skeleton-bg) !important;
}
.flex-center {
display: flex !important;
- justify-content: center !important;
align-items: center !important;
+ justify-content: center !important;
}
.text4D {
color: #4d4639 !important;
}
+
.p-10 {
padding: 10px;
}
@@ -698,4 +760,7 @@ main {
}
.textCD {
color: #cdc5bd !important;
+
+.selected-start-end {
+ background-color: var(--mui-palette-primary-main) !important;
}
diff --git a/src/utils/Helper.ts b/src/utils/Helper.ts
index a01c0ad0..7d6a9940 100644
--- a/src/utils/Helper.ts
+++ b/src/utils/Helper.ts
@@ -1,3 +1,5 @@
+import FingerprintJS from 'fingerprintjs2';
+
export const ATTENDANCE_ENUM = {
PRESENT: 'present',
ABSENT: 'absent',
@@ -78,21 +80,24 @@ export const getMonthName = () => {
export const getDayAndMonthName = (dateString: Date | string) => {
const date = new Date(dateString);
- const day = date.getDate(); ;
+ const day = date.getDate();
const month = date.toLocaleString('default', { month: 'long' });
return `${day} ${month}`;
};
-export const getDayMonthYearFormat = (dateString: string) => {
+export const getDayMonthYearFormat = (dateString: string) => {
const [year, monthIndex, day] = dateString.split('-');
- const date = new Date(parseInt(year, 10), parseInt(monthIndex, 10) - 1, parseInt(day, 10));
+ const date = new Date(
+ parseInt(year, 10),
+ parseInt(monthIndex, 10) - 1,
+ parseInt(day, 10)
+ );
return date.toLocaleDateString('en-US', {
day: '2-digit',
month: 'long',
year: 'numeric',
});
};
-
// Function to truncate URL if it's too long
export const truncateURL = (
@@ -149,13 +154,44 @@ export const getLabelForValue = (value: string): string => {
};
export const generateRandomString = (length: number): string => {
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ const characters =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
-}
+};
+
+export const generateUUID = () => {
+ var d = new Date().getTime();
+ var d2 =
+ (typeof performance !== 'undefined' &&
+ performance.now &&
+ performance.now() * 1000) ||
+ 0;
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+ var r = Math.random() * 16;
+ if (d > 0) {
+ r = (d + r) % 16 | 0;
+ d = Math.floor(d / 16);
+ } else {
+ r = (d2 + r) % 16 | 0;
+ d2 = Math.floor(d2 / 16);
+ }
+ return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
+ });
+};
+
+export const getDeviceId = () => {
+ return new Promise((resolve) => {
+ FingerprintJS.get((components: any[]) => {
+ const values = components.map((component) => component.value);
+ const deviceId = FingerprintJS.x64hash128(values.join(''), 31);
+ resolve(deviceId);
+ });
+ });
+};
diff --git a/src/utils/app.constant.ts b/src/utils/app.constant.ts
index 5019d103..478d8da5 100644
--- a/src/utils/app.constant.ts
+++ b/src/utils/app.constant.ts
@@ -1 +1 @@
-export const limit:number = 300;
\ No newline at end of file
+export const limit: number = 300;
diff --git a/src/utils/attendanceStats.ts b/src/utils/attendanceStats.ts
index d56fbb77..3b541442 100644
--- a/src/utils/attendanceStats.ts
+++ b/src/utils/attendanceStats.ts
@@ -20,6 +20,10 @@ const getPresentStudentCount = async (
const response = await attendanceInPercentageStatusList(attendanceRequest);
const attendanceDates = response?.data?.result?.attendanceDate;
const presentStudents: any = {};
+
+ if (!attendanceDates) {
+ return presentStudents;
+ }
for (const date of Object.keys(attendanceDates)) {
const attendance = attendanceDates[date];
const present = attendance.present || 0;
diff --git a/src/utils/googleAnalytics.ts b/src/utils/googleAnalytics.ts
new file mode 100644
index 00000000..b6a6b960
--- /dev/null
+++ b/src/utils/googleAnalytics.ts
@@ -0,0 +1,28 @@
+import ReactGA from 'react-ga4';
+
+export const initGA = (measurementId: string) => {
+ ReactGA.initialize(measurementId);
+};
+
+export const logPageView = (url: string) => {
+ ReactGA.send({ hitType: 'pageview', page: url });
+};
+
+export const logEvent = ({
+ action,
+ category,
+ label,
+ value,
+}: {
+ action: string;
+ category: string;
+ label?: string;
+ value?: number;
+}) => {
+ ReactGA.event({
+ action,
+ category,
+ label,
+ value,
+ });
+};
diff --git a/src/utils/telemetry.js b/src/utils/telemetry.js
new file mode 100644
index 00000000..6cfb597f
--- /dev/null
+++ b/src/utils/telemetry.js
@@ -0,0 +1,215 @@
+import { generateUUID, getDeviceId } from './Helper';
+import FingerprintJS from 'fingerprintjs2';
+
+let hostURL = process.env.NEXT_PUBLIC_TELEMETRY_URL;
+let CsTelemetryModule;
+let EkTelemetry;
+let jQuery;
+
+if (typeof window !== 'undefined') {
+ CsTelemetryModule =
+ require('@project-sunbird/client-services/telemetry').CsTelemetryModule;
+ EkTelemetry = require('@project-sunbird/telemetry-sdk');
+ jQuery = require('jquery');
+ window.jQuery = jQuery;
+}
+
+const telemetryConfig = {
+ apislug: '',
+ pdata: {
+ id: 'pratham-teacher-app',
+ pid: '0.0.1',
+ ver: 'pratham-teacher-app',
+ },
+ env: 'pratham-teacher-app',
+ channel: '',
+ did: 'did',
+ authtoken: '',
+ studentid:
+ (typeof window !== 'undefined' && localStorage.getItem('userId')) ||
+ 'Anonymous',
+ uid:
+ (typeof window !== 'undefined' && localStorage.getItem('id')) ||
+ 'Anonymous',
+ sid: generateUUID(),
+ batchsize: 1,
+ mode: '',
+ host: hostURL, //TODO: Change this host and endpoint properly
+ endpoint: '/telemetry/v1/telemetry',
+ tags: [],
+};
+
+if (typeof window !== 'undefined') {
+ getDeviceId().then((deviceId) => {
+ telemetryConfig.did = deviceId;
+ });
+}
+
+export const telemetryFactory = {
+ init: () => {
+ if (typeof window !== 'undefined') {
+ console.log('EkTelemetry', EkTelemetry);
+ if (!CsTelemetryModule.instance.isInitialised) {
+ // CsTelemetryModule.instance.init({});
+ // CsTelemetryModule.instance.telemetryService.initTelemetry({
+ // config: telemetryConfig,
+ // userOrgDetails: {}
+ // });
+ }
+ }
+ },
+
+ interact: (interactEventInput) => {
+ if (typeof window !== 'undefined') {
+ const eventData = getEventData(interactEventInput);
+ if (CsTelemetryModule.instance.isInitialised) {
+ CsTelemetryModule.instance.telemetryService.raiseInteractTelemetry({
+ options: eventData.options,
+ edata: eventData.edata,
+ });
+ }
+ }
+ },
+
+ impression: (impressionEventInput) => {
+ if (typeof window !== 'undefined') {
+ const eventData = getEventData(impressionEventInput);
+ if (CsTelemetryModule.instance.isInitialised) {
+ CsTelemetryModule.instance.telemetryService.raiseImpressionTelemetry({
+ options: eventData.options,
+ edata: eventData.edata,
+ });
+ }
+ }
+ },
+
+ assess: (assessEventInput) => {
+ if (typeof window !== 'undefined') {
+ const eventData = getEventData(assessEventInput);
+ if (CsTelemetryModule.instance.isInitialised) {
+ CsTelemetryModule.instance.telemetryService.raiseAssesTelemetry({
+ options: eventData.options,
+ edata: eventData.edata,
+ });
+ }
+ }
+ },
+
+ response: (responseEventInput) => {
+ if (typeof window !== 'undefined') {
+ const eventData = getEventData(responseEventInput);
+ if (CsTelemetryModule.instance.isInitialised) {
+ CsTelemetryModule.instance.telemetryService.raiseResponseTelemetry({
+ options: eventData.options,
+ edata: eventData.edata,
+ });
+ }
+ }
+ },
+
+ interrupt: (interactEventInput) => {
+ if (typeof window !== 'undefined') {
+ const eventData = getEventData(interactEventInput);
+ if (CsTelemetryModule.instance.isInitialised) {
+ CsTelemetryModule.instance.telemetryService.raiseInterruptTelemetry({
+ options: eventData.options,
+ edata: eventData.edata,
+ });
+ }
+ }
+ },
+
+ start: ({ appName, ...edata }) => {
+ if (typeof window !== 'undefined') {
+ return {
+ type: edata?.type,
+ eid: generateUUID(),
+ $set: { id: localStorage.getItem('id') || 'Anonymous' },
+ actor: {
+ id: localStorage.getItem('id') || 'Anonymous',
+ type: 'Teacher',
+ },
+ context: {
+ type: appName ? appName : 'Standalone',
+ },
+ edata,
+ };
+ }
+ },
+
+ end: ({ appName, ...edata }) => {
+ if (typeof window !== 'undefined') {
+ return {
+ type: edata?.type,
+ eid: generateUUID(),
+ $set: { id: localStorage.getItem('id') || 'Anonymous' },
+ actor: {
+ id: localStorage.getItem('id') || 'Anonymous',
+ type: 'Teacher',
+ },
+ context: {
+ type: appName ? appName : 'Standalone',
+ },
+ edata,
+ };
+ }
+ },
+};
+
+function getEventData(eventInput) {
+ const timestamp = Date.now();
+ const event = {
+ edata: eventInput.edata,
+ options: {
+ context: getEventContext(eventInput),
+ object: getEventObject(eventInput),
+ tags: [],
+ },
+ ets: timestamp,
+ };
+ return event;
+}
+
+function getEventObject(eventInput) {
+ if (eventInput.object) {
+ const eventObjectData = {
+ id: eventInput.object.id || '',
+ type: eventInput.object.type || '',
+ ver: eventInput.object.ver || '',
+ rollup: eventInput.object.rollup || {},
+ };
+ return eventObjectData;
+ } else {
+ return {};
+ }
+}
+
+function getEventContext(eventInput) {
+ const eventContextData = {
+ channel: eventInput.edata.channel || telemetryConfig.channel,
+ pdata: eventInput.context.pdata || telemetryConfig.pdata,
+ env: eventInput.context.env || telemetryConfig.env,
+ sid: eventInput.sid || telemetryConfig.sid,
+ uid:
+ (typeof window !== 'undefined' && localStorage.getItem('id')) ||
+ telemetryConfig.uid, //user id
+ cdata: eventInput.context.cdata || [],
+ };
+ if (telemetryConfig.sid) {
+ eventContextData.cdata.push({
+ id: telemetryConfig.sid,
+ type: 'UserSession',
+ });
+ }
+ eventContextData.cdata.push({
+ id: 'uuid',
+ type: 'Device',
+ });
+ return eventContextData;
+}
+
+function getRollUpData(data = []) {
+ const rollUp = {};
+ data.forEach((element, index) => (rollUp['l' + (index + 1)] = element));
+ return rollUp;
+}
diff --git a/src/utils/tourGuideSteps.ts b/src/utils/tourGuideSteps.ts
new file mode 100644
index 00000000..84183ed8
--- /dev/null
+++ b/src/utils/tourGuideSteps.ts
@@ -0,0 +1,31 @@
+import { TFunction } from 'next-i18next';
+export const getSteps = (t: TFunction) => [
+ {
+ target: 'joyride-step-0',
+ content: t('GUIDE_TOUR.STEP_0'),
+ },
+ {
+ target: '.joyride-step-1',
+ content: t('GUIDE_TOUR.STEP_1'),
+ },
+ {
+ target: '.joyride-step-2',
+ content: t('GUIDE_TOUR.STEP_2'),
+ },
+ {
+ target: '.joyride-step-3',
+ content: t('GUIDE_TOUR.STEP_3'),
+ },
+ {
+ target: '.joyride-step-4',
+ content: t('GUIDE_TOUR.STEP_4'),
+ },
+ {
+ target: '.joyride-step-5',
+ content: t('GUIDE_TOUR.STEP_5'),
+ },
+ {
+ target: '.joyride-step-6',
+ content: t('GUIDE_TOUR.STEP_6'),
+ },
+];
diff --git a/tsconfig.json b/tsconfig.json
index fb68dc1a..06a63a7c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,5 +1,6 @@
{
"compilerOptions": {
+ "target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
@@ -16,6 +17,6 @@
"@/*": ["./src/*"]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types/global.d.ts"],
"exclude": ["node_modules"]
}
diff --git a/types/global.d.ts b/types/global.d.ts
new file mode 100644
index 00000000..a839b854
--- /dev/null
+++ b/types/global.d.ts
@@ -0,0 +1,3 @@
+interface Window {
+ GA_INITIALIZED: boolean;
+}